标签:
进程是Unix操作系统抽象概念中最基本的一种。
进程管理是所有操作系统的心脏所在。
1. 进程是处于执行期的程序。除了可执行程序代码,还包括打开的文件、挂起的信号、内核内部数据、一个或者多个执行线程等多种资源
2. 线程:是进程中活动的对象。每个线程都有一个独立的程序计数器,进程栈和一组进程寄存器。内核调度的对象是线程。
3. 在现代操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。同一进程中的线程之间可以共享虚拟内存,但是每个都拥有自己的虚拟存储器
4. 进程的生命周期
5. 进程在创建它的时刻开始存活,这通常是调用fork系统的结果。该系统调用通过复制一个现有进程来创建一个全新的进程。fork系统调用从内核返回两次,一次到父进程,另一次回到新产生的子进程。
进程描述符:进程列表存放在任务队列(task list)这一双向链表中,链表的项是task_struct即进程描述符的结构。该类型定义在<linux/sched.h>中。进程描述符包含的数据能完整地描述一个正在执行的程序:
每个任务的thread_info结构在它的内核栈的尾端分配。每个任务的堆栈尾端(比如,对于向上增长的堆栈来说,就是在堆栈的栈顶)有结构体thread_ info,它指向了task_ struct结构体
1. 内核中的大部分处理处理进程的代码都是通过task_ struct进行的;因此,需要通过current宏查找到当前正在运行进程的进程描述符
2. 通过current宏查找到当前正在运行进程的进程描述符。X86系统中,current把栈指针的后13个有效位屏蔽掉,用来计算出thread_ info的偏移(通过current_ thread_ info函数)
movl $-8192, %eax
andl %esp,%eax3.进程状态
3.内核通过一个唯一的进程标识值PID来标识每个进程。pid存放在各自进程描述符中。
进程在任何时刻,都必定处于五种状态中的一种
调用set_ task_ state(task,state)函数将进程设置为指定状态
所有的进程都是pid为1的init进程的后代。
内核在系统启动的最后阶段启动init进程。
系统中的每一个进程必有一个父进程,可以拥有0个或多个子进程,拥有同一个父进程的进程叫做兄弟。
这种关系存放在进程描述符中,parent指针指向父进程task_struct,children是子进程链表。
获得父进程的进程描述符:
struct task_struct *my_parent = current->parent;
访问子进程:
struct task_struct *task;
struct list_head *list;
list_for_each(list, ¤t->children){
task = list_entry(list, struct task_struct, sibling);
/* task现在指向当前的某个子进程 */
}
init进程的进程描述符是作为init_task静态分配的。
获取链表中的下一个进程:
list_entry(task->tasks.next, struct task_struct, tasks);
获取链表中的上一个进程:
list_entry(task->tasks.prev, struct task_struct, tasks);
以上依赖于next_task(task)和prev_task(task)这两个宏实现。
for_each_process(task)宏,依次访问整个任务队列,每次访问任务指针都指向链表中的下一个元素。
struct task_struct *task;
for_each_process(task){
/* 它打印出每一个任务的名称和PID */
printk("%s[%d]\n",task->comm, task->pid);
}
Unix系统的进程创建方式
※一般内核会选择子进程首先执行。
why?
一般子进程会马上调用exec()函数,避免写时拷贝的额外开销。
最后copy_process返回的就是指向子进程的指针
除了不拷贝父进程的页表项外,vfork()系统调用和fork()功能相同。子进程作为父进程的一个单独的线程在它的地址空间里运行,父进程被阻塞,直到子进程退出或执行exec()。
vfork()系统调用的实现是通过向clone()系统调用传递一个特殊标志来进行的:
线程机制提供了在同一程序内共享内存地址空间运行的一组线程。在Linux系统中,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都有自己的task_struct。
1. 线程的创建与普通进程类似,只不过在调用clone()的时候需要传递一些参数标志来指明共享的资源
2. 传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类
内核线程与普通进程的区别只在于内核线程没有独立的地址空间,从来不切换到用户空间,可以被调度和被抢占.
终结进程大部分依赖于do_exit()来完成:
给子进程重新找养父(线程组中的其他线程或者init进程)
调用schedule()切换到新的进程
这之后,进程不可运行并处于EXIT_ZONBIE退出状态,占用的所有内存就是内核栈、thread_info结构和task_struct结构。此时进程存在的唯一目的就是向它的父进程提供信息。
1. 孤儿进程:父进程在进程之前退出,就会遗留下子进程,也就是孤儿进程
2. 解决方法:在当前的线程组内给孤儿进程寻找新的父进程;
否则直接以init作为其父进程
do_exit()中会调用exit_notify()
exit_notify()会调用forget_original_parent()
forget_original_parent()会调用find_new_reaper()
然后遍历所有子进程并为它们设置新的父进程。
然后调用ptrace_exit_finish()同样进行新的寻父过程,是给ptraced的子进程寻找父亲。
最后init进程会例行调用wait()来检查其子进程,清除所有与其相关的僵死进程。
一旦系统为进程成功地找到和设置了新的父进程,就不会再有出现驻留僵死进程的危险了。init进程会例行调用wait()来检查其子进程,清除所有与其相关的僵死进程。
标签:
原文地址:http://www.cnblogs.com/fuyujing/p/5358554.html