Linux读书笔记第三、四章
第三章
主要内容:
- 进程和线程
- 进程的生命周期
- 进程的创建
- 进程的终止
1. 进程和线程
进程和线程是程序运行时状态,是动态变化的,进程和线程的管理操作(比如,创建,销毁等)都是有内核来实现的。
Linux中的进程于Windows相比是很轻量级的,而且不严格区分进程和线程,线程不过是一种特殊的进程。
所以下面只讨论进程,只有当线程与进程存在不一样的地方时才提一下线程。
进程提供2种虚拟机制:虚拟处理器和虚拟内存
每个进程有独立的虚拟处理器和虚拟内存,
每个线程有独立的虚拟处理器,同一个进程内的线程有可能会共享虚拟内存。
内核中进程的信息主要保存在task_struct中(include/linux/sched.h)
进程标识PID和线程标识TID对于同一个进程或线程来说都是相等的。
Linux中可以用ps命令查看所有进程的信息:
ps -eo pid,tid,ppid,comm
2. 进程的生命周期
进程的各个状态之间的转化构成了进程的整个生命周期。
3. 进程的创建
Linux中创建进程与其他系统有个主要区别,Linux中创建进程分2步:fork()和exec()。
fork: 通过拷贝当前进程创建一个子进程
exec: 读取可执行文件,将其载入到内存中运行
创建的流程:
- 调用dup_task_struct()为新进程分配内核栈,task_struct等,其中的内容与父进程相同。
- check新进程(进程数目是否超出上限等)
- 清理新进程的信息(比如PID置0等),使之与父进程区别开。
- 新进程状态置为 TASK_UNINTERRUPTIBLE
- 更新task_struct的flags成员。
- 调用alloc_pid()为新进程分配一个有效的PID
- 根据clone()的参数标志,拷贝或共享相应的信息
- 做一些扫尾工作并返回新进程指针
创建进程的fork()函数实际上最终是调用clone()函数。
创建线程和进程的步骤一样,只是最终传给clone()函数的参数不同。
比如,通过一个普通的fork来创建进程,相当于:clone(SIGCHLD, 0)
创建一个和父进程共享地址空间,文件系统资源,文件描述符和信号处理程序的进程,即一个线程:clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)
在内核中创建的内核线程与普通的进程之间还有个主要区别在于:内核线程没有独立的地址空间,它们只能在内核空间运行。
这与之前提到的Linux内核是个单内核有关。
4. 进程的终止
和创建进程一样,终结一个进程同样有很多步骤:
子进程上的操作(do_exit)
- 设置task_struct中的标识成员设置为PF_EXITING
- 调用del_timer_sync()删除内核定时器, 确保没有定时器在排队和运行
- 调用exit_mm()释放进程占用的mm_struct
- 调用sem__exit(),使进程离开等待IPC信号的队列
- 调用exit_files()和exit_fs(),释放进程占用的文件描述符和文件系统资源
- 把task_struct的exit_code设置为进程的返回值
- 调用exit_notify()向父进程发送信号,并把自己的状态设为EXIT_ZOMBIE
- 切换到新进程继续执行
子进程进入EXIT_ZOMBIE之后,虽然永远不会被调度,关联的资源也释放掉了,但是它本身占用的内存还没有释放,
比如创建时分配的内核栈,task_struct结构等。这些由父进程来释放。
父进程上的操作(release_task)
父进程受到子进程发送的exit_notify()信号后,将该子进程的进程描述符和所有进程独享的资源全部删除。
从上面的步骤可以看出,必须要确保每个子进程都有父进程,如果父进程在子进程结束之前就已经结束了会怎么样呢?
子进程在调用exit_notify()时已经考虑到了这点。
如果子进程的父进程已经退出了,那么子进程在退出时,exit_notify()函数会先调用forget_original_parent(),然后再调用find_new_reaper()来寻找新的父进程。
find_new_reaper()函数先在当前线程组中找一个线程作为父亲,如果找不到,就让init做父进程。(init进程是在linux启动时就一直存在的)
第四章
主要内容:
- 什么是调度
- 调度实现原理
- Linux上调度实现的方法
- 调度相关的系统调用
1. 什么是调度
现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程)。
这个管理程序就是调度程序,它的功能说起来很简单:
- 决定哪些进程运行,哪些进程等待
- 决定每个进程运行多长时间
此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更紧急的进程打断。
总之,调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用CPU(即尽量少的切换进程,进程切换过多,CPU的时间会浪费在切换上);另一方面,保证各个进程能公平的使用CPU(即防止一个进程长时间独占CPU的情况)。
2. 调度实现原理
前面说过,调度功能就是决定哪个进程运行以及进程运行多长时间。
决定哪个进程运行以及运行多长时间都和进程的优先级有关。为了确定一个进程到底能持续运行多长时间,调度中还引入了时间片的概念。
2.1 关于进程的优先级
进程的优先级有2种度量方法,一种是nice值,一种是实时优先级。
nice值的范围是-20~+19,值越大优先级越低,也就是说nice值为-20的进程优先级最大。
实时优先级的范围是0~99,与nice值的定义相反,实时优先级是值越大优先级越高。
实时进程都是一些对响应时间要求比较高的进程,因此系统中有实时优先级高的进程处于运行队列的话,它们会抢占一般的进程的运行时间。
3. Linux上调度实现的方法
Linux上的调度算法是不断发展的,在2.6.23内核以后,采用了“完全公平调度算法”,简称CFS。
CFS算法在分配每个进程的CPU时间时,不是分配给它们一个绝对的CPU时间,而是根据进程的优先级分配给它们一个占用CPU时间的百分比。
比如ProcessA(NI=1),ProcessB(NI=3),ProcessC(NI=6),在CFS算法中,分别占用CPU的百分比为:ProcessA(10%),ProcessB(30%),ProcessC(60%)
因为总共是100%,ProcessB的优先级是ProcessA的3倍,ProcessC的优先级是ProcessA的6倍。
4. 调度相关的系统调用
调度相关的系统调用主要有2类:
1) 与调度策略和进程优先级相关 (就是上面的提到的各种参数,优先级,时间片等等) - 下表中的前8个
2) 与处理器相关 - 下表中的最后3个
系统调用 |
描述 |
nice() |
设置进程的nice值 |
sched_setscheduler() |
设置进程的调度策略,即设置进程采取何种调度算法 |
sched_getscheduler() |
获取进程的调度算法 |
sched_setparam() |
设置进程的实时优先级 |
sched_getparam() |
获取进程的实时优先级 |
sched_get_priority_max() |
获取实时优先级的最大值,由于用户权限的问题,非root用户并不能设置实时优先级为99 |
sched_get_priority_min() |
获取实时优先级的最小值,理由与上面类似 |
sched_rr_get_interval() |
获取进程的时间片 |
sched_setaffinity() |
设置进程的处理亲和力,其实就是保存在task_struct中的cpu_allowed这个掩码标志。该掩码的每一位对应一个系统中可用的处理器,默认所有位都被设置,即该进程可以再系统中所有处理器上执行。 用户可以通过此函数设置不同的掩码,使得进程只能在系统中某一个或某几个处理器上运行。 |
sched_getaffinity() |
获取进程的处理亲和力 |
sched_yield() |
暂时让出处理器 |
Linux读书笔记第三、四章的更多相关文章
- Linux内核分析 读书笔记 (第四章)
第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间.进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有通过调度程序的合理调度,系统资源才能最大限 ...
- 05 技术内幕 T-SQL 查询读书笔记(第四章)
第四章 子查询:在外部查询内嵌套的内部查询(按照期望值的数量分为,标量子查询 scalar subqueries,多值子查询multivalued subqueries)(按照子查询对外部查询的依赖性 ...
- 20135239 益西拉姆 linux内核分析 读书笔记之第四章
chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一 ...
- linux读书笔记第三章
第3章 进程管理20 3.1 进程20 进程就是处于执行期的程序(目标码存放在某种存储介质上),但进程并不仅仅局限于一段可执行程序代码.通常进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据 ...
- 《linux程序设计》--读书笔记--第十四章信号量、共享内存和消息队列
信号量:用于管理对资源的访问: 共享内存:用于在程序之间高效的共享数据: 消息队列:在程序之间传递数据的一种简单方法: 一.信号量 临界代码:需要确保只有一个进程或者一个执行线程可以进入这个临界代码并 ...
- Linux读书笔记第一、二章
第一章 Linux内核简介 1.1Unix历史 Unix特点:1.很简洁 2.所有东西都被当成文件对待 3.Unix内核和相关的系统工具软件都是用C语言编写而成 4.进程创建非常迅速 1.2追寻 ...
- C primer plus 读书笔记第十四章
这一章主要介绍C语言的结构和其他数据形式,是学习算法和数据结构的重点. 1.示例代码 /*book.c -- 仅包含一本书的图书目录*/ #include <stdio.h> #defin ...
- 初读"Thinking in Java"读书笔记之第四章 ---控制执行流程
true和false Java不允许将数字作为布尔值使用. 所有条件表达式都将布尔值作为判断条件,决定执行路径. if-lese 迭代 while,do-while,for为三个迭代语句. ,逗号操作 ...
- jQuery 实战读书笔记之第四章:使用特性、属性和数据
使用属性 /* 每个元素都有一或多个特性,,这些特性的用途是给出相应元素或其内容的附加信息.(出自 JavaScript 高级程序设计) */ /* 特性是固有的 JavaScript 对象 属性指的 ...
随机推荐
- DevExpress09、SimpleButton、CheckButton、DropDownButton、HScrollBar控件和VScrollBar控件
SimpleButton控件 使用SimpleButton控件, 创建一个Button按钮, 可以通过其Image属性添加图片: 该控件与WinForm自带的Button按钮类似: 效果如下: Che ...
- js实现千位分隔符——str.replace()用法
/*js*/function commafy(num){ return num && num.toString().replace(/(\d{1,3})(?=(\d{3})+(?:$| ...
- 如何将XML文件导入Excel中
如下图所示为一个规范的XML文件,在Excel中可以将规范的XML文件导入到Excel成为规范的表格.具体有如下几种方法: 工具/原料 Excel 方法/步骤 单击“数据”选项卡下的“ ...
- python第三十课--异常(else讲解)
演示else语句和异常处理机制结合使用 try: print('try...') print(10/0) except: print('except...') else: print('else... ...
- mpvue 应用 Vant Weapp框架开发微信小程序
今天在使用mpvue开发微信小程序的过程中需要实现一个底部上拉选择列表的功能,因为之前做过H5微信公众号的开发,使用的就是有赞的Vant-ui,所以第一时间就想到了有赞的Vant Weapp UI框架 ...
- Docker技术入门与实战 第二版-学习笔记-5-容器-命令及限制内存与cpu资源
1.启动容器 启动容器有两种方式: 基于镜像新建一个容器并启动 将在终止状态(stopped)的容器重新启动 1)新建并启动——docker run 比如在启动ubuntu:14.04容器,并输出“H ...
- MP实战系列(十五)之执行分析插件
SQL 执行分析拦截器[ 目前只支持 MYSQL-5.6.3 以上版本 ],作用是分析 处理 DELETE UPDATE 语句, 防止小白或者恶意 delete update 全表操作! 这里我引用M ...
- Android开发——Android中常见的4种线程池(保证你能看懂并理解)
0.前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52415337 使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用 ...
- neo4j用collect 代替union 并实行分页
MATCH pa=(j:User)-[r:PostLikeRel|:ReplyRel|:RetweetRel]->(m:User{guid:"f092a1dc6c23b26b020bd ...
- rpm yum apt-get redhat centos ubuntu
rpm是由红帽公司开发的软件包管理方式,使用rpm我们可以方便的进行软件的安装.查询.卸载.升级等工作.但是rpm软件包之间的依赖性问题往往会很繁琐,尤其是软件由多个rpm包组成时.Yum(全称为 Y ...