一、进程
1.进程就是出于执行期的程序
2.执行线程,简称线程,是在进程中活动的对象
3.进程提供两种虚拟机制:虚拟处理器和虚拟内存
二、进程描述符和任务结构
1.内核把进程的列表存放在焦作任务队列的双向循环链表中,其中每一项都是类型为task_struct、称为进程描述符的结构
2.进程描述符的分配和存放:
(1)目的:Linux通过slab分配task_struct结构,以达到对象复用以及和缓存着色的目的(避免资源动态分配和释放带来的资源消耗)
(2)分配:每个任务的堆栈尾端(比如,对于向上增长的堆栈来说,就是在堆栈的栈顶)有结构体thread_info,它指向了task_struct结构体
(3)查找:
1)内核中的大部分处理处理进程的代码都是通过task_struct进行的;因此,需要通过current宏查找到当前正在运行进程的进程描述符
2)X86系统中,current把栈指针的后13个有效位屏蔽掉,用来计算出thread_info的偏移(通过current_thread_info函数)
3.进程状态
进程在任何时刻,都必定处于五种状态中的一种
(1)TASK_RUNNING
(2)TASK_INTERRUPT
(3)TASK_UNINTERRUPT
(4)TASK_TRACED
(5)TASK_STOPPED
4.设置进程当前状态
(1)调用set_task_state(task,state)函数将进程设置为指定状态
5.进程上下文
(1)可执行代码从一个可执行文件载入到进程的地址空间执行。当一个程序执行了系统调用,内核就会“代表进程执行”并处于进程上下文中
(2)对比:在中断上下文中,系统不代表进程执行——不会有进程去干扰这些中断处理程序
三、进程创建
1.Unix系统的进程创建方式
(1)fork()通过拷贝当前进程创建一个子进程
(2)exec()负责读取可执行文件并将其载入地址空间开始运行
(3)写时拷贝
1)Linux的fork()使用写时拷贝推迟甚至免除拷贝。内核在创建新进程的时候并不复制整个地址空间,而是让父进程和子进程共享同一个拷贝;直到子进程/父进程需要写入的时候才进行拷贝
2)因而,fork的实际开销只是复制父进程的页表以及给子进程创建唯一的进程描述符
2.fork函数
(1)Linux通过clone系统调用实现fork
(2)由clone去调用do_fork()
(3)定义在<kernel/fork.c>中的do_fork()完成创建中的大部分工作,它调用copy_process函数,然后让进程开始运行
四、内核线程
1.内核线程:独立运行在内核空间的标准进程。
2.内核线程没有独立的地址空间,只在内核空间运行,从来不切换到用户空间,可以被调度和被抢占。
3.内核线程只能由其他内核线程创建
五、进程终结
1.进程终结时,内核必须释放它所占有的资源并告知父进程。
2.进程终结的原因:一般是来自自身,发生在调用exit()系统调用时。
3.删除进程描述符
通过release_task()实现进程描述符的删除,至此,所有资源都被释放了
4.解决孤儿进程
(1)父进程在进程之前退出,就会遗留下子进程,也就是孤儿进程
(2)解决方法:在当前的线程组内给孤儿进程寻找新的父进程;
直接以init作为其父进程