[转帖]Linux的进程线程及调度
Linux的进程线程及调度
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10393707.html
本文为宋宝华《Linux的进程、线程以及调度》学习笔记。
1 进程概念
1.1 进程与线程的定义
操作系统中的经典定义:
进程:资源分配单位。
线程:调度单位。
操作系统中用PCB(Process Control Block, 进程控制块)来描述进程。Linux中的PCB是task_struct结构体。
1.2 进程生命周期
1.2.1 进程状态
R, TASK_RUNNING:就绪态或者运行态,进程就绪可以运行,但是不一定正在占有CPU
S, TASK_INTERRUPTIBLE:浅度睡眠,等待资源,可以响应信号,一般是进程主动sleep进入的状态
D, TASK_UNINTERRUPTIBLE:深度睡眠,等待资源,不响应信号,典型场景是进程获取信号量阻塞
Z, TASK_ZOMBIE:僵尸态,进程已退出或者结束,但是父进程还不知道,没有回收时的状态
T, TASK_STOPED:停止,调试状态,收到SIGSTOP信号进程挂起
1.2.2 进程创建与消亡相关API
1) system()
通过调用shell启动一个新进程
2) exec()
以替换当前进程映像的方式启动一个新进程
3) fork()
以复制当前进程映像的方式启动一个新进程,子进程中fork()返回0,父进程fork()返回为子进程ID。
4) wait()
父进程挂起,等待子进程结束。
5) 孤儿进程与僵尸进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。孤儿进程不会浪费资源。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。僵尸进程浪费系统资源(进程描述符task_struct存在,进程占用的资源被回收,不存在内存泄漏,实际上基本不浪费系统资源,参宋宝华的课程)。
避免僵尸进程:
僵尸进程产生原因:
1、子进程结束后向父进程发出SIGCHLD信号,父进程默认忽略了它;
2、父进程没有调用wait()或waitpid()函数来等待子进程的结束。
避免僵尸进程方法:
1、父进程调用wait()或者waitpid()等待子进程结束,这样处理父进程一般会阻塞在wait处而不能处理其他事情。
2、捕捉SIGCHLD信号,并在信号处理函数里面调用wait函数,这样处理可避免1中描述的问题。
3、fork两次,父进程创建儿子进程,儿子进程再创建一个孙子进程,然后儿子进程自杀,孙子进程成为孤儿进程被init进程收养。
1.3 进程间通信
1) 信号
信号这里指的是事件。比如按CTRL-C组合键会发送SIGINT信号,进程里可以捕捉到这个信号进行相应处理。
2) 管道PIPE
一切皆文件,管道的操作也是类似文件的操作。
popen()函数类似于fopen()函数,返回的是对象指针。
pipe()函数类似于open()函数,返回的是对象描述符。
管道是在亲属进程(同一父进程创建出的进程)之间进行数据传输的。
3) 命名管道FIFO
命名管道可用于在无亲属关系之前是进程间通信。
mkfifo()/mknod()将在文件系统中创建一个有路径和名称的文件。把这个管道文件当作普通文件用就行了,就可以实现进程间通信。
4) 信号量
信号量、消息队列、共享内存是System V IPC机制。
临界区:任何时刻只能有一个进程进行独占式访问的代码区。
信号量:大部分进程间通信只需要二进制信号号,因此这里只讨论二进制信号量。进入临界区前,执行P操作(若信号量大于1则减1并进入临界区,否则挂起本进程);退出临界区时,执行V操作(若有进程在等待挂起则唤醒之,否则信号量加1)。
互斥量:互斥信号量是二进制信号量的一个子集。
5) 消息队列
与命令管道类似,但不必考虑打开/关闭管道的复杂操作。消息队列独立于进程而存在。
6) 共享内存
需要通信的进程间共享一块内存进行数据交换。
2 进程线程的实现本质
Linux调度器实际是识别task_struct进行调度。
无论进程线程,底层都对应一个task_struct,进程和线程的区别是共享资源的多少,两个进程间完全不共享资源,两个线程间共享所有资源。
2.1 fork()
执行fork后,父进程的task_struck对拷给子进程,父子进程最初资源完全一样,但是两份不同拷贝,因此任何改动都造成二者的分裂。
父子进程对内存资源(mm)的管理使用了COW(Copy-On-Write, 写时拷贝)技术:
1) 在fork之前,一片内存区对应一份物理地址和一份虚拟地址,内存区的权限的RW;
2) 在fork之后,父子进程看到的内存区虚拟地址相同,物理地址也相同,父子进程使用的其实是同一片物理内存,未发生内存拷贝,操作系统会将此内存区权限改为RO;
3) 父或子进程对内存区执行写操作将触发PageFault,操作系统此时会将内存区拷贝一份,父子进程看到的虚拟地址仍然一样,但是物理地址已经不则。各进程虚拟地址到物理地址的映射由MMU(Memory Management Unit, 内存管理单元)管理。
fork运行在有MMU的CPU上。
2.2 vfork()

