课本学习笔记4:第三章 20135115臧文君

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了课本学习笔记4:第三章 20135115臧文君相关的知识,希望对你有一定的参考价值。

进程管理

 

注:作者:臧文君,原创作品转载请注明出处。

 

一、进程

1、进程管理是所有操作系统的心脏所在。

2、进程:是处于执行期的程序以及相关的资源的总称,实际上,进程就是正在执行的程序代码的实时结果。

3、执行线程:简称线程thread,是在进程中活动的对象。

4、内核调度的对象是线程,而不是进程。

5、对Linux而言,线程是一种特殊的进程。

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

同一个进程中的线程之间可以共享虚拟内存,但每个都拥有各自的虚拟处理器。

7、Linux系统中,调用fork()系统调用,通过复制一个现有进程来创建一个全新的进程。

调用fork()的进程称为父进程,新产生的进程称为子进程。

fork()系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。

调用exec()这组函数可以创建新的地址空间,并把新的程序载入其中。

fork()实际上是由clone()系统调用实现的。

程序通过exit()系统调用退出执行。

 

二、进程描述符及任务结构

1、内核把进程的列表存放在叫做任务队列的双向循环链表中。

2、链表中的每一项都是类型为task_struct、称为进程描述符的结构,其中包含一个具体进程的所有信息。

进程描述符相对较大:

技术分享

3、分配进程描述符

Linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色的目的。

4、进程描述符的存放

(1)内核通过一个唯一的进程标识值或PID来标识每个进程。

(2)PID的最大默认设置为32768,它实际上就是系统中允许同时存在的进程的最大数目,可以由系统管理员通过修改/proc/sys/kernel/pid_max来提高上限。

(3)内核把每个进程的PID存放在它们各自的进程描述符中,通过current宏查找到当前正在运行进程的进程描述符。

5、进程状态:进程描述符中的state域。

技术分享

技术分享

6、内核调整某个进程的状态,使用set_task_state(task,state)函数。

7、可执行代码是进程的重要组成部分。

系统调用和异常处理程序是对内核明确定义的接口。

8、进程之间存在继承关系,所以的进程都是PID为1的init进程的后代。

 

三、进程创建

1、Unix进程的创建通过fork()和exec()。

fork()通过拷贝当前进程创建一个子进程,exec()函数负责读取可执行文件并将其载入地址空间开始运行。

2、Linux的fork()使用写时拷贝(copy-on-write)页实现。

fork()的实际开销就是复制父进程的页表以及给子进程创建唯一的进程描述符。

3、fork()、vfork()和_clone()库函数都根据各自的参数标志去调用clone(),然后由clone()去调用do_fork(),do_fork()调用copy_process()函数,然后让进程开始运行。

4、vfork()除了不拷贝父进程的页表项外,与fork()的功能相同。

 

四、线程在Linux中的实现

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

线程机制支持并发程序设计技术,在多处理器系统上,它也能保证真正的并行处理。

2、Linux把所有的线程都当做进程来实现,线程仅仅被视为一个与其他进程共享某些资源的进程。

3、线程的创建与进程类似,只是在调用clone()的时候需要传递一些参数标志来指明需要共享的资源。

父子进程共享地址空间、文件系统资源、文件描述符和信号处理程序。

技术分享

技术分享

4、内核线程

(1)内核经常需要在后台执行一些操作(例:flush,ksofirqd),这些任务通过内核线程完成。

(2)内核线程与普通进程的区别:内核线程没有独立的地址空间,只在内核空间运行,不切换到用户空间。

相同:可以被调度,可以被抢占。

(3)内核线程也只能有其他的内核线程创建,内核通过从kthreadd内核进程中衍生出所有新的内核线程来自动处理这一点。

(4)新创建的进程处于不可运行状态,如果不通过调用wake_up_process()明确地唤醒它,它不会主动运行。

创建一个进程并让它运行起来,可以通过调用kthread_run()来实现。

 

五、进程终结

1、当一个进程终结时,内核必须释放它所占有的资源,并告知父进程。

2、大部分的进程终结都要靠do_exit()。

3、在父进程获得已终结的子进程的信息后,或者通知内核它并不关注那些信息后,子进程的task_struct结构才被释放。

当最终需要释放进程描述符是,release_task()会被调用。

4、如果父进程在子进程之前退出,必须有机制来保证子进程能找到一个新的父亲,否则这些成为孤儿的进程就会在退出时永远处于僵死状态,白白地耗费内存。

解决方法:给子进程在当前线程组内找一个线程作为父亲,如果不行,就让init作为它们的父进程。

5、init进程会例行调用wait()来检查其子进程,清除所有与其相关的僵死进程。

 

以上是关于课本学习笔记4:第三章 20135115臧文君的主要内容,如果未能解决你的问题,请参考以下文章

作业7:可执行程序的装载 20135115臧文君

作业1:计算机是如何工作的?20135115臧文君

作业6:进程的描述和进程的创建 20135115臧文君

作业3:构造一个简单的Linux系统MenuOS 20135115臧文君

《Linux内核》课本读书笔记 第三章

Java高并发学习笔记:Thread详解