linux内核基础(系统调用,简明)

时间:2021-06-01 15:48:13

内核基础(系统调用)

在说系统调用之前,先来说说内核是怎么和我们交互的,或者说是怎么和我们产生交集的。

首先,内核是用来控制硬件的只有内核才能直接控制硬件,所以说内核很重要,如果内核被控制那么电脑的一切都被控制了,所以我们需要把内核保护起来,所以SHELL 就诞生了,我们绝大多数情况下是在和SHELL 交互,应用程序也运行与SHELL之上,当执行一些进程时,进程会切换进程上下文,这时进程从用户态切换到内核态,也就是常说的内核代表进程执行。但是就算如此内核依然是被保护起来的,我们始终不能和内核直接进行数据的交换,我们几乎不可能从内核中读取数据。但是内核依然能明白我们写的函数需要运行的进程,因为我们的函数是调用的C库,而C库中的函数均是系统调用(可以理解位内核的一些功能函数)的封装表现,所以说内核可以理解并且能运行上层的应用程序。

系统调用号:

在Linux中每一系统调用都有一个系统调用号,进程是不会提及系统调用名称的。如果一个系统调用被删除,它所占的系统调用号不会被回收,因为如果回收,那么其他以前调用这个函数的程序就会调用成别的函数,其实是错误的,它还很高兴的执行呢,其中一个系统调用sys_ni_syscall( )就是用来填充被删除系统调用的空白的,这个函数只返回-ENOSYS这个值,然后什么都不干,内核记录了系统调用表中的所有已经注册过的系统调用列表,存储在sys_call_table中,每一种体系结构都明确定义了这个表,里面指定了唯一的系统调用号。


系统调用的性能:

Linux系统调用比其他很多的操作系统执行的要快。Linux很短的上下文切换时间很短是一个重要的原因,进出内核都被优化的很便捷高效,另一个重要的原因就是每一个系统调用本身就已经非常高效便捷了。

系统调用处理程序:

就像前边说的那样用户空间是不能执行内核代码的,所以系统调用函数不能直接放在用户空间中执行,所以必须要靠软中断来实现,通过一个异常来促使系统切换到内核态去处理异常处理程序,此时异常处理程序就是系统调用程序。X86有一个叫做sysenter 的指令,这条指令被用来使陷入内核执行系统调用的方式。

系统调用的使用

因为所有的系统调用陷入内核的方式都一样,所以仅仅是陷入内核空间是不够的因此必须把系统调用号一并传给内核。X86系统是将系统调用号放入eax寄存器中的。通样系统调用的参数也是先放在寄存器中一并传入的,(那几个寄存器不同系统是不同的)。

参数验证:

不论是系统调用还是其他的一般的程序,参数验证一直都是一个重要的话题,参数验证可以将错误发生在产生损失之前。

尤其是系统调用,如果在内核中发生参数的错误,可能会造成极端的不良影响,所以必须进行严格的参数验证,一般有如下几条:

1.传递的指针指向的区域属于用户空间,进程绝不能哄骗内核去读取内核空间的数据(内核是很单纯的)

2.指针指向的内存区域必须在进程的地址空间里,不能哄骗内核去读取其他进程的数据

3.读或者写或者是读写的权限内存必须分明,进程决不能绕过内存的访问限制

内核提供了两种方法来完成必须的检查和内核空间和用户空间的数据的来回拷贝,copy_to_user()  copy_from_user()

都需要三个参数,第一个是目的内存地址,第二个是源内存地址,第三个位需要操作的字节数。都是原子操作。