程序员的自我修养——第十二章——系统调用与API
系统调用接口往往是通过中断来实现,比如Linux使用0x80号中断作为系统调用的入口,Windows采用0x2E号中断作为系统调用的入口。
EAX |
名字 |
C语言定义 |
含义 |
参数 |
1 |
exit |
void _exit(int status) |
退出进程 |
EBX表示退出码 |
2 |
fork |
pid_t fork(void) |
复制进程 |
EBX表示复制参数 |
3 |
read |
ssize_t read( int fd, void *buf, size_t count) |
读文件 |
EBX表示文件句柄,ECX表示读取缓冲地址,EDX表示读取的大小 |
4 |
write |
ssize_t write( int fd, const void *buf, size_t count); |
写文件 |
同sys_read |
5 |
open |
Int open( Const char *path int flags, mode_t mode); |
打开文件 |
EBX表示文件路径,ECX表示打开文件的模式,EDX也表示打开的模式 |
... |
|
|
|
|
我们可以通过系统调用open()、read()和close()来绕过glibc的fopen()、fread()、fclose()。
/*sys_call_01.c*/
#include <unistd.h>
int main()
{
char buffer[64];
char buffer_r[64];
char *error_message = "open file error \n";
char *success_message = "open file success \n";
char *message = "please input something\n";
write(0,message,strlen(message));
int r_len = read(1,buffer_r,64);
buffer_r[r_len] = '\0';
printf("read_len = %d \t buffer_read = %s \n",r_len,buffer_r);
int fd = open("readme.txt",0,0);
if(fd == -1)
{
write(0, error_message, strlen(error_message));
return -1;
}
write(0,success_message, strlen(success_message));
read(fd, buffer,64);
printf("buffer: %s\n",buffer);
close(fd);
return 0;
}
注:stdin,stdout, stderr 的文件描述符分别为'0','1','2'
$ ./sys_call_01
please input something
read from stdin
read_len = 16 buffer_read = read from stdin
open file success
buffer: this is a test!
this is a test!
this is a test!
this is a tes/
在不同的系统中,系统调用是不相同的,为了统一,各种编程语言提供了运行库的接口来统一相同的功能。
比如,C语言里面的fread,用于读取文件,在Windows下这个函数的实现可能是调用ReadFile这个API,而如果在Linux下则很可能调用read这个系统调用。但不在管哪个平台,我们都可以使用C语言运行库的fread来读取文件。
运行时库将不同的操作系统的系统调用包装为统一固定的接口,使得同样的代码,在不同的操作系统下都可以直接编译,并产生一致的效果。这就是源代码级上的可移植性。
中断一般有两个属性,一个称为中断号(从0开始),一个称为中断处理程序(Interrupt Service Routine,ISR)。不同的中断具有不同的中断号,而同时一个中断处理程序一一对应一个中断号。在内核中,有一个数组称为中断向量表(Interrupt Vector Table),这个数组的第n项包含了指向第n个中断的中断处理程序的指针。
一个函数指针的数组:
(* ivt[])(int n) = { isr_01, isr-02, isr_03,...}
CPU中断过程
基于int的Linux的经典系统调用实现:
- 触发中断 2. 切换堆栈 3. 中断处理程序
在2.6的内核里面没有找到_syscall0
Windows API
一个普通的fwrite()的调用路径:
Windows API现在的数量很庞大,按照功能被划分为几大类:
所以不管内核如何改变接口,只要维持API层面的接口不变,理论上所有的应用程序都不用重新编译就可以正常运行,这也是Windows API存在的主要原因。
对于第下一章的内容暂时不转上来了,我只看了C的运行库的设计,基本上看懂了,什么时候来好好学习一下!