Linux系统调用

最近重新开始看操作系统,看得我云里雾里,经典的操作系统书又很难,看不大懂。学校的操作系统课,说实在的,真的好难和自己真正编程结合起来,里面的概念很多,而且很多都是很老的,历史总是这么蛋疼。没办法,只能硬着头皮去看。这次先讲讲系统调用,因为没看过里面的具体源码,但是尽可能让文章偏向实际编程中的应用。

什么叫系统调用


计算机有很多的硬件,比如键盘,鼠标,网卡,磁盘,声卡这些硬件都是我们实际可以触碰到的,但是对于用户应用程序而言是没办法直接操作这些设备的。为什么呢?一是硬件涉及到很多电路实现,让用户自己编写程序去操作是很困难和复杂的。二是编程的人直接操作硬件会有很大的安全隐患,因为这些资源都是很多程序共享的,万一我们写的程序能够直接操作硬件,安全性就不能保障了。所以所有这些设备的上面抽象了一层驱动程序,这些驱动程序会实现很多硬件相关操作,也就是说每个硬件发布商都得提供各自的硬件的驱动程序用于操作硬件。那么应用程序怎么去调用这些驱动程序呢?答案就是通过操作系统的系统调用,我们告诉操作系统我们要对某个硬件做某个操作,操作系统来帮你完成,然后把得到的结果返回给我们。

space
整个内存分成两部分,一部分是内核空间,还有一部分是用户空间。内核空间跑的程序就是操作系统,内存管理,进程管理,文件管理,设备管理等。用户空间就是我们用户跑的程序,比如一个程序加载到内存,里面会有堆,栈,数据段,代码段等,这些占用的都是用户空间。用户程序要去访问硬件设备,必须先切换到内核空间,通过内核的预定义函数去访问。
也就是说,用户程序想要去访问外部资源,必须要通过系统调用来实现。系统调用是操作系统留给用户程序的唯一访问内核空间的途径,不然用户程序任意访问内存,特别是涉及到内核态的代码就会有安全隐患。

系统调用做了什么


在用户进程调用read()函数的时候,系统调用是怎么知道要调用的是sys_read呢?也就说怎么区别用户函数相对应的系统调用呢?主要是通过系统号来指定的,每个系统号都对应一个系统调用函数。
系统调用都是操作系统内核编译时候预先指定好的,查看/usr/include/syscall.h
#define SYS_syscall        0
#define SYS_exit           1
#define SYS_fork           2
#define SYS_read           3
#define SYS_write          4
...

比如我们的read()函数,在应用程序调用read(),因为read()是属于libc库里的,会去调用libc里的read(),而libc里面的read()调用会使CPU的执行从用户态切换到内核态。具体的实现是libc里面的read()函数会产生一个软中断,一般都是int 0x80中断,这个中断是系统预定义好的,一旦产生这个中断,CPU的现场状态就会从用户态切换到内核态去执行预定义的函数,system_call()函数是内核统一的调用接口。那么怎么找到相应的系统调用呢?得把系统调用号也从用户空间传递到内核空间,传递的过程很简单,就是把系统调用号放到寄存器,然后在内核态取出。内核会根据系统调用号来执行sys_read(),这个函数其实也是很高度的抽象,因为对于Linux而言,万物皆文件,具体到不同的设备根据描述符来区别。同样的道理,read()函数的参数也会通过寄存器传递到内核态,比如预定义读函数的buffer内存地址。

syscall

系统调用返回后,操作系统会调用syscall_exit,这会把读取到的内容从内核copy到用户空间。

标签:Linux, Kernel

评论已关闭