几个基本的关系:
1、系统调用 & API
应用程序编程接口(API)是一个函数定义,例如常见的sysinfo(),getpid(),open()等函数,这些函数说明了如何获得一个给定的服务;
系统调用是通过软中断向内核发出一个明确的请求。
有些API的实现调用了多组系统调用,而有些则不需要内核提供信息,就不用系统调用。
2、系统调用 & 系统命令
系统命令相对于API高一层,每个系统命令都是一个可执行程序,例如:ls等,这些命令都使用了系统调用。
3、系统调用 & 内核函数
内核函数与普通函数在形式上没有什么区别,只不过前者在内核实现,需要满足一些内核编程的要求;
系统调用是用户进入内核的接口层,它本身并非内核函数,它由内核函数实现。
进入内核后,不同的系统调用调用对应的内核函数,这些函数被称为系统调用的服务例程,例如getpid()实际调用的服务例程为sys_getpid()。现在用SYSCALL_DEFINE0代替。
- #define SYSCALL_DEFINE0(name) asmlinkage long sys_##name(void)
现在来以sysinfo()系统调用为例来分析它的执行过程。
a、用户态下的sysinfo()函数
b、经过libc库的封装
c、经过int 0×80陷入内核
由硬件完成。
d、system_call处理找到对应的服务例程
- ENTRY(system_call)
- RING0_INT_FRAME # can't unwind into user space anyway
- pushl %eax # save orig_eax
- CFI_ADJUST_CFA_OFFSET 4
- SAVE_ALL
- GET_THREAD_INFO(%ebp)
- # system call tracing in operation / emulation
- testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags(%ebp)
- jnz syscall_trace_entry
- cmpl $(nr_syscalls), %eax
- jae syscall_badsys
- syscall_call:
- call *sys_call_table(,%eax,4)
- movl %eax,PT_EAX(%esp) # store the return value
- syscall_exit:
- ......
首先保存系统调用号%eax;
SAVE_ALL:将系统调用的参数压到内核栈中.
e、经过上面的宏处理后到函数 SYSCALL_DEFINE1(sysinfo, struct sysinfo __user *, info)
- SYSCALL_DEFINE1(sysinfo, struct sysinfo __user *, info)
- {
- struct sysinfo val;
- do_sysinfo(&val);
- if (copy_to_user(info, &val, sizeof(struct sysinfo)))
- return -EFAULT;
- return 0;
- }
该函数首先执行do_sysinfo()函数去填充val结构体,然后调用copy_to_user()函数将数据拷贝到用户态缓冲区中。
总结一下:系统调用首先通过libc库分装,然后通过int 0×80陷入内核,system_call处理,内核函数的实现,放回数据给用户。
其中有些细节没有说到,需要对应代码去一步步分析,例如上面提到的SAVE_ALL这个宏通过反汇编编译内核后的vmlinuz文件,在system_call处可以看到SAVE_ALL的具体执行过程。
转自:
阅读(2565) | 评论(0) | 转发(0) |