4.内核线程(主要了解这个概念)
内核经常需要在后台执行一些操作,这种任务可以通过内核线程(kernel thread)完成--独立运行在内核空间的标准进程。
内核线程和普通的进程间的区别在于内核线程没有独立的地址空间(实际上它的mm指针被设置为NULL)。
它们只在内核空间运行,从来不切换到用户空间去。内核进程和普通进程一样,可以被调度,也可以被抢占。
这些线程在系统启动时由另外一些内核线程启动。实际上,内核线程也只能由其他内核线程创建。
在现有内核线程中创建一个新的内核线程的方法如下:
int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
一般情况下,内核线程会将它在创建时得到的函数永远执行下去,该函数通常由一个循环构成,在需要的时候,
这个内核线程就会被唤醒和执行,完成了当前任务,它会自行休眠。
----------------------------------------------------------------------------------------------------
【系统调用】
1.系统调用号
每一个系统调用被赋予一个独一无二的系统调用号,应用程序通过调用号指明想执行哪个系统调用,不会提及系统调用的名称。
一旦分配就不能再有任何变更,否则编译好的应用程序就会崩溃。如果一个系统调用被删除,它所占用的系统调用号也不允许被回收利用。
系统维护着一张系统调用表,记录了每一个注册的系统调用。
2.系统调用处理程序
用户空间的程序不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以在内核的地址空间读写的话,系统安全就会失去控制。
所以,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换至内核态,这样内核就可以代表应用程序来执行该系统调用了。
通知内核的机制是靠软中断实现的:通过引发一个异常来促使系统切换到内核态去执行异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。
X86系统上的软中断由int$0x80指令产生。这条指令会触发一个异常导致系统切换到内核态并执行第128号异常处理程序,而该程序正是系统调用程序,叫system_call()。
因为所有的系统调用陷入内核的方式都一样,所以仅仅是陷入内核空间是不够的,需要把系统调用号一并传给内核,
这个传递动作是通过在触发中断前把调用号装入eax寄存器实现的。system_call通过给定的系统调用号,执行相应的系统调用。
3.参数传递
系统调用需要的参数也像传递系统调用号一样,通过寄存器传递。在X86系统上,ebx、ecx、edx、esi和edi按照顺序存放前五个参数。
给用户空间返回的值夜通过寄存器传递。在X86系统上,它存放在eax寄存器中。
系统调用要求功能明确、必要且要考虑长远,Linux尽量避免系统调用的添加。
跟内核的交互,可通过创建设备节点,用read、write访问,用ioctl()来进行特别的设置操作和获取特别信息。