我正在尝试使用 syscall 56 在 /dev/input/js0 打开我的 Xbox 游戏手柄。我已验证设备在给定路径上的存在。包括通过使用 open() 用 C 编写的测试程序。然而,程序继续分支FailExit。
.global main
.section .text
.align 2
main:
ldr x0, =GamepadPath
mov x1, #0
mov x8, #56
svc #0
cmp x0, #0
blt FailExit
mov x0, #1
ldr x1, =SuccessMsg
mov x2, #25
mov x8, #64
svc #0
mov x0, #0
mov x8, #93
svc #0
FailExit:
mov x0, #1
ldr x1, =ErrorMsg
mov x2, #25
mov x8, #64
svc #0
mov x0, #0
mov x8, #93
svc #0
.section .data
.section .rodata
GamepadPath: .asciz "/dev/input/js0"
SuccessMsg: .asciz "Gamepad Connected\n\n"
ErrorMsg: .asciz "Gamepad Not Available\n\n"
.section .bss
我尝试理解一些参考资料,包括:
让我感到困惑的是,Syscall 56,OpenAt在x0处的第一个参数应该是由系统调用Open设置的返回值,我在ARMv8 Linux的任何参考中都找不到它。
linux.die.net 往往有过时版本的手册页。 man7.org 更好。 您可以在这里阅读:
dirfd 参数与路径名结合使用 论证如下:
• 如果 pathname 中给出的路径名是绝对路径名,则 dirfd 是 被忽略了。
这就是你的情况;
/dev/input/js0
是绝对路径(以 /
开头)。
因此,只需将您喜欢的任何内容(例如 0 或 -1)作为
dirfd
第一个参数传递给 openat
系统调用即可。 这意味着路径名成为第二个参数,标志成为第三个参数。
尝试
mov x0, #-1
ldr x1, =GamepadPath
mov x2, #0
mov x8, #56
svc #0
openat
添加的值适用于像foo/bar/baz
这样的相对路径。 对于正常的
open
,这总是相对于当前工作目录得到解决;因此,如果您当前的工作目录是 /blah
,那么 open("foo/bar/baz")
将始终打开 /blah/foo/bar/baz
。 您可以通过传递特殊值 openat
(在 AT_FDCWD
中定义为 <fcntl.h>
)作为第一个参数,从 -100
获得相同的行为。 因此,在您的代码中,如果您想在使用相对路径时复制 open
,只需执行 mov x0, #-100
并使用其余参数填充 x1
、x2
、x3
。
但是你也可以先打开一个目录并传递它的 fd。 因此,如果您当前的工作目录是
/blah
但您确实想相对于 /gurgle
打开,您可以这样做
dirfd = openat(-1, "/gurgle", O_RDONLY | O_DIRECTORY);
fd = openat(dirfd, "foo/bar/baz", O_RDONLY);
这将打开
/gurgle/foo/bar/baz
。
使用文件描述符可以帮助避免竞争条件。 假设您要创建一个文件
/foo/new
,但前提是 /foo/old
已存在于该目录中。 如果你做了显而易见的事情
fdold = open("/foo/old", O_RDONLY);
if (fdold >= 0) {
fdnew = open("/foo/new", O_CREAT | O_WRONLY, 0644)
}
那么问题是,在两次调用
open
之间,有人可以重命名并重新创建目录 /foo
,或者将其重新创建为指向其他位置的符号链接。 然后,您最终可能会在 不包含
new
的目录中创建 old
。
openat
让您避免这种情况:
dirfd = openat(-1, "/foo", O_RDONLY | O_DIRECTORY);
fdold = openat(dirfd, "old", O_RDONLY);
if (fdold) {
openat(dirfd, "new", O_CREAT | O_WRONLY, 0644)
}
现在这两个文件肯定会存在于同一个目录中,无论该目录的名称是否同时从
foo
更改为其他名称。