Linux系统调用

来源:互联网 发布:上海斑马公寓知乎 编辑:程序博客网 时间:2024/06/06 02:16

杨明辉 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”

一、实验过程

1.进入实验楼,打开终端输入命令cd Code进入Code目录,然后输入>open.c新建文件,并将C语言的源码输入文件中(源码下面给出),然后输入命令gcc main.c -o main -m32对源文件进行编译,最后输入命令./main运行程序。实验结果如图1所示:


图1

2. 再次输入命令>openms.c新建一个c程序文件,然后将利用嵌入式汇编编写的c语言源程序输入文件中;
输入命令gcc openms.c -o openms -m32编译c程序,最后输入./openms运行程序,实验结果如图2所示。

图2

二、源码分析

1.首先利用c语言库函数的API来编写程序进行系统调用,本次实验使用的是open(sys_open)系统调用,系统号为5。程序源码及源码分析如下所示:

   程序源码

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>int main(int argc, char *argv[]){    int fd;    char str[100];    fd = open("text.txt",O_RDONLY);    if(fd < 0)    {        perror("open");    }    while(read(fd,str,sizeof(str)) > 0)    {        printf("%s",str);    }    close(fd);    return 0;    }
源码分析

1. 首先程序开头的#include<fcntl.h>导入了open函数所在的文件,只有导入该文件后,程序才可以调用open函数。

2. open函数的定义为 int open(const char * pathname, int flags);  

3. 参数pathname指向文件的目录,flags为文件打开方式。

4. flags在头文件fcntl.h定义,其中的含义为:

1. O_RDONLY 以只读方式打开文件

2. O_WRONLY 以只写方式打开文件

3. O_RDWR 以可读写方式打开文件。上述三种旗标是互斥的,也就是不可同时使用,但可与下列的旗标利用OR(|)运算符组合。

4. O_CREAT 若欲打开的文件不存在则自动建立该文件。

5. O_EXCL 如果O_CREAT 也被设置,此指令会去检查文件是否存在。文件若不存在则建立该文件,否则将导致打开文件错误。此外,若O_CREATO_EXCL同时设置,并且欲打开的文件为符号连接,则会打开文件失败。

6. O_NOCTTY 如果欲打开的文件为终端机设备时,则不会将该终端机当成进程控制终端机。

7. O_TRUNC 若文件存在并且以可写的方式打开时,此旗标会令文件长度清为0,而原来存于该文件的资料也会消失。

8. O_APPEND 当读写文件时会从文件尾开始移动,也就是所写入的数据会以附加的方式加入到文件后面。

9. O_NONBLOCK 以不可阻断的方式打开文件,也就是无论有无数据读取或等待,都会立即返回进程之中。

10. O_NDELAY O_NONBLOCK

11. O_SYNC 以同步的方式打开文件。

12. O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接,则会令打开文件失败。

13. O_DIRECTORY 如果参数pathname 所指的文件并非为一目录,则会令打开文件失败。

5. 当文件成功打开后函数会返还一个正整数的文件描述符,附值给fd,利用该文件描述符可以对文件进行读写及其它相关操作,本次程序读利用read()取文件中的数据,然后输出,最后调用close()函数关闭文件。

2. 利用嵌入式汇编来进行来进行系统调用,程序源码及源码分析如下所示:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <fcntl.h>int main(){int fd;char path[]="text.txt";char context[100];int re=O_RDONLY;asm volatile("mov %2,%%ecx\n\t""mov %1,%%ebx\n\t""mov $0x5,%%eax\n\t""int $0x80\n\t""mov %%eax,%0\n\t": "=m" (fd):"b"(path),"c"(re)); if(fd < 0)    {        perror("open");    }    while(read(fd,context,sizeof(context)) > 0)    {        printf("%s",context);    }    close(fd);    return 0;       }    
源码分析:

1. 本程序除了打开文件的方式是利用嵌入式汇编语言外,其它的程序与上面的完全一样。

2. 在Linux中的嵌入式汇编是通过执行int $0x80来执行系统调用的,这条汇编指令产生向量为128的编程异常,达到进行系统调用的目的。

3. 内核实现了很多不同的系统调用,进程必须指明需要哪个系统调用,这需要传递一个名为系统调用号的参数,这个参数使用eax寄存器来传递。

4. 系统调用也需要输入输出参数,比如:实际的值、用户态进程地址空间的变量的地址、甚至是包含指向用户态函数的指针的数据结构的地址等参数。

5. 在系统调用号(eax)之外,还可以利用ebx,ecx,edx,esi,edi,ebp来传递参数,但每个参数的长度不能超过寄存器的长度,即32位。

6. 本次实验通过ecx和ebx来传递参数,系统调用后的返回结果保存在eax中。

三、 总结

1. 操作系统为用户态进程与硬件设备进行交互提供了一组接口——系统调用有如下好处:
把用户从底层的硬件编程中解放出来
极大的提高了系统的安全性
使用户程序具有可移植性
2.  操作系统中的状态分为管态(核心态)和目态(用户态)。

3. 特权指令:一类只能在核心态下运行而不能在用户态下运行的特殊指令。不同的操作系统特权指令会有所差异,但是一般来说主要是和硬件相关的一些指令。

4.  用户程序只在用户态下运行,有时需要访问系统核心功能,这时通过系统调用接口使用系统调用。

5.  由于操作系统提供了系统调用,才使得应用程序编写更加简单,程序运行更加流畅,系统运行更加安全。

6.  系统调用通过软中断向内核发出一个明确的请求,是操作系统为用户态进程与硬件设备进行交互提供的一组接口,系统调用正是通过中断来完成的。






0 0