首页 > 代码库 > Linux内核-进程管理

Linux内核-进程管理

Linux内核-进程管理


1.进程

#内核调度的对象是线程,不是进程

#对Linux而言,线程只是特殊的进程

#进程提供两种虚拟机制:虚拟处理器、虚拟内存

#创建进程通过fork()来从父进程复制创建进程


2.进程描述符

#任务队列:双向链表(每一项都是task_struct--->进程描述符)

#Linux通过slab分配器分配task_struct

#内核通过唯一的进程标识值(PID)来标识进程

#五种进程状态:  1.TASK_RUNNING 进程可执行

                        2.TASK_INTERRUPTIBLE 可中断

                        3.TASK_UNINTERRUPTIBLE 不可中断

                        4._TASK_TRACED 被其他进程跟踪

                        5._TASK_STOPPED 进程停止执行

#设置进程状态: set_task_state(task, state)函数和set_surrent_state(state)函数

#所有进程都是PID为1的init进程的后代

#init进程描述符 init_task


3.进程创建

#fork()和exec()

 fork()拷贝当前进程创建子进程

 exec()读入可执行文件并载入空间地址开始执行

#写时拷贝:fork()使用写时拷贝(copy-on-write)使得地址空间的页拷贝被推迟到实际发生写入时才进行

                   (fork()后立即执行exec()就无需进行拷贝)

                    fork()的实际开销就是复制父进程的页表以及分配进程描述符

#fork()执行过程:通过系统调用clone()调用fork()

    clone()调用do_fork(),do_fork()调用copy_process()

    copy_process()过程:

        1.调用dum_task_struct()为新进程创建内核栈、thread_info、task_struct

        2.确保创建子进程后当前进程数没有超过限制

        3.子进程开始与父进程区别开来,进程描述符中的许多成员初始化或清0

        4.子进程的状态被设置为TASK_UNINTERRUPTIBLE

        5.调用copy_flags()更新来更新task_struct的flags成员

        6.调用alloc_pid()为新进程分配PID

        7.copy_process()拷贝共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间

        8.copy_process()做扫尾工作并返回一个指向子进程的指针

#vfork():向clone()系统调用传递一个特殊标志来进行:

    ?    ?1.调用copy_process()时,task_struct的vfork_done成员被设置为NULL

    ?    ?2.如果执行do_fork()时给定地址,vfork_done会指向一个特定地址

    ?    ?3.子进程开始执行后,父进程不会立即开始执行,直到vfork_done指针向它发出信号

    ?    ?4.调用mm_release()进程退出地址空间,并检查vfork_done是否为空,若不为空,等待父进程发送信号

    ?    ?5.回到do_fork(),父进程醒来并返回


4.线程在Linux中的实现

#线程机制提供了在同一程序中共享内存地址的空间运行的一组线程

#Linux将所有的线程当做进程来实现

#创建线程:

    ?clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND , 0)


5.进程终结

#发生在进程调用exit()系统调用时:

    ?任务大部分要靠do_exit()来完成:

    ?    ?1.将task_struct的标志成员设置为PF_EXITING

    ?    ?2.调用del_timer_sync()删除任一内核定时器

    ?    ?3.如果BSD的进程记账开启,do_exit()调用acct_update_integrals()来输出记账信息

    ?    ?4.调用exit_mm()释放进程占用的mm_struct

    ?    ?5.调用sem_exit(),如果进程等待IPC信号,则离开队列

    ?    ?6.调用exit_files()和exit_fs()

    ?    ?7.把存放在task_struct的exit_code成员中的任务退出代码置为由exit()提供的退出代码

    ?    ?8.调用exit_notify()向父进程发送信号,为子进程寻找养父,养父为线程组中的其他线程或者init,并把进程状态设为EXIT_ZOMBIE

    ?    ?9.do_exit()调用schedule()切换到新的进程

#删除进程描述符:调用了do_exit(),进程僵死,但是系统还保留了进程描述符

    ?释放进程描述符时,release_task()会被调用:

    ?    ?1.调用_exit_signal(),该函数调用_unhash_process(),后者又调用 detach_pid()从pidhash上删除该进程,同时也要从任务列表中删除该进程

    ?    ?2._exit_signal()释放目前僵死进程的资源,进行统计记录

    ?    ?3.如果该进程为进程组最后一个进程,并且领头已经死掉,release_task()通知领头进程的父进程

    ?    ?4.release_task()调用put_task_struct()释放进程内核栈和thread_info所占的页

#孤儿进程造成的进退维谷:如果父进程在子进程之前退出,必须有机制保证子进程能找到新的父亲

  

Linux内核-进程管理