系统调用是应用程序和linux内核交互的主要接口,或许可以通过学习各个系统调用的具体实现来加深对linux的理解。
应用程序运行在用户态,syscall的实现是运行在内核态,需要有一种机制从用户态切换到内核态,然后才能执行syscall的代码,状态转换通常是由cpu提供的指令来实现,如中断int 0x80(cpu当然也可以提供其他实现,如x86下的sysenter)。内核启动的时候会设置好中断表,中断表可以理解为中断号码和中断处理程序的一个映射,比如int 0x80对应的中断处理程序就是系统调用处理程序,在x86_64下,处理程序叫做system_call,在arch/x86/kernel/entry_64.S里实现。
每个系统调用都有一个编号,system_call通过系统调用编号来调用具体的系统调用:call *sys_call_table(,%rax,8) 。
x86_64下sys_call_table的定义可以在arch/x86/kernel/Syscall_64.c里面找到,它include了arch/x86/include/asm/unistd_64.h这个文件:
/*
* This file contains the system call numbers.
*
* Note: holes are not allowed.
*/
/* at least 8 syscall per cacheline */
#define __NR_read 0
__SYSCALL(__NR_read, sys_read)
#define __NR_write 1
__SYSCALL(__NR_write, sys_write)
#define __NR_open 2
__SYSCALL(__NR_open, sys_open)
#define __NR_close 3
__SYSCALL(__NR_close, sys_close)
#define __NR_stat 4
__SYSCALL(__NR_stat, sys_newstat)
#define __NR_fstat 5
sys_call_table将每个系统调用编号和具体的系统调用实现关联起来。系统调用的具体实现分布在内核代码各处。如open这个系统调用是在fs/open.c中实现的:
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode)
{
long ret;
if (force_o_largefile())
flags |= O_LARGEFILE;
ret = do_sys_open(AT_FDCWD, filename, flags, mode);
/* avoid REGPARM breakage on x86: */
asmlinkage_protect(3, ret, filename, flags, mode);
return ret;
}
SYSCALL_DEFINE3是一个宏,在include/linux/syscalls.h里定义:
#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
上面宏展开之后,应当是这个样子:
sys_open (const char __user *, filename, int, flags, int, mode)
{
...
}
sys_open的具体实现是在do_sys_open这个函数里完成,这个函数和一般c编写的函数没有太大的差别,这不过它是允许在内核态的。
各个系统调用基本处理流程基本上应该是差不多的,通过搜索SYSCALL_DEFINE[0-6],可以找到各个系统调用的具体实现。
如 find /path/to/src -name *.c | xargs grep 'SYSCALL_DEFINE[0-6]'
__END__