对于无MMU的CPU,无法应用COW,无法支持fork。
无MMU的CPU使用vfork创建进程,父进程将一直阻塞直到子进程exit或exec。
vfork和fork的本质区别是,vfork中的父子进程共用同一片内存区。
2.3 pthread_create()

Linux线程本质上就是进程,只是线程间共享所有资源。如上图所示。
每个线程都有自己的task_struct,因为每个线程可被CPU调度。多线程间又共享同一进程资源。这两点刚好满足线程的定义。
Linux就是这样用进程实现了线程,所以线程又称为轻量级进程。
2.4 PID和TGID

POSIX要求,同一进程的多个线程获取进程ID是得到的是唯一ID值。
Linux同一进程的多线程,在内核视角实际上每个线程都有一个PID,但在用户空间需要getpid返回唯一值,Linux使用了一个小技巧,引入了TGID的概念,getpid()返回的的TGID值。
进程视角的top命令:
不带参数的top命令(默认情况),显示的是进程对单核CPU的利用率,例如,一个进程内有三个线程,主线程创建了线程1和线程2,线程1和线程2都调用一个while(1),则对双核CPU而言,线程1和线程2各用一个核,占用率都是100%,则top命令看到的进程CPU利用率是200%,进程ID是主线程的PID(也是TGID)。
线程视角的top命令:top –H命令从线程视角显示CPU占用率,上例中,将会显示,线程1占用率100%,线程2占用率100%。
说线程的PID,是指用户空间的进程ID,值就是TGID;当特别指出,线程在内核空间的PID,则指线程在内核中task_struct里特有的PID。
3 进程调度

