Linux-进程概念

Posted The August

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux-进程概念相关的知识,希望对你有一定的参考价值。

冯诺依曼体系结构

生活中大部分的计算机,服务器都遵守冯诺依曼体系。

  • 目前所认识的计算机,都是有一个个的硬件组件组成
  • 输入单元:包括键盘, 鼠标,扫描仪, 写板等
  • 中央处理器(CPU):含有运算器和控制器等
  • 输出单元:显示器,打印机等
  • 冯诺依曼体系结构:
    1. 这里的存储器指的是内存
    1. 不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
    1. 外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
    1. 所有设备都只能直接和内存打交道。

注:

  • 输入设备:键盘、网卡、磁盘、话筒……
  • CPU:运算器和控制器
  • 输出设备:显示器、网卡、磁盘、音响……
  • 输出设备和输入设备统称为外设
  • 存储器:CPU和所有外设的缓存
  • 冯诺依曼规定了硬件层面上的数据流向
  • 可执行程序运行时必须先加载到内存(冯诺依曼规定)
  • 在数据层面:CPU并不和外设打交道,外设只和内存打交道
  • QQ中传递文件:输入:磁盘、输出:网卡 、输入:网卡、输出:磁盘
  • QQ中聊天:输入:键盘、输出:网卡 、输入:网卡、输出:显示器

操作系统(Operator System)

任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。

操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(例如函数库,shell程序等等)

操作系统是进行软硬件资源管理的软件

操作系统:

  • 可以减少用户使用计算机的成本
  • 对下管理好所有的软硬件,对上给用户提供一个稳定高效的运行环境(软件:进程管理、文件管理、驱动管理…… 硬件:磁盘、网卡、显卡、内存……)
  • 在整个计算机软硬件架构中,操作系统的定位是:一款纯正的“搞管理”的软件


