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: 读取可执行文件,将其载入到内存中运行

创建的流程:

  1. 调用dup_task_struct()为新进程分配内核栈,task_struct等,其中的内容与父进程相同。
  2. check新进程(进程数目是否超出上限等)
  3. 清理新进程的信息(比如PID置0等),使之与父进程区别开。
  4. 新进程状态置为 TASK_UNINTERRUPTIBLE
  5. 更新task_struct的flags成员。
  6. 调用alloc_pid()为新进程分配一个有效的PID
  7. 根据clone()的参数标志,拷贝或共享相应的信息
  8. 做一些扫尾工作并返回新进程指针

创建进程的fork()函数实际上最终是调用clone()函数。

创建线程和进程的步骤一样,只是最终传给clone()函数的参数不同。

比如,通过一个普通的fork来创建进程,相当于:clone(SIGCHLD, 0)

创建一个和父进程共享地址空间,文件系统资源,文件描述符和信号处理程序的进程,即一个线程:clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0)

在内核中创建的内核线程与普通的进程之间还有个主要区别在于:内核线程没有独立的地址空间,它们只能在内核空间运行。

这与之前提到的Linux内核是个单内核有关。

4. 进程的终止

和创建进程一样,终结一个进程同样有很多步骤:

子进程上的操作(do_exit)

  1. 设置task_struct中的标识成员设置为PF_EXITING
  2. 调用del_timer_sync()删除内核定时器, 确保没有定时器在排队和运行
  3. 调用exit_mm()释放进程占用的mm_struct
  4. 调用sem__exit(),使进程离开等待IPC信号的队列
  5. 调用exit_files()和exit_fs(),释放进程占用的文件描述符和文件系统资源
  6. 把task_struct的exit_code设置为进程的返回值
  7. 调用exit_notify()向父进程发送信号,并把自己的状态设为EXIT_ZOMBIE
  8. 切换到新进程继续执行

子进程进入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. 什么是调度

现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计算机上同时运行的各个任务(也就是进程)。

这个管理程序就是调度程序,它的功能说起来很简单:

  1. 决定哪些进程运行,哪些进程等待
  2. 决定每个进程运行多长时间

此外,为了获得更好的用户体验,运行中的进程还可以立即被其他更紧急的进程打断。

总之,调度是一个平衡的过程。一方面,它要保证各个运行的进程能够最大限度的使用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()


暂时让出处理器

时间: 2024-11-08 17:38:25

Linux读书笔记第三、四章的相关文章

【读书笔记】第四章 瞬时响应:网站的高性能架构

第四章 瞬时响应:网站的高性能架构 4.1 网站性能测试 4.1.1 不同视角下的网站性能 1.用户角度:网站响应速度快还是慢2.开发人员:关注系统本身及其子系统的性能,响应时间,吞吐量,并发能力,稳定性等指标.3.运维人员:关注基础设施和资源利用率,比如贷款能力,服务器配置,数据中心网络架构等. 4.1.2 性能测试指标 1.响应时间 下表是一些常用的系统操作所需要的响应时间 2.并发数:系统能够同时处理的请求数目3.吞吐量:单位时间内,系统处理的请求数量(注意与并发数区分).TPS(每秒事务

linux读书笔记第三章

第3章 进程管理20 3.1 进程20 进程就是处于执行期的程序(目标码存放在某种存储介质上),但进程并不仅仅局限于一段可执行程序代码.通常进程还要包含其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程.当然还包括用来存放全局变量的数据段等,实际上,进程就是正在执行的程序代码的实时结果,内核需要有效而又透明地管理所有细节. 执行线程,简称线程,是在进程中活动的对象,每个线程都拥有一个独立的程序计数器.进程栈和一组进程寄存器,内核

20135239 益西拉姆 linux内核分析 读书笔记之第四章

chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一步骤称为让步) - 抢占式多任务: - Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进程在被抢占之前能够运行的时间是预先设置好的(也就是进程的时间片) 4.2 linux的进程调度 O(1)调度器:对大服务器的工作负载很理想,但是缺少交互进程. 反转楼梯最后期限调度算法

C primer plus 读书笔记第十四章

这一章主要介绍C语言的结构和其他数据形式,是学习算法和数据结构的重点. 1.示例代码 /*book.c -- 仅包含一本书的图书目录*/ #include <stdio.h> #define MAXTITL 41 #define MAXAUTL 31 struct book { /* data */ char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book library; /

Android深度探索(卷1)HAL与驱动开发--读书笔记(第四章)

l  配置Android源代码下载环境步骤 创建一个用于存放下载脚本文件(repo)的目录 #mkdir ~/bin #PATH=~/bin/repo 下载repo脚本文件 #curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo >~bin/repo #chmod a+x~/bin/repo 创建用于存放Android源代码目录 #mkdir android_source #cd android_source 初始化 #repo

算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张 一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护. 14.1 动态顺序统计 顺序统计树(order-static tree) : 在红黑树的基础上, 在每个

并发编程的艺术读书笔记-第四章线程基础

线程6种状态 1)new:初始状态,线程被构建,但还没有调用start()方法. 2)runnable:运行状态,java中将系统的运行状态和就绪状态统称为运行状态 3)blocked:阻塞状态,表示线程阻塞于锁 (synchronized) 4)waiting:等待状态,进入该状态表示该线程需要等待其它线程作出一些动作 (中断或者通知 , object.wait()) 5)TIME_WAITING:超时等待状态,该状态不同于waiting,他是可以在指定时间自动返回的  sleep导致 6)t

Linux内核设计与实现读书笔记——第三章

Linux内核设计与实现读书笔记——第三章 进程管理 20135111李光豫 3.1进程 1.进程即处于执行期的程序,并不局限于一个可执行的代码,是处于执行期程序以及其相关资源的总称. 2.Linux系统中,对于进程和线程并没有明显的区分,线程是一种特殊的进程. 3.Linux系统中,常用fork()进程创建子进程.调用fork()进程的成之为其子进程的父进程. 4.fork()继承实际上由clone()系统调用实现.最后通过exit()退出执行. 3.2任务描述符及任务结构 1.任务队列实质上

事件驱动编程——《Unix/Linux编程实践教程》读书笔记(第7章)

1.curses库 /* 基本curses函数 */ initscr(); // 初始化curses库和tty endwin(); // 关闭curses并重置tty refresh(); // 使屏幕按照你的意图显示 move(r, c); // 移动光标到屏幕的(r, c)位置 addstr(s); // 在当前位置画字符串s addch(c); // 在当前位置画字符c clear(); // 清屏 standout(); // 启动standout模式(一般使屏幕反色) standend