本文为我学习孟宁老师的linux内核课程的总结,同时也作为课程学习的作业。
唐建,《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
1、系统调用概述
通常用户空间是无法直接访问内核空间的,但是有时候确实需要这样做,于是就产生的系统调用,系统调用是操作系统对用户空间提供的访问内核空间的API。
系统调用分为:系统调用API、封装例程、系统调用处理程序、系统调用服务程序,其中系统调用处理程序和服务程序在内核空间。
API 1 系统调用例程1 处理程序1
API2 系统调用例程2 system_call 处理程序2
API3 系统调用例程3 处理程序3
…… …… ……
四者对应关系如上,多个API可能对应一个系统调用,一个API也可能对应对个系统调用,一个系统调用对应一个封装例程。
2、系统调用流程
用户态调用系统调用API时,调用到系统调用封装例程,例程通过命令触发一个软终端(系统调用专用,中断号:0x80),系统进入内核态,并走到
system_call,可以认为这个就是此软终端的中断服务程序入口,然后通过传递过来的系统调用号来决定调用相应的系统调用服务程序。
3、详细流程,及举例
下面我通过跟踪和模拟系统调用来展示整个流程。
(1)/下图是一个简单的程序,里面使用了open、read 这两个最常用的系统调用。右边是输出结果。
(2)、下面我们将API改成汇编,直接通过汇编来调用系统调用,进而分析系统调用的过程。
首先查看系统调用号到文件/arch/x86/syscalls/syscall_32.tbl 或者syscall_64.tbl 文件。如下图,第四列就是对应的内核中的处理函数。
上述代码与前面的c代码对比。第一部分是 open系统调用,第二部分是read 系统调用。
__asm__ __volatile__ (
"mov %1, %%ebx\n\t" ——将open需要的参数准备好,文件路径
"mov %2, %%ecx\n\t" ——准备参数,文件打开权限
"mov $5,%%eax\n\t" ——系统调用号,open的系统调用号为5
"int $0x80\n\t" ——触发中断0X80
"mov %%eax, %0\n\t" ——系统调用返回了,将返回参数取出来。
:"=m"(fd)
:"p"(path), "c"(O_RDONLY)
);
__asm__ __volatile__ (
"mov %2, %%ecx\n\t" ——准备入参
"mov $3, %%eax\n\t" ——系统调用号
"int $0x80\n\t" ——触发中断
"mov %%eax, %0\n\t" ——取出返回值
:"=m"(n)
:"b"(fd),"p"(buf),"d"(20)
);
4、总结
(1)、 每个系统调用都对应一个系统调用号,而系统调用号就对应内核中的相应处理函数。
(2)、所有系统调用都是通过中断0x80来触发的。
(3)、使用系统调用时,通过eax 寄存器将系统调用号传递到内核,系统调用的入参通过ebx、ecx……依次传递到内核
(4)、和函数一样,系统调用的返回值保存在eax中,所有要从eax中取出