注:

  • 硬件部分遵守冯诺依曼体系
  • OS不信任任何用户,任何对系统硬件或者软件访问,都必须通过OS的手
  • 计算机体系是一个层状结构,任何访问硬件或者软件的行为,都必须通过OS接口,贯穿OS进行访问
  • 库函数:语言或者第三方库(第一方:系统的、第二方:自己的,其余是第三方的)给我们提供的接口
  • 系统调用:OS提供的接口
    总结:
  • 计算机管理硬件:
    1. 描述起来,用struct结构体
    1. 组织起来,用链表或其他高效的数据结构
  • 操作系统是进行软硬件资源管理的软件(其中管理的本质是先描述在组织(是对数据的管理)
  • 管理分为三种:管理者、执行者、被管理者(eg管理者为OS、执行者为驱动程序、被管理者为底层硬件)

补:

  • 系统调用:
    1. 系统调用把应用程序的请求传输给系统内核执行
    1. 系统调用函数的执行过程应该是由用户态变为内核态(又称系统态)
    1. 利用系统调用能够得到操作系统提供的多种服务
    1. 是操作系统提供给编程人员的接口
    1. 系统调用给用户屏蔽了设备访问的细节
    1. 系统调用保护了一些只能在内核模式执行的操作指令
  • read是系统调用不是库函数

进程

描述进程-PCB

基本概念
课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。

描述进程-PCB

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。称之为PCB(process control block),Linux操作系统下的PCB是: task_struct 。task_struct是PCB的一种
  • 在Linux中描述进程的结构体叫做task_struct。
  • task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

task_ struct内容分类

  • 标示符: 描述本进程的唯一标示符,用来区别其他进程。
  • 状态: 任务状态,退出代码,退出信号等。
  • 优先级: 相对于其他进程的优先级。
  • 程序计数器: 程序中即将被执行的下一条指令的地址。
  • 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
  • 上下文数据: 进程执行时处理器的寄存器中的数据
  • I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  • 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 其他信息

注:

  • 在编程语言中:顺序语句、判断语句、循环语句
  • CPU中有一种寄存器叫pc指针(也称EIP),它是用来记录正在执行指令的下一条指令的地址
  • CPU核心工作流程:
    1. 取指令
    1. 分析指令
    1. 执行指令
  • CPU中运行的代码都是进程的代码
  • 当一个进程在运行中,因为某些原因,需要被暂时停止执行,让出CPU,需要进程保存(保存的目的是为了恢复)自己的所有的临时数据(最重要的是进程的上下文数据
  • 在每个CPU中都有一个运行队列,其中运行队列中的进程都是处在运行状态的(CPU是选择性的调度)

总结:

  • OS可以一次跑起多个程序,并且OS要管理起来这些运行起来的程序,OS要对进程进行管理
  • 进程控制块(PCB):struct task_struct 结构体
  • OS对进程的管理转化成为了对进程信息的管理,先描述再组织,对进程的管理转化为对双链表的增删查改
  • 进程=你的程序+内核申请的数据结构(PCB)
  • 优先级的本质是在资源(CPU、网卡、显卡、磁盘……)有限的前提下,确立谁先访问资源,谁后访问的问题

补:

  • 进程放在CPU上之后,不是一直在运行直到进程运行结束,每个进程都有一个运行时间单位——时间片
  • 一般进程让出CPU:一种是来了一个优先级更高的进程(OS必须支持抢占);另一种是时间片到了
  • 单CPU,单核:跑起来多个进程,通过进程快速切换的方式,在一段时间内,让所有的进程代码都得到推进——并发
  • 多CPU,多核:任何时刻,允许多个进程同时执行——并行
  • 进程在CPU上运行时,会有很多寄存器上的临时数据——上下文数据
  • 系统感知进程的唯一实体是PBC(进程控制块)

查看进程



通过系统调用创建进程-fork

语法:

1. 创建子进程

理解fork:

  • 程序员角度:
    1. 父子共享用户代码是只读的不可修改并且不可写入),而用户数据各自私有一份(目的是不让进程间相互干扰)(数据用的是写时拷贝)
    1. 在操作系统中,所有进程具有独立性的
    1. 进程具有独立性是操作系统所表现出来的,OS通过父子进程的用户数据各自私有一份,从而不让进程互相干扰来实现的
  • 从内核角度:
    1. 进程=我的程序+内核数据结构(PCB task_struct)
    1. 创建子进程,通常以父进程为模板其中子进程默认使用的是父进程的代码和数据(写时拷贝)

2. fork有两个返回值


注:

  • fork有两个返回值的原因是在创建子进程成功之后,子进程和父进程共享代码
  • fork:子进程的返回值是0,父进程返回值是子进程的pid,因为子进程只有一个父进程,而父进程有多个子进程,需要对每个子进程进行标识(pid),并且记住他们
  • fork中父进程的返回值是不会记录到用户数据当中的,这些只是给用户看的,便于记录子进程(pid)

总结:

  • 运行 man fork 认识fork
  • fork有两个返回值
  • 父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
  • fork 之后通常要用 if 进行分流

进程状态

Linux下的内核源代码

  • R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。

  • S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠

  • D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。

  • T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。

  • X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

  • Z(zombie)-僵尸进程:

  1. 僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程(使用wait()系统调用)没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  2. 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
  3. 只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态

R状态

S状态

T状态

Z状态


注:

  • 只要进程正常结束,就是正常死亡(X状态不太好演示)
  • D状态在云服务器上不容易实现
  • 进程是R状态,不一定是在CPU上运行,进程在运行队列中,就是R状态(进程已准备好,等待调度)
  • S状态为浅度休眠(对外部事件可以做出反应),大部分情况下都是这种状态
  • D状态为深度休眠(不可以被杀掉,即便是操作系统,只能等待D状态进程自动醒来,或者是关机重启(可能被卡死))
  • S状态和D状态称为等待状态
  • 进程退出,一般不是立刻让OS回收信息,释放进程的所有资源
  • 进程创建的目的是为了将自己退出时的相关信息,写入进程的PCB中,供OS或者父进程来进行读取,读取成功后,该进程才算真正死亡
  • 进程退出时,当OS或者父进程来未读取到子进程的信息时,这是处于Z状态

状态与状态+的区别
eg

补:
kill命令
kill 命令用于删除执行中的程序或工作
语法:kill [-s <信息名称或编号>][程序] 或 kill [-l <信息编号>]

查看进程状态

第一种:

第二种:

ps aux / ps axj 命令



补:D状态和Z状态用kill命令是杀不掉的

僵尸进程危害

  • 进程的退出状态必须被维持下去,因为OS关心进程是否完成了任务。可父进程如果一直不读取,那子进程就一直处于Z状态
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护
  • 一个父进程创建了很多子进程,就是不回收,会造成内存资源的浪费。因为数据结构对象本身就要占用内存。(在C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!)这样就会存在内存泄漏。

孤儿进程

父进程先退出,子进程就称之为“孤儿进程”


注:

  • 孤儿进程被1号systemd进程领养,当然要有systemd进程回收
  • 在centos7.6中1号进程是systemd,而在centos6.5中的1号进程是initd

进程优先级

进程的优先级:

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  • 优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
  • 还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能

注:

  • 优先级是在一定能得到某种资源,只是先后的问题
  • 权限是决定你能还是不能得到某种资源
  • 优先级是得到某种资源(CPU)的先后顺序,其本质是因为资源有限(CPU)

查看系统进程

  • UID : 代表执行者的身份
  • PID : 代表这个进程的代号
  • PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
  • PRI :代表这个进程可被执行的优先级,其值越小越早被执行
  • NI :代表这个进程的nice值
  • TTY:运行该程序时的终端设备
  • CMD:命令

注:

  • Linux的优先级由pri和nice值共同确定(优先级的数值越小,优先级越高;优先级的数值越大,优先级越低)
  • nice值就是优先级的修正数据,范围是[-20,19]
  • 优先级不可能一味的高,也不可能一味的低(操作系统的调度器要适度地考虑平衡问题,避免“饥饿问题”)

PRI and NI

  • PRI是进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小
    进程的优先级别越高
  • NI就是nice值了,其表示进程可被执行的优先级的修正数值
  • PRI值越小越快被执行,PRI(new)=PRI(old)+nice
  • 当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行
  • 调整进程优先级,在Linux下,就是调整进程nice值
  • nice其取值范围是-20至19,一共40个级别。
  • 进程的nice值不是进程的优先级,但是进程nice值会影响到进程的优先级变化。
  • nice值是进程优先级的修正修正数据

注:

  • nice的值的范围[-20,19],是一种可控范围,原则上OS内的调度器,要公平(不是平均)且较高效的调度
  • PRI(new)=PRI(old)+nice中的PRI(old)默认是80,因为有一个基准值,便于调整;在设计上实现起来比较方便
  • 在Linux系统中,标识一个用户,并不是通过用户名标识的(是给用户看的),而是通过用户的uid(计算机比较善于处理数据)

查看进程优先级的命令

用top命令更改已存在进程的nice:

  • top
  • 进入top后按“r”–>输入进程PID–>输入nice值

注:

  • 用top命令更改已存在进程的nice,如果输入的值大于19,NI的值就是19;如果输入的值小于-20,NI的值就是-20。
  • 无论怎么输入NI的值,最终PRI的值为[60,99]

补充概念

  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级
  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发

注:

  • 进程与资源之间,进程永远是多数的
  • 竞争性和独立性是进程运行的特殊属性(或原则);并行和并发是计算机的调度特性

环境变量与命令行参数

Linux下的环境变量与命令行参数

进程地址空间(灵魂四问)

进程地址空间的分布


注:

  • 进程地址空间不是内存地址空间
  • 进程地址空间,会在进程的整个生命周期内一直存在,直到进程退出

什么是进程地址空间

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int g_val = 0;
int main()

	pid_t id = fork();
 	if(id < 0)
 	
		perror("fork");
 		return 0;
 	
 	else if(id == 0)
 	 
 		//child,子进程肯定先跑完,也就是子进程先修改,完成之后,父进程再读取
 		g_val=100;
 		printf("child[%d]: %d : %p\\n", getpid(), g_val, &g_val);
 	
 	else
 	 
 		//parent
 		sleep(3);
		printf("parent[%d]: %d : %p\\n", getpid(), g_val, &g_val);
 	
 	sleep(1);
 	return 0;


运行结果:

child[3046]: 100 : 0x80497e8
parent[3045]: 0 : 0x80497e8

父子进程中的g_val的地址竟然是一样的

注:

  • 任何的编程语言里面的地址,绝对不是物理地址,而是虚拟地址(C++/C语言中的&得到的是虚拟地址不是物理地址)
  • 虚拟地址是操作系统提供的,数据和代码一定在物理内存上(冯诺依曼规定),因此需要将虚拟内存转化成物理内存(由OS自动完成)
  • 父子进程代码共享,而数据是各自私有一份的(写时拷贝)
  • 当所有程序运行起来之后,该程序立即变成进程

总结:

  • 地址空间本质是进程看待内存的方式,是抽象出来的一个概念,内核struct mm_struct,这样的每一个进程,都认为自己独占系统内存资源
  • 区域划分本质:将线性地址空间划分成为一个一个的area,[start,end]
  • 虚拟地址本质:在[start,end] 之间的各个地址叫做虚拟地址

为什么要存在地址空间

第一种情况:

  • 如果进程直接访问物理内存,那么所看到的地址就是物理地址,如果进行指针越界访问,那么进程间的独立性就无法保证。
  • 因为物理内存一暴露,其中就有可能有恶意程序直接通过物理地址进行内存数据篡改,并且可以读取里面的内容

第二种情况:

  • 当一个程序变为进程时,需要将可执行程序加载到内存中;当进程退出时,也需要内存知道,因此内存管理需要知道进程是运行还是终断、退出
  • 因此内存管理模块和进程模块是强耦合的
  • 内存管理只需知道哪些内存区域(page)是无效的,哪些区域是有有效的,只需将内存管理和进程管理进行解耦

第三种情况:

  • 磁盘在存储二进制可执行程序时,是进行一个个区域划分的(代码区、全局数据区、只读数据区)这些区域是以4KB为单位划分的(页帧),而物理内存是没有按照磁盘划分顺序划分的(按照页(一页是4KB)为单位划分的)
  • 如果进程想要执行代码,OS需要在物理内存中毫无章法的乱找(除非OS开辟一块连续的物理内存(不现实)),这时,虚拟地址空间是几乎是按照磁盘区域划分的,它可以经过页表的映射来找到代码的位置并顺利执行代码

注:

  • 两个不同的进程虚拟地址可以完全一样

  • 页表:

    1. 页表是完成虚拟地址到物理地址的映射
    1. 将虚拟地址到物理地址的转化
    1. 同时也可以帮系统进行合法性检测
  • 内存管理模块和进程管理模块是强耦合的

  • 操作系统:进程管理、文件管理、内存管理、驱动管理

  • 可执行程序,其本身就已经划分成为一个个的区域(.code、.data、.bss、.readonly……),这样划分便于程序链接,而代码在用编译器编译时是将数据和代码放在一起

  • 页框:页框数=物理内存大小/每一个分配内存的大小(4KB),页框以4KB位单位

  • 页帧:在磁盘一个个程序被划分成4KB大小的数据

  • 页号:页表项的序号

  • 执行顺序语句的过程:当前语句的起始地址+当前代码的长度(虚拟地址存放代码是存放在连续区域的)

  • 虚拟地址空间不存在存放地址的功能,因此需要用户必须保存起来(避免内存泄露)

  • 进程与程序的区别:

    1. 程序是静态的,进程是动态的,程序是存储在某种介质上的二进制代码,进程对应了程序的执行过程,系统不需要为一个不执行的程序创建进程,一旦进程被创建,就处于不断变化的动态过程中,对应了一个不断变化的上下文环境。
    1. 程序是永久的,进程是暂时存在的。程序的永久性是相对于进程而言的,只要不去删除它,它可以永久的存储在介质当中。

总结:

  1. 保护物理内存,不受到任何进程内的地址直接访问,方便进行合法性校验
  2. 将内存管理和进程管理进行解耦
  3. 让每个进程,以同样的方式来看待代码和数据

地址空间与物理内存之间的关系

虚拟地址和物理地址之间是通过页表完成的映射关系

回答上面代码(为什么地址一样?)的问题

  • 变量内容不一样,所以父子进程输出的变量绝对不是同一个变量
  • 但地址值是一样的,说明,该地址绝对不是物理地址
  • 在Linux地址下,这种地址叫做 虚拟地址
  • 我们在用C/C++语言所看到的地址,全部都是虚拟地址!物理地址,用户一概看不到,由OS统一管理,OS必须负责将 虚拟地址 转化成 物理地址
  • 同一个变量,地址相同,其实是虚拟地址相同(原因是fork出一个子进程,其中子进程会继承大部分父进程的信息(代码指向同一块空间,数据也指向同一块空间),因此g_val的地址也被继承了,但是当子进程试图修改g_val值时,这是需要用到写时拷贝将父进程的数据拷贝一份给子进程(开辟新的空间)并修改了子进程中的g_val的值),因此内容不同其实是被映射到了
    不同的物理地址!

补:

  • TCB: 线程控制块

  • MMU:内存管理单元,一种负责处理中央处理器(CPU)的内存访问请求,功能包括虚拟地址到物理地址的转换(即 虚拟内存管理)、内存保护、中央处理器高速缓存的控制

  • CACHE: 高速缓存

  • DMA: 直接内存存取

以上是关于Linux-进程概念的主要内容,如果未能解决你的问题,请参考以下文章

Linux从青铜到王者第六篇:Linux进程概念第二篇

Linux进程概念篇

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

Linux——进程概念进程创建僵尸进程孤儿进程环境变量程序地址空间详解

:Linux进程概念