Linux内核进程管理原理详解(代码演示)
前言:Linux内核里大部分都是C语言。建议先看《Linux内核设计与实现(Linux Kernel Development)》,Robert Love,也就是LKD。
Linux是一种动态系统,能够适应不断变化的计算需求。Linux计算需求的表现是以进程的通用抽象为中心的。进程可以是短期的(从命令行执行的一个命令),也可以是长期的(一种网络服务)。因此,对进程及其调度进行一般管理就显得极为重要。
在用户空间,进程是由进程标识符(PID)表示的。从用户的角度来看,一个 PID 是一个数字值,可惟一标识一个进程。一个 PID 在进程的整个生命期间不会更改,但 PID 可以在进程销毁后被重新使用,所以对它们进行缓存并不见得总是理想的。在用户空间,创建进程可以采用几种方式。可以 执行一个程序(这会导致新进程的创建),也可以 在程序内,调用一个 fork或 exec 系统调用。fork调用会导致创建一个子进程,而exec调用则会用新程序代替当前进程上下文。这里将对这几种方法进行讨论以便您能很好地理解它们的工作原理。
这里将按照下面的顺序展开对进程的介绍,首先展示进程的内核表示以及它们是如何 ...
Linux内核进程管理并发同步与原子操作
并发同步并发 是指在某一时间段内能够处理多个任务的能力,而 并行 是指同一时间能够处理多个任务的能力。并发和并行看起来很像,但实际上是有区别的,如下图(图片来源于网络):
上图的意思是,有两条在排队买咖啡的队列,并且只有一架咖啡机在处理,而并行就有两架的咖啡机在处理。咖啡机的数量越多,并行能力就越强。可以把上面的两条队列看成两个进程,并发就是指只有单个CPU在处理,而并行就有两个CPU在处理。为了让两个进程在单核CPU中也能得到执行,一般的做法就是让每个进程交替执行一段时间,比如让每个进程固定执行100毫秒 ,执行时间使用完后切换到其他进程执行。而并行就没有这种问题,因为有两个CPU,所以两个进程可以同时执行。如下图:
上面介绍过,并发有可能会打断当前执行的进程,然后替切换成其他进程执行。如果有两个进程同时对一个共享变量 count 进行加一操作,由于C语言的 count++ 操作会被翻译成如下指令:
123mov eax, [count]inc eaxmov [count], eax
那么在并发的情况下,有可能出现如下问题:
假设count变量初始值为0:
进程1执行完 ...
Linux内核进程管理进程优先级
前言:进程优先级实际上是系统对进程重要性的一个客观评价。根据这个评价的结果来为进程分配不同的系统资源,这个资源包括内存资源和CPU资源。为了保证“公平公正”的评价每个进程,Google工程师为此设计了一套评价系统。
为什么要有进程优先级?这似乎不用过多的解释,毕竟自从多任务操作系统诞生以来,进程执行占用cpu的能力就是一个必须要可以人为控制的事情。因为有的进程相对重要,而有的进程则没那么重要。进程优先级起作用的方式从发明以来基本没有什么变化,无论是只有一个cpu的时代,还是多核cpu时代,都是通过控制进程占用cpu时间的长短来实现的。就是说在同一个调度周期中,优先级高的进程占用的时间长些,而优先级低的进程占用的短些。从这个角度看,进程优先级其实也跟cgroup的cpu限制一样,都是一种针对cpu占用的QOS机制。我曾经一直很困惑一点,为什么已经有了优先级,还要再设计一个针对cpu的cgroup?得到的答案大概是因为,优先级这个值不能很直观的反馈出资源分配的比例吧?不过这不重要,实际上从内核目前的进程调度器cfs的角度说,同时实现cpushare方式的cgroup和优先级这两个机制 ...
Linux内核进程调度O(1)调度算法
Linux是一个支持多任务的操作系统,而多个任务之间的切换是通过 调度器 来完成,调度器 使用不同的调度算法会有不同的效果。Linux2.4版本使用的调度算法的时间复杂度为O(n),其主要原理是通过轮询所有可运行任务列表,然后挑选一个最合适的任务运行,所以其时间复杂度与可运行任务队列的长度成正比。而Linux2.6开始替换成名为 O(1)调度算法,顾名思义,其时间复杂度为O(1)。虽然在后面的版本开始使用 CFS调度算法(完全公平调度算法),但了解 O(1)调度算法 对学习Linux调度器还是有很大帮助的,所以本文主要介绍 O(1)调度算法 的原理与实现。由于在 Linux 内核中,任务和进程是相同的概念,所以在本文混用了任务和进程这两个名词。
1、O(1)调度算法原理1.1prio_array 结构O(1)调度算法 通过优先级来对任务进行分组,可分为140个优先级(0 ~ 139,数值越小优先级越高),每个优先级的任务由一个队列来维护。
prio_array 结构就是用来维护这些任务队列,如下代码:
1234567891011#define MAX_USER_RT_PRIO 1 ...
Linux内核进程述符和进程状态
一,进程程序是指储存在外部存储(如硬盘)的一个可执行文件, 而进程是指处于执行期间的程序, 进程包括 代码段(textsection) 和 数据段(data section) , 除了代码段和数据段外, 进程一般还包含打开的文件, 要处理的信号和CPU上下文等等。
二,进程描述符Linux进程使用 struct task_struct根据描述(include/linux/sched.h), 如下:
1234567891011121314struct task_struct {/** offsets of these are hardcoded elsewhere - touch with care */ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ unsigned long flags; /* per process flags, defined below */ int sigpending; mm_segment_t addr_limit; /* t ...
Linux内核进程间通信-管道
1、管道的定义
管道是第一个广泛应用的进程间通信手段。日常在终端执行shell命令时,会大量用到管道。但管道的缺陷在于只能在有亲缘关系(有共同的祖先)的进程之间使用。为了突破这个限制,后来引入了命名管道。
2、管道的用途
管道是最早出现的进程间通信的手段。在shell中执行命令,经常会将上一个命令的输出作为下一个命令的输入,由多个命令配合完成一件事情。而这就是通过管道来实现的。在图9-3中,进程who的标准输出,通过管道传递给下游的wc进程作为标准输入,从而通过相互配合完成了一件任务。
3、管道的操作
管道的作用是在具有亲缘关系的进程之间传递消息,所谓有亲缘关系,是指有同一个祖先。所以管道并不是只可以用于父子进程通信,也可以在兄弟进程之间还可以用在祖孙之间等,反正只要共同的祖先调用了pipe函数,打开的管道文件就会在fork之后,被各个后代所共享。
不过由于管道是字节流通信,没有消息边界,多个进程同时发送的字节流混在一起,则无法分辨消息,所有管道一般用于2个进程之间通信,另外管道的内容读完后不会保存,管道是单向的,一边要么读,一边要么写,不可以又读又写,想要一边读一边写,那就 ...
Linux进程、线程、调度(一)
希望可以通过本小结 彻底地搞清楚进程生命周期,进程生命周期创建、退出、停止,以及僵尸进程的本质;
进程 是处于执行期的程序以及相关的资源的总称,是操作系统资源分配的单位。
123456进程的资源到底包括什么?1.打开的文件2.挂起的信号3.内核的内部数据4.处理器的状态5.内存映射的内存地址空间 等等
Linux系统 对线程和进程并不特别区分。线程仅仅被视为一个与其他线程共享某些资源的进程。每个线程都拥有唯一自己的task_struct。
内核调度的对象是根据task_struct结构体。可以说是线程,而不是进程。
不仅仅要有资源,还需要有进程的描述,例如:pid
进程描述符及task_structlinux通过task_struct结构体描述一个进程。
mm 成员:描述内存资源fs 成员:描述文件系统资源files 成员:进程运行时打开了多少文件,fd的数组signal 成员:进程接收的信号资源
Linux通过slab分配器分配task_struct结构,只需在栈底创建新的结构,struct thread_info。每个任务的thread_info结构在它的内核栈的尾端分配。
p ...
Linux进程、线程、调度(三)
本节主要介绍进程的调度器,设计的目标:吞吐和响应,轮流让其他进程获取CPU资源。
进程调度机制的架构操作系统通过中断机制,来周期性地触发调度算法进行进程的切换。
rq: 可运行队列,每个CPU对应一个,包含自旋锁,进程数量,用于公平调度的CFS结构体,当前正在运行的进程描述符。
cfs_rq: cfs调度的运行队列信息,包含红黑色的根节点,正在运行的进程指针,用于负载均衡的叶子队列等。
sched_entity: 调度实体,包含负载权重值,对应红黑树节点,虚拟运行时vruntime等。
sched_class: 调度算法抽象成的调度类,包含一组通用的调度操作接口,将接口和实现分离。
schedule函数的流程包括:
1、关闭内核抢占,标识cpu状态。通知RCU更新状态,关闭本地终端,获取所要保护的运行队列的自旋锁,为查找可运行进程做准备。2、检查prev状态,决定是否将进程插入到运行队列,或者从运行队列中删除。3、task_on_rq_queued(prev) : 将pre进程插入到运行队列的队尾。4、pick_next_task : 选取下一个将要执行的进程。5、context ...
Linux进程、线程、调度(二)
fork 、vfork、clone
Linux 内核的调度算法,是根据task_struct结构体来进行调度的。
写时拷贝技术当p1把p2创建出来时,会把task_struct里描述的资源结构体对拷给p2。区分进程的标志,就是 p2的资源不是p1的资源。两个task_struct 的资源都相同,那就不叫两个进程了。
执行一个copy,但是任何修改都造成分裂,如:chroot,open,写memory,最难copy的是 mm 这个部分,因为要做写时拷贝。
Linux通过MMU进行虚拟地址到物理地址的转换,当进程执行fork()后,会把页表中的权限设置为RD-ONLY,当P1,P2去写该页时,CPU会收到page fault,申请新的内存。Linux再将页表中的virt1指向新的物理地址。
1234567891011121314151617181920212223242526#include <sched.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>int data = ...
Linux进程、线程、调度(四)
延续(三)中,调度器的其他内容:关于多核、分群、硬实时
多核下的负载均衡Linux 每个CPU可能有多个操作线程,每个核均运行的调度算法是 SCHED_FIFO, SCHED_RR,SCHED_NORMAL(CFS)等,每个核都“以劳动为乐”。
tips: 旧的调度算法是通过+/- 5 nice值,来照顾IO型,惩罚CPU型。新的进程调度算法CFS,会根据ptime/nice值进行红黑树匹配。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950#include <stdio.h>#include <pthread.h>#include <sys/types.h>void *thread_fun(void *param){ printf("thread pid:%d, tid:%lu\n", getpid(), pthread_self()); while (1 ...