调用系统调用的不同方法

时间:2015-11-02 18:00:57

标签: c assembly system-calls

在某些代码中,我可以看到以奇怪的方式调用系统调用,以sched_yield为例:

#define __NR_sys_sched_yield    __NR_sched_yield

inline _syscall0(void, sys_sched_yield);

然后我们可以使用sys_sched_yield()。 我很好奇直接使用sched_yield和这种方式之间的区别。 在src / include / asm / unistd中,定义了_syscall0

#define _syscall0(type,name)  \
type name(void)     \
{        \
    long __res;      \
    __asm__ volatile ("int $0x80"  \
    : "=a" (__res)    \
    : "0" (__NR_##name));   \
    __syscall_return(type,__res);  \
}

2 个答案:

答案 0 :(得分:0)

可能是那些可能无法使用Song ID的系统。 至于差异,=INDEX('worksheet 1'!A:A,MATCH("*"&B5&"*",'worksheet 1'!D:D,0)) 会在出错时返回sched_yield并设置sched_yield,而此实现可能会从内核返回原始值。无法确定,因为您没有提供必须是宏的-1的定义。

答案 1 :(得分:0)

这是使用glibc的linux。 BSD有一个sched_yield但有自己的libc。

这并不奇怪。 syscall0宏发出汇编int 0x80指令,并且系统调用号位于rax寄存器[x86架构]中。这是linux的标准系统调用接口。在幕后,所有作为系统调用包装器的linux glibc函数都会这样做[或使用更现代的sysenter / sysexit x86指令配对]

glibc倾向于在其包装函数中“篡夺”系统调用并添加它们周围的东西。例如,当你调用fork时,它[最终]调用__libc_fork,它会执行大量与线程和文件关闭等相关的额外内容。

一般来说,glibc做出了不错的选择。但是,有时经验丰富的Linux应用程序程序员希望原始系统调用行为,特别是如果他们正在编写必须与内核,设备驱动程序或设备硬件进行密切交互的系统实用程序或程序/库。 / p>

实际上,__libc_fork不会调用fork 系统调用,而是调用clone 系统调用,这是[更难使用] fork的超集。但是,普通的fork系统调用仍然存在。所以,如果你想要那个,你需要宏观的东西 - 我敢打赌某处有一个sys_fork定义。

另一方面,glibc可能会将sched_yield ala POSIX实现为nop,返回-1并将errno设置为ENOSYS。我刚检查了最新的glibc源代码,除了mach之外我找不到“真正的”实现。它可能确实做了真实的事情,我找不到它。

有时候,linux有一个系统调用,但是glibc不想支持它,或者他们认为它对于应用程序程序员来说太危险了,所以他们忽略了包装函数。因此,宏是一种“结束”glibc的方式。

glibc实施sched_yield作为nop(posix)的可能原因是他们认为它“不好”并且可能会告诉您使用nanosleep代替。我已经使用了它们,它们相同,具体取决于您的用例和所需效果。

有时,您需要执行原始的内联系统调用。例如,内核调用ELF加载器[每个支持ELF二进制文件的系统必须有一个并且linux是ld-linux.so]来加载ELF二进制文件。它必须在glibc.so可用之前运行,因为它实际上是glibc.so中的链接,ELF加载器必须有一些内置的系统调用openread

此外,大多数系统都有一个syscall函数,它接受可变数量的参数。你可以实现:

#define my_sched_yield() syscall(__NR_sched_yield)
#define my_read(_fd,_buf,_len) syscall(__NR_read,_fd,_buf,_len)

此函数处理内核的系统调用返回值/错误并设置errno。这就是__syscall_return宏必须做的事情。

__NR_*前缀是linux使用的,但其他系统有AUE_*SYS_*