linux内核设计与实现一书阅读整理 之第三章
chapter 3 进程管理
3.1 进程
- 进程就是处于执行期的程序。
- 进程就是正在执行的程序代码的实时结果。
- 内核调度的对象是线程而并非进程。
在现代操作系统中,进程提供两种虚拟机制:
虚拟处理器
虚拟内存
- 进程是处于执行期的程序以及相关的资源的总称。
- 进程包括代码段和其他资源。
几个函数:
fork():创建新进程
exec():创建新的地址空间并把新的程序载入其中
clone():fork实际由clone实现
exit():退出执行
wait4():父进程查询子进程是否终结
wait()、waitpid():程序退出执行后变为僵死状态,调用这两个消灭掉。
3.2 进程描述符及任务结构
- 内核把进程的列表存放在叫做任务队列的双向循环链表中。
- 链表中的每一项都是类型为task_struct、称为进程描述符的结构。
- 进程描述符的类型为task_struct,里面包含的数据有:
3.2.1 分配进程描述符
- Linux通过slab分配器分配task_struct结构——能达到对象复用和缓存着色的目的。
- 分配:每个任务的堆栈尾端(比如,对于向上增长的堆栈来说,就是在堆栈的栈顶)有结构体threadinfo,它指向了taskstruct结构体
- 每个任务的threadinfo结构在它的内核栈的尾端分配。 结构中task域中存放的是指向该任务实际taskstruct的指针。
3.2.2 进程描述符的存放
- 内核通过一个唯一的进程标识值PID来标识每个进程。
- pid类型为pidt,实际上就是一个int类型,最大值默认设置为32768,如若需要,可有系统管理员通过修改/proc/sys/kernel/pidmax上限。
- pid存放在各自进程描述符中。
- 内核中的大部分处理处理进程的代码都是通过task_struct进行的;因此,需要通过current宏查找到当前正在运行进程的进程描述符
X86系统中,current把栈指针的后13个有效位屏蔽掉,用来计算出threadinfo的偏移(currentthread_info函数)
movl $-8192, %eax
andl %esp,%eax
3.2.3 进程状态
进程描述符中的state域是用来描述进程当前状态的。共有五种状态,标志如下:
- TASK_RUNNING(运行):进程是可执行的,或者正在执行,或者在运行队列中等待执行
- TASK_INTERRUPTIBLE(可中断):进程正在睡眠/被阻塞
- TASK_UNINTERRUPTIBLE(不可中断):睡眠/被阻塞进程不被信号唤醒
- TASK_TRACED:被其他进程跟踪的进程
- TASK_STOPPED(停止):进程停止执行;进程没有投入运行也不能投入运行。 接收到SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU等信号时,或者调试时收到任何信号,都可以进入这种状态。
状态转换图如下:
3.2.4 设置当前进程状态
使用settaskstate(task,state)函数.
settaskstate(task,state); //将任务task的状态设置为state
setcurrentstate(state) 和下面等价
- settaskstate(current,state)
3.2.5 进程上下文
- 可执行代码从一个可执行文件载入到进程的地址空间执行。当一个程序执行了系统调用,内核就会“代表进程执行”并处于进程上下文中
- 对比:在中断上下文中,系统不代表进程执行——不会有进程去干扰这些中断处理程序
3.2.6 进程家族树
- 所有的进程都是PID为1的init进程的后代
对于给定的进程,获取链表中下一个进程:
- list_entry(task->tasks.prev,struct
- task_struct,tasks)
3.3 进程创建
一般操作系统产生进程的机制:
1. 在新的地址空间创建进程
2. 读入可执行文件
3. 执行
Unix的机制:
fork()和exec()。 fork():
通过拷贝当前进程创建一个子进程。
子进程与父进程的区别仅在于PID,PPID和某些资源和统计量
exec():
读取可执行文件并将其载入地址空间开始运行。
3.3.1 写时拷贝
- Linux的fork()使用写时拷贝推迟甚至免除拷贝。内核在创建新进程的时候并不复制整个地址空间,而是让父进程和子进程共享同一个拷贝;直到子进程/父进程需要写入的时候才进行拷贝
- 因而,fork的实际开销只是复制父进程的页表以及给子进程创建唯一的进程描述符
3.3.2 fork()
-** Linux通过clone系统调用实现fork** - 创建进程的大概步骤如下:
fork()、vfork()、__clone()都根据各自需要的参数标志调用clone()。
由clone()去调用do_fork()。
do_fork()调用copy_process()函数,然后让进程开始运行。
返回do_fork()函数,如果copy_process()函数成功返回,新创建的子进程被唤醒并让其投入运行。
一般内核会选择子进程首先执行。
3.3.3 vfork()
除了不拷贝父进程的页表项之外,vfork()系统调用和fork()的功能相同。理想情况下不要调用vfork()。
子进程作为父进程的一个单独的线程在它的地址空间里运行 ,父进程被阻塞,直到子进程退出或执行exec()。子进程不能向地址空间写入。
vfork()系统调用的实现是通过向clone()传递一个特殊标志来进行的。
调用copyprocess()是,taskstruct的vfor_done成员被设置为NULL。
- 执行dofork()时,如果给定特定标志,则vfordone会指向一个特定地址。
- 子进程先开始执行后,父进程不是马上恢复执行,而是一直等待,知道子进程通过vfordone指针向它发送信号。 在调用mmrelease()时,该函数用于进程退出内存地址空间,并且检查vfor_done是否为空,如果不为空,则会向父进程发送信号。
- 回到dofork(),父进程醒来并返回。 ## 3.4 线程在linux中实现 ## 在Linux系统中,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都有自己的taskstruct
3.4.1 创建线程
与普通进程类似,只不过在调用clone()的时候需要传递一些参数标志来指明共享的资源
3.4.2 内核线程
与普通进程的区别只在于内核线程没有独立的地址空间。
- 它只能通过其他内核线程创建;内核通过kthread内核进程衍生所有的内核线程
- 新创建的线程处于不可运行状态,直到wake_up_process()明确地唤醒它
## 3.5 进程终结 ## - 进程终结时,内核必须释放它所占有的资源并告知父进程。
进程终结的原因:一般是来自自身,发生在调用exit()系统调用时。
显式的调用
隐式的从某个程序的主函数返回
大部分依赖于do_exit()来完成。其中有几个重点:
……
给子进程重新找养父(线程组中的其他线程或者init进程)
调用schedule()切换到新的进程
……
- 这之后,进程不可运行并处于EXITZONBIE退出状态,占用的所有内存就是内核栈、threadinfo结构和task_struct结构。此时进程存在的唯一目的就是向它的父进程提供信息。
3.5.1 删除进程描述符
释放task_struct结构发生在父进程获得已终结的子进程信息并且通知内核不关注后,需要的系统调用是wait4():
挂起调用它的进程,直到其中的一个子进程退出,此时函数返回该子进程的PID。
- 释放进程描述符时,需要调用release_task()。
3.5.2 孤儿进程
- 概述:父进程在进程之前退出,就会遗留下子进程,也就是孤儿进程
- 其解决方法:
- 在当前的线程组内给孤儿进程寻找新的父进程;否则直接以init作为其父进程
- 调用顺序:
- do_exit()--
- >forget_original_parent()-
- >find_new_parent()-
- >ptrace_exit_finish()
- (这一函数是为被跟踪的进程寻找父进程,因为被跟踪的进程会以调试程序作为临时父亲)
总结
在本章中,我知道了操作系统的核心概念--进程,也跟着书本学习了进程的一般特性的重要性等等,很有趣。
参考资料
《linux内核设计与实现》原书第三版
linux内核设计与实现一书阅读整理 之第三章的更多相关文章
- linux内核设计与实现一书阅读整理 之第五章
CHAPTER 5 系统调用 5.1 与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层,该层主要作用有三个: 为用户空间提供了一种硬件的抽象接口 系统调用保证了系统的稳定和安全 每个进 ...
- linux内核设计与实现一书阅读整理 之第一二章整合
第一章:Linux内核简介 一.Unix和linux Unix是一个强大.健壮和稳定的操作系统. 1.Unix内核特点 十分简洁:仅提供几百个系统调用并且有明确的目的: 在Unix中,大部分东西都被( ...
- linux内核设计与实现一书阅读整理 之第十八章
CHAPTER 18 调试 18.1 准备开始 需要的是准备是: - 一个bug - 一个藏匿bug的内核版本 - 相关内核代码的知识和运气 重点: 想要成功的进行调试,就取决于是否能让这些错误重现. ...
- 《Linux内核设计与实现》CHAPTER17阅读梳理
<Linux内核设计与实现>CHAPTER17阅读梳理 [学习时间:3.5hours] [学习内容:设备类型,模块,内核对象,sysfs] 个人思考部分见[]标出的部分 一.课堂讲解整理& ...
- 《Linux内核设计与实现》CHAPTER4阅读梳理
<Linux内核设计与实现>CHAPTER4阅读梳理 [学习时间:3hours] [学习内容:多任务:进程调度策略:Linux中进程调度的关键问题:抢占] 个人思考部分见[]标出的部分 一 ...
- 《Linux内核设计与实现》CHAPTER18阅读梳理
<Linux内核设计与实现>CHAPTER18阅读梳理 [学习时间:2hours] [学习内容:bug的来源分析:bug调试途径] 一.bug来源 1.内核中的bug 内核中的bug表现得 ...
- 《Linux内核设计与实现》CHAPTER5阅读梳理
<Linux内核设计与实现>CHAPTER5阅读梳理 [学习时间:2.5hours] [学习内容:系统调用的概念.功能及实现:系统调用的创建和使用方法] CHAPTER5 系统调用 1.系 ...
- 《Linux内核设计与实现》第四周读书笔记——第五章
<Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...
- 《Linux内核设计与实现》CHAPTER13阅读梳理
<Linux内核设计与实现>第13章阅读总结 [edited by 5216lwr] 一.虚拟文件系统概述 1.虚拟文件系统 (也称作虚拟文件交换或VF)作为内核子系统,为用户空间程序提供 ...
随机推荐
- 小强版之无码理解C语言指针
1. 先从普通变量开始 2. 撸完变量撸指针 3. 故事情节进一步发展,此处少儿不宜 4. 奶茶妹妹捉奸,小强死定了 5. 源码欣赏 #include <stdio.h> ...
- Iron Speed Designer设计工具开发总结
9.0版本: 1.1 ISP和VS不要同时生成,代码写在override方法之下,不然生成之后会覆盖;正常情况下,ISP可以写代码,只不过没有快捷提示,一般我们先注释一下字段(如://sdsfdsfd ...
- ThinkPHP3.2开发仿京东商城项目实战视频教程
ThinkPHP3.2仿京东商城视频教程实战课程,ThinkPHP3.2开发大型商城项目实战视频 第一天 1.项目说明 2.时间插件.XSS过滤.在线编辑器使用 3.商品的删除 4.商品的修改完成-一 ...
- Pod的创建过程
Pod是kubernetes中最小的调度单位,里面包含多个容器,也是真正运行你服务的仓库,同一个pod中容器之间资源共享(IP .网络.cpu.mem.挂载目录等). 1. 准备一个yaml(RC/ ...
- Java 内存模型_2
title: Java 内存模型_2 date: 2017-01-28 02:04:06 tags: [JMM] categories: [Programming,Java] --- Why 理解 J ...
- python循环综合运用
循环很重要,计算机很蠢,唯一的优势就是按照指令不停的执行,所以决定在说一下. break语句,用在循环体中,迫使循环立即终止,即跳出所在循环体,继续执行循环体后面的语句. sum=0 i=1 whil ...
- podSpec文件相关知识整理
上一篇文章整理了我用SVN创建私有库的过程,本文将整理一下有关podSpec文件的相关知识. podSpec中spec的全称是“Specification”,说明书的意思.顾名思义,这是用来描述你这个 ...
- spring boot开启热部署
步骤一:添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...
- Linux第一二章笔记
第一章 Linux内核简介 1. Unix内核的特点 简洁:仅提供系统调用并有一个非常明确的设计目的 抽象:几乎所有东西都被当做文件 可移植性:使用C语言编写,使得其在各种硬件体系架构面前都具备令人惊 ...
- java实验2实验报告(20135232王玥)
实验二 Java面向对象程序设计 一.实验内容 1. 初步掌握单元测试和TDD 2. 理解并掌握面向对象三要素:封装.继承.多态 3. 初步掌握UML建模 4. 熟悉S.O.L.I.D原则 5. 了解 ...