3.1 实时进程调度
SCHED_FIFO:不同优先级按照优先级高的先跑到睡眠,优先级低的再跑;同等优先级先进先出。
SCHED_RR:不同优先级按照优先级高的先跑到睡眠,优先级低的再跑;同等优先级轮转。
内核RT补丁:
/proc/sys/kernel/sched_rt_period_us
/proc/sys/kernel/sched_rt_runtime_us
在period的时间里RT最多只能跑runtime的时间
3.2 普通进程调度
SCHED_OTHER:
3.2.1 动态优先级(早期2.6)
进程有IO消耗性和CPU消耗型两种衡量参数。
优先级高的意味着:1) 得到更多的时间片,2) 醒时能抢占优先级低的。时间片轮转。
内核存储静态优先级,用户可通过nice来修改静态优先级。
进程的动态优先级则是根据静态优先级实时计算出来的,调度算法奖励IO消耗性(调高优先级增加实时性)、处罚CPU消耗型(调低优先级减小实时性)
3.2.2 CFS:完全公平调度(新内核)
红黑树,左边节点小于右边节点的值
运行到目前为止vruntime最小的进程
同时考虑了CPU/IO和nice
总是找vruntime最小的线程调度。
vruntime = pruntime/weight × 1024;
vruntime是虚拟运行时间,pruntime是物理运行时间,weight权重由nice值决定(nice越低权重越高),则运行时间少、nice值低的的线程vruntime小,将得到优先调度。这是一个随运行而动态变化的过程。
工具chrt和renice:
1 2 3 4 5设置SCHED_FIFO和50 RT优先级 # chrt -f -a -p 50 10576 设置nice # renice -n -5 -g 9394 # nice -n 5 ./a.out
4 多核负载均衡
略
5 参考资料
[1] 宋宝华,Linux进程、线程和调度
[2] https://blog.csdn.net/sdkdlwk/article/details/65938204
[3] https://blog.csdn.net/kklvsports/article/details/52268085
[4] https://blog.csdn.net/qq_20218109/article/details/52078076
[5] https://www.cnblogs.com/yuxingfirst/p/3165407.html
[转帖]Linux的进程线程及调度的更多相关文章
- Linux的进程线程及调度
本文为宋宝华<Linux的进程.线程以及调度>学习笔记. 1 进程概念 1.1 进程与线程的定义 操作系统中的经典定义: 进程:资源分配单位. 线程:调度单位. 操作系统中用PCB(Pro ...
- Linux查看进程线程个数
1.根据进程号进行查询: # pstree -p 进程号 # top -Hp 进程号 2.根据进程名字进行查询: # pstree -p `ps -e | grep server | awk '{pr ...
- Linux下进程线程,Nignx与php-fpm的进程线程方式
1.进程与线程区别 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流, ...
- Linux内核——进程管理与调度
进程的管理与调度 进程管理 进程描写叙述符及任务结构 进程存放在叫做任务队列(tasklist)的双向循环链表中.链表中的每一项包括一个详细进程的全部信息,类型为task_struct,称为进程描写叙 ...
- Linux的进程/线程间通信方式总结
Linux系统中的进程间通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程 ...
- Linux的进程/线程通信方式总结(转)
Linux系统中的进程通信方式主要以下几种: 同一主机上的进程通信方式 * UNIX进程间通信方式: 包括管道(PIPE), 有名管道(FIFO), 和信号(Signal) * System V进程通 ...
- [转载]了解Linux的进程与线程
本文转自Tim Yang的博客http://timyang.net/linux/linux-process/ .对于理解Linux的进程与线程非常有帮助.支持原创.尊重原创,分享知识! 上周碰到部署在 ...
- linux常见进程与内核线程
发现大量jdb2进程占用io资源.jdb2进程是一个文件系统的写journal的进程 kthreadd:这种内核线程只有一个,它的作用是管理调度其它的内核线程.它在内核初始化的时候被创建,会循环运行一 ...
- Linux下进程的创建过程分析(_do_fork do_fork详解)--Linux进程的管理与调度(八)
Unix标准的复制进程的系统调用时fork(即分叉),但是Linux,BSD等操作系统并不止实现这一个,确切的说linux实现了三个,fork,vfork,clone(确切说vfork创造出来的是轻量 ...
随机推荐
- 使用intellij idea搭建spring-springmvc-mybatis整合框架环境
使用intellij idea搭建spring-springmvc-mybatis整合框架环境 1.打开idea,创建maven项目,File-New-Project 2.选择Maven,勾选Cr ...
- Actor模型和CSP模型的区别
引用至:http://www.jdon.com/concurrent/actor-csp.html Akka/Erlang的actor模型与Go语言的协程Goroutine与通道Channel代表的C ...
- mysql数据库的test类型
文章参考自 window系统参考:http://blog.sina.com.cn/s/blog_46f7bb6d0102vde3.html linux 参考:http://www.linuxeye.c ...
- OpenCV——轮廓填充drawContours函数解析
函数的调用形式 void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const ...
- Python脱产8期 Day02
一 语言分类 机器语言,汇编语言,高级语言(编译和解释) 二 环境变量 1.配置环境变量不是必须的2.配置环境变量的目的:为终端提供执行环境 三Python代码执行的方式 1交互式:.控制台直接编写运 ...
- 错误:“Manifest merger failed with multiple errors, see logs”
今天用Android Studio打开以前写个的项目后,出现如下错误:Manifest merger failed with multiple errors, see logs 现象是: 遇到这个问 ...
- 02-Centos7安装部署Mirrorgate
1.以Docker方式运行 MirrorGate服务器作为docker镜像提供,因此要运行它只需在终端中执行以下命令: 注意mongo镜像要使用3.6版本,其他版本会提示版本问题. #Spinup m ...
- ES6入门之let、cont
一.前提 解决ES5中只有全局作用域和函数作用域,没有块级作用域而带来的不合理的场景. let 基本用法 用法和var 一样,只是let声明的变量只有在let命令所在的代码块有效 { let a = ...
- R实战 第十篇:列联表和频数表
列联表是观测数据按两个或更多属性(定性变量)分类时所列出的频数分布表,它是由两个以上的变量进行交叉分类的频数分布表.交互分类的目的是将两变量分组,然后比较各组的分布状况,以寻找变量间的关系. 按两个变 ...
- [UWP 自定义控件]了解模板化控件(10):原则与技巧
1. 原则 推荐以符合以下原则的方式编写模板化控件: 选择合适的父类:选择合适的父类可以节省大量的工作,从UWP自带的控件中选择父类是最安全的做法,通常的选择是Control.ContentContr ...