Linux内核分析第四周作业
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核分析第四周作业相关的知识,希望对你有一定的参考价值。
使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
首先从系统调用表中选择一个系统调用,我选择了122号系统调用——uname
122 i386 uname sys_newuname
先用man查一下api是怎么使用的
man 2 api
这里的2表示查询系统调用uname(2),否则默认查的是uname(1),也就是在shell中运行的uname程序。
可以看到,要使用uname,首先要include头文件sys/utsname.h。然后定义一个utsname结构体的变量,将这个变量的指针作为参数传递给uname。系统就会在结构体中填充信息,都是字符串,可以直接打印。
于是写下最简单的代码:
#include <stdio.h> #include <sys/utsname.h> int main() { struct utsname name; uname(&name); printf("--API--\\n"); printf("%s\\n", name.sysname); printf("%s\\n", name.nodename); printf("%s\\n", name.release); printf("%s\\n", name.version); printf("%s\\n", name.machine); return 0; }
编译以后看一下运行结果
然后考虑用内联汇编实现系统调用。将代码修改一下
1 #include <stdio.h> 2 #include <sys/utsname.h> 3 4 int main() 5 { 6 struct utsname name; 7 8 asm volatile( 9 "mov %0, %%ebx\\n\\t" 10 "mov $122, %%eax\\n\\t" 11 "int $0x80\\n\\t" 12 : 13 :"r"(&name) 14 ); 15 16 printf("--ASM--\\n"); 17 printf("%s\\n", name.sysname); 18 printf("%s\\n", name.nodename); 19 printf("%s\\n", name.release); 20 printf("%s\\n", name.version); 21 printf("%s\\n", name.machine); 22 return 0; 23 }
编译以后看一下运行结果
和API的调用方式结果一样。
下面分析一下汇编代码调用的细节。
第9行向EBX写入参数,即name结构体的地址
第10行将系统调用号122写入EAX
第11行引发0x80的中断。中断会立刻运行IRQ。
Linux内核在启动之初的trap_init函数中就将0x80的IRQ固定为系统调用的处理函数
其中SYSCALL_VECTOR的值就是0x80
因为中断是内核处理的,所以此时已经进入内核态。
0x80的IRQ——system_call是用汇编写的
一开始保存了现场
然后根据EAX中的系统调用号,去调用对应的系统调用。最后将调用结果保存起来,退出IRQ的时候通过EAX返回给用户程序。
顺便看一眼这次选择的系统调用代码。根据系统调用表,112对应的系统调用名字叫做newuname(居然还有olduname和oldolduname。。。)
很简单,uname的内容一直在内核中保存着,每次系统调用只要将内容从内核态复制到用户态即可,并且复制的时候用锁保护了一下。
总结
Linux为了系统的稳定性,分为用户态和内核态。用户态的程序无法使用系统的底层功能,因此需要系统调用临时地让内核替用户程序去完成一些底层功能。
无论是API还是汇编的方式,都是通过0x80中断进入内核态,然后内核根据EAX中的系统调用号去调用对应的系统调用函数,最后将返回值通过EAX返回给用户态程序。
此外,系统调用是进程调度切换的时机,因为此时控制交给了内核。对于用户程序来说仅仅是调用了系统调用,然后从系统调用返回。在系统调用中发生的进程切换对于用户程序是透明的。
王岩
原创作品转载请注明出处
《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
以上是关于Linux内核分析第四周作业的主要内容,如果未能解决你的问题,请参考以下文章
linux内核分析第四周-使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用