日期:2014-05-16  浏览次数:20600 次

Linux内核中的进程(二)

版权所有,转载请说明转自 http://my.csdn.net/weiqing1981127

 

进程是正在执行的程序代码的实时结果,是处于执行期的程序以及相关的资源的总称。线程是在进程中活动的对象,内核调度的对象是线程,而不是进程。对Linux系统而言,不区分线程和进程。进程提供两种虚拟机制:虚拟处理器和虚拟内存。线程之间可以共享内存,但每个都拥有各自的虚拟处理器。在Linux中,通过fork创建子进程,子进程通过exit()系统调用终结进程并将其占用的资源释放掉,此时子进程被设置为僵死状态,父进程可以通过wait()waitpid()系统调用查询子进程是否终结。

 

内核把进程的列表存放在叫任务队列(task_list)的双向循环列表中,链表中的每一个项都是类型为task_struct,称为进程描述符。Linux通过slab分配器分配task_struct结构,由于现在用的slab分配器动态生成task_struct,所以只需要在栈低或者栈顶创建一个新的结构体struct thread_info。内核通过进程标识值PID来标识每个进程,内核把每个进程的PID存放在它们各自的进程描述符中,所有的进程都是PID1init进程的后代,PID最大默认值为32768,这个值越小转一周就越快,系统管理员可以通过修改/proc/sys/kennel/pid_max来提高上限。

 

进程五种状态标志:TASK_RUNNINGTASK_INTERRUPTIBLETASK_UN INTERRUPTIBLE_TASK_TRACED_TASK_STOPPED

 

进程上下文

一般程序在用户空间执行,当一个程序执行了一个系统调用或者触发了某个异常,它就陷入内核空间,此时,我们称内核“代表进程执行”并处于进程上下文中。除非在此间隙有更高优先级的进程需要执行并且由调度器做出了相应的调整,否则在内核退出的时候,程序恢复在用户空间会继续执行。系统调用和异常处理程序是对内核明确定义的接口,对内核的所有访问都必须通过这些接口。

 

进程创建

子进程和父进程区别仅仅在于PID(每个进程唯一)PPID(父进程的进程号,子进程将其设置为被拷贝进程的PID)和某些资源和统计量(如挂起的信号)Linuxfork()使用写时拷贝页实现,写时拷贝页是一种可以推迟甚至免除拷贝数据的技术,内核此时并不是赋值整个进程地址空间,而是让父进程和子进程共享同一个拷贝。fork()的实际开销就是赋值父进程的页表以及给子进程创建唯一的进程描述符。内核有意会选择子进程先执行,因为一般子进程都会马上调用exec()函数,这样避免了写时拷贝页的额外开销,如果父进程先执行的话,有肯能会开始向地址空间写入。Vfork()除了不拷贝父进程的页表项外,其他跟fork一样,由于vfork语意微妙,系统最好不要用这个函数。

 

线程创建

用户空间创建线程可以用clone函数创建,但我们的重点是内核线程,内核常常需要在后台执行一些操作,这种任务可以通过内核线程完成。内核线程和普通进程的区别在于内核线程没有独立的地址空间,它们只在内核空间运行,从来不切换到用户空间去,内核线程和普通进程一样,可以被调度,可以用被抢占。

内核通过从kthreadd内核进程中衍生出所有新的内核线程的,于是,从现有内核线程中创建一个新的内核线程有两种方法。其一,利用kthread_creat()函数创建,并用