快捷搜索:  汽车  科技

linux如何实现定制系统调用(Linux系统原理之系统调用)

linux如何实现定制系统调用(Linux系统原理之系统调用)另外使用“strace”命令,后面直接跟Linux命令,可以直接查看整个ls操作的过程,调用了哪些库函数和系统调用。6)继续在libc库中执行,执行完成后返回到用户应用程序中。3)System_call()函数根据传递过来的参数处理所有的系统调用。使用system_call_table[参数]执行系统调用。4)系统调用返回。5)执行iret或者sysexit汇编指令两种方式退出系统调用,并调用resume_userspace()函数进入用户空间。

系统调用

系统调用是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口获得操作系统内核提供的服务。例如,用户额可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件;通过时钟相关的系统调用获得系统时间或设置系统时间;通过进程控制相关的系统调用来创建进程、实现进程调度、进程管理等。

操作系统的主要功能是为管理硬件资源和为应用程序开发人员提供良好的环境来使应用程序具有更好的兼容性,为了达到这个目的,所有操作系统在内核里提供一系列具备预定功能的多内核函数,这些内建函数完成对硬件的访问和对文件的打开、读写、关闭等操作。Linux系统中称这些函数叫做“系统调用”,即System Call。这些函数实现了将操作从用户空间转换到内核空间,有了这些接口函数,用户就可以方便地访问硬件了。例如,在用户空间调用open()函数,则会在内核空间调用sys_open()。系统调用把应用程序的请求传给内核,调用相应的的内核函数完成所需的处理,将处理结果返回给应用程序。一个已经安装的系统所支持的系统调用都可以在/usr/include/bits/syscall.h文件里看到。

1)用户程序中调用库函数abc()。

2)系统加载libc库调用索引和参数后,执行int $0x80中断(0x80中断对应的中断例程被称为system call handler)或者sysenter汇编指令进入系统调用,因为中断使得进程从用户态进入内核态,所以参数通过寄存器传送。执行system_call函数。

3)System_call()函数根据传递过来的参数处理所有的系统调用。使用system_call_table[参数]执行系统调用。

4)系统调用返回。

5)执行iret或者sysexit汇编指令两种方式退出系统调用,并调用resume_userspace()函数进入用户空间。

6)继续在libc库中执行,执行完成后返回到用户应用程序中。

另外使用“strace”命令,后面直接跟Linux命令,可以直接查看整个ls操作的过程,调用了哪些库函数和系统调用。

系统调用中输入输出的参数为实际传递的值或者是用户态进程的地址,或者是指向用户态函数指针的数据结构地址。传递的参数放在寄存器eax中,即系统调用号。寄存器传递参数的个数满足两个条件。一是参数的长度不超过寄存器的长度,如果是32位平台不超过32位,64位平台不超过64位。二是不包括eax中的系统调用号,参数的个数不超过6个。

再谈系统调用和库函数

系统调用

系统调用提供的函数如open close read write ioctl等,需包含头文件unistd.h。以write为例:其函数原型为size_t write(int fd const void *buf size_t nbytes),其操作对象为文件描述符或文件句柄fd(file descriptor),要想写一个文件,必须先以可写权限用open系统调用打开一个文件,获得所打开文件的fd,例如 fd=open(/”/dev/video/” O_RDWR)。fd是一个整型值,每新打开一个文件,所获得的fd为当前最大fd加1。Linux系统默认分配了3个文件描述符值:

0-standard input,1-standard output,2-standard error。

系统调用通常用于底层文件访问(low-level file access),例如在驱动程序中对设备文件的直接访问。系统调用是操作系统相关的,因此一般没有跨操作系统的可移植性。

系统调用发生在内核空间,因此如果在用户空间的一般应用程序中使用系统调用来进行文件操作,会有用户空间到内核空间切换的开销。事实上,即使在用户空间使用库函数来对文件进行操作,因为文件总是存在于存储介质上,因此不管是读写操作,都是对硬件(存储器)的操作,都必然会引起系统调用。也就是说,库函数对文件的操作实际上是通过系统调用来实现的。例如C库函数fwrite()就是通过write()系统调用来实现的。这样的话,使用库函数也有系统调用的开销,为什么不直接使用系统调用呢?这是因为,读写文件通常是大量的数据(这种大量是相对于底层驱动的系统调用所实现的数据操作单位而言),这时,使用库函数就可以大大减少系统调用的次数(系统调用越少系统开销越小性能越好)。这一结果又缘于缓冲区技术,在用户空间和内核空间,对文件操作都使用了缓冲区。例如用fwrite写文件,都是先将内容写到用户空间缓冲区,当用户空间缓冲区满或者写操作结束时,才将用户缓冲区的内容写到内核缓冲区,同样的道理,当内核缓冲区满或写结束时才将内核缓冲区内容写到文件对应的硬件媒介。

库函数调用

标准C库函数提供的文件操作函数如fopen fread fwrite fclose fflush fseek等,需包含头文件stdio.h。以fwrite为例,其函数原型为size_t fwrite(const void *buffer size_t size size_t item_num FILE *pf),其操作对象为文件指针FILE *pf,要想写一个文件,必须先以可写权限用fopen函数打开一个文件,获得所打开文件的FILE结构指针pf,例如pf=fopen(/”~/proj/filename/” /”w/”)。实际上,由于库函数对文件的操作最终是通过系统调用实现的,因此,每打开一个文件所获得的FILE结构指针都有一个内核空间的文件描述符fd与之对应。同样有相应的预定义的FILE指针:stdin-standard input,stdout-standard output,stderr-standard error。库函数调用通常用于应用程序中对一般文件的访问。库函数调用是系统无关的,因此可移植性好。由于库函数调用是基于C库的,因此也就不可能用于内核空间的驱动程序中对设备的操作


后面会分享更多运维方面的内容,感兴趣的朋友可以关注下!!

linux如何实现定制系统调用(Linux系统原理之系统调用)(1)

猜您喜欢: