进程的概念

1.计算机的层状结构

计算机是层状结构,操作系统是一款负责管理软硬件资源的软件,对下管理好各种软硬件资源,这是手段,目的是对上提供良好(稳定、快速、安全)的运行环境;

先来看硬件,

冯诺依曼体系结构

从效率的角度考虑,输入/出设备速度是ms级的,存储器(一般为内存)速度是us级的,CPU(中央处理器)内部的寄存器速度是ns级的,根据木桶效应,如果CPU和IO设备之间进行数据传输的话,数据传输速率和IO设备的速度一个级别,速度太低;所以就找了存储器作为一个速度的缓冲,输入设备先将数据传输到内存,再由内存送到运算器进行处理,结果交给内存,再给到输出设备,内存是硬件级别的缓冲空间,处于核心地位,计算机的整体运算速度其实是和内存的速度属于一个级别的,;而且此时数据传输和运算可以是并行的,在CPU处理一批数据的时候,输入设备可以将下一批数据预加载到内存,或者内存也可以把运算结果加载到输出设备;

为什么不都做成CPU内部寄存器的存储部件呢?太贵了!大几十万的电脑不利于普及啊;

那么我们来理解一下为什么处于磁盘中的文件要运行必须先加载到内存中(成为进程),因为现代计算机架构基本都是冯诺依曼,有略微优化,那么CPU是不能和磁盘打交道的,先加载到内存,才能进行后续操作;

那么文件为什么存储在磁盘呢?主存、CPU都是掉电易失性存储器,断电之后存储内容也消失了;而磁盘是非易失性存储器,掉电之后存储的文件还在;

以及默认显示的数据,是会被缓存到内存的,

输入设备:鼠标、键盘、话筒、摄像头、网卡、磁盘等;
输出设备:显示器、播放器硬件、磁盘、网卡等;

有的设备是纯输入或纯输出,有的设备既可以是输入也可以是输出;

运算器:进行数逻运算(对输入的数据进行数据运算、逻辑运算等);

控制器:对硬件计算流程进程控制

五个组件都是独立的个体,需要用系统总线或者IO总线连接起来;协同作用,组织起来形成一个系统对外输出计算服务。

Linux下一切皆文件,那么登录qq肯定要,任何程序本质可是可执行的二进制文件,所以首先将QQ的可执行程序加载到内存,进行可视化界面渲染,登录QQ之后,我们输入的文字从键盘输入设备输入,和聊天框、发送时间、昵称、头像等传输到主存,接着给到运算器进行打包、压缩,接着将结果给到主存,接着传输给输出设备网卡,网卡传送到网络,网络传输给另外一端的网卡,接着网卡作为输入设备,将文件给到存储器,存储器给到运算器,进行解压、解包,之后传输给主存,主存给到显示器等输出设备(以上为简要理解);如果传输文件等,下载之后存储在磁盘中。

一切操作都要落到硬件上,但软件开发、客户使用都是和软件打交道,因为我们没有和硬件打交道的能力,再者现代操作系统不相信用户,一些数据和文件配置都是很重要的信息,如果用户无意或者恶意修改,会导致系统崩溃、出错等问题,我们都是通过软件通过系统调用来完成各项功能,因为系统调用接口是操作系统实现的,不涉及恶意修改等问题;

而一切硬件都是通过驱动程序控制的,因为硬件各种各样,驱动程序提供向上的各种接口,在硬件出厂时由厂商配置驱动程序,用户只需安装对应的驱动程序即可使用;

再往上是操作系统,进行内存、驱动程序、程序、文件等的管理,狭义的操作系统是指Linux内核,广义的操作系统还包括shell外壳等;

再往上是提供给用户的系统调用接口,只有系统调用才能对系统进行修改,保护了系统的安全性;因为Linux底层是C语言写的,其实接口就是函数,系统调用进行传参,返回值给到上层;其实所有编程语言的本质是一套头文件、一个库文件和一个编译器,库文件比如printf()就是封装各种各样的系统调用接口,供程序员二次开发使用,因为C/C++是混编的,所以C++就提出了很多适配C++风格的C语言头文件,Linux底层是C语言写的,那么各种各样上层语言主要是面向对象语言诸如python,go,php,java的解释器都是C/C++写的,比如java的虚拟机;

再把系统调用封装成用户操作接口,比如我们用的shell外壳,以及各种语言封装的库函数等,程序员可以使用不同的编程语言来进行系统调用,比如一般我们提到的windows,ios,安卓,苹果系统的概念;

再往上就是用户级别,进行指令操作、后端开发等,用户包括普通用户和程序员,普通用户主要使用程序员开发的各种软件,所以和计算机系统接口打交道的基本上是程序员,包括库开发、做语言级别或者管理;

从开发的角度来看,操作系统对外表现为一个整体,为了自身安全,封闭一些行为,但是会暴露部分接口(系统调用),来为上层提供服务,供上层开发使用;系统调用在使用上,功能比较基础,对用户要求较高,一般开发者会对部分系统调用进行适度封装,形成库,有利于更上层用户或者开发者进行二次开发;

基于系统之上的编程称为系统编程,Linux下一切皆文件,网络也属于文件,网络编程也属于系统编程;

那么文件什么时候加载到内存?如何加载到内存?诸多加载到内存的程序如何有条理地进行调度,OS如何对硬件和软件资源进行管理?

OS系统作为管理者不需要和硬件直接打交道,OS只需要硬件的状态等信息就可以实现管理,而数据的收集工作交给驱动程序;当OS要使用硬件时,直接和驱动程序交互,驱动程序负责驱动硬件提供服务;

硬件、驱动程序和OS是计算机运行基本载体,有了这些,计算机就能很好的跑起来,但跑起来不是目的,计算机本质是工具,用起来才是目的;

那么OS是如何管理的呢?

管理其实是对数据(属性)的管理,所有面向对象语言都会先描述再组织,描述一般为定义一个类或struct,成员变量刻画了某一类对象的共有属性,以属性来表征一个又一个的对象,将现实世界的人与物抽象化为由各种属性表征的类和对象,即所谓建模,把现实世界抽象成计算机能读懂能处理的语言,从而解决现实世界的问题;描述完之后实例除了大量的对象,接下来还要进行组织,运用顺序表、链表、二叉树、AVL树等数据结构OS下必然会有大量的数据结构将一个又一个的数据组织起来,结构决定配套算法,算法背后是应用场景;其实对人和物的管理最终都是落到对数据结构诸如链表等的增删改查上

比如学校对学生的管理,

通讯录的代码实现定义数据结构也是一个思路,

比如实现三子棋时struct类型定义,坐标xy,用二维数组进行管理,以及扫雷游戏,定义雷和空白,并用二维数组保存;

2. 进程

2.1 概念

开机的第一件事就是加载OS到内存,进程是加载到内存中或正在运行中的程序,也叫任务,进程=数据结构对象PCB+数据(包含指令);

PCB,进程控制块,描述进程所拥有的属性值,本质是结构体,Linux下是task_struct,附一份源码链接,tesk_struct 源码

test_struct内容

  • 进程编号PID 状态(休眠、挂起、阻塞、运行、死亡等)
  • 优先级(优先级用于进程调度)
  • PC(程序计数器),程序进行跳转需要记录当前执行位置,便于回到调用处继续执行
  • 内存指针,指向进程对应的数据等
  • 上下文数据,保存上下文信息便于重新调度恢复程序执行现场信息
  • I/O状态,包括I/O请求,分配设备情况
  • 记账信息,进程使用CPU时间总和,分时处理系统用于更公平进程进程调度,反应调度器优劣,包括信号、文件、网络等
  • ...

CPU除了进程调度,还要进行检查系统状态、释放资源、控制内容刷新到磁盘等日常管理工作;

同一份文件在不同时间加载到内存,会形成不同的进程,因为相同的只是数据内容;

一个操作系统可以运行多个进程,那么需要数据结构进行管理,数据结构可能是叠加的,比如既是链表,又是队列,错综复杂;

OS的整个架构是未来编程语言再怎么变化,也稳如泰山,因为一旦底层发生变化,基于此的架构都有可能出现问题;

ps ajx 可以查看进程完整信息

ls /proc 查看实时进程信息,OS用文件系统将内存、文件等信息进行可视化;这些都是内存级别的文件或进程,在关机之后会不复存在,开机之后进行加载;所有运行的进程都会在这个目录下生成一个名为进程PID号的目录或文件,举例如下,test进程的PID是17391,通过ls /proc/17391即可查看

cwd是进程所在目录,当通过进程创建文件,比如fopen("linux.log","w");创建名为linux.log的文件,这个文件默认创建在cwd当前工作目录下,在进程运行的时候会默认在文件名前补上当前工作目录的路径,linux.log其实是/home/guchen/dir/linux.log,exe(软链接)显示链接到文件的磁盘位置;

可以在vim的命令模式下直接查看man手册


可以简单理解,任何一个进程都是bash的子进程

2.2 fork

./运行程序 指令级别

fork() 代码层面创建子进程

fork之后的语句是父子进程共享


while :; do ps ajx | head -1 ; ps ajx | grep proc | grep -v grep; sleep 1; done

Q1: 为什么好似if和else if同时满足?

Q2: fork做了什么?

Q3:好似id有两个值,怎么做到的?

fork是系统调用,本质就是C语言函数,创建子进程(进程=内核数据结构+代码+数据),Linux内部PCB是以task_struct进行存储的,所以会把父进程的task_struct复制一份,接着把pid, ppid等内容进行修改或填充,父子进程共享fork之后的代码(因为代码不可以被修改),所以其实是有两个进程同时运行了fork之后的代码,父进程的id>0,子进程的id=0;在return返回id之前,fork函数前面的语句已经完成了相关工作,创建PCB,父子进程共享fork之后的代码,对于父进程的数据,进行的是写时拷贝,如果子进程要访问父进程的数据,直接访问,如果子进程要写入父进程的数据,因为进程之间是独立的,所以会单独开辟一片空间供子进程写入,如果父进程要写入,直接在父进程的数据进行写入即可

id好似是两个值,和虚拟地址空间有关

为什么要给父进程返回子进程的pid呢?因为父进程要区分不同的子进程呀,为什么给子进程返回0呢?因为是表明进程创建成功

返回值不同,区分不同的执行流,执行不同的代码块

为什么创建子进程,让父子进程执行不同的代码段,完成用户需求

bash是如何创建子进程的,fork,执行解释新命令

2.3 进程状态

2.3.1 理论

运行态

阻塞态

挂起态

操作系统是通过管理PCB对进程进行管理的,Linux下通过将task_struct放在不同队列中,进行管理;

在运行队列的进程(已经获取到申请的资源等,可以被调度上处理机运行)都属于运行态(R),因为每个进程上CPU运行的时间是很短的,人类是感知不到这种进程调度的(由调度器完成),一般是选择队头的task_struct上处理机运行,下处理机之后放到队尾,选下一个task_struct上处理机运行(进程切换);

一个进程上处理机并不是运行结束才下处理机,一般是一个时间片的时间,在一个时间段内,所有进程代码都会被执行(并发

如果进程申请的资源比如I/O得不到满足,就被放到对应设备(或者进程,比如一个进程要等到另一个进程运行完)的等待队列;如果迟迟得不到满足,OS内部的内存资源严重不足,保证正常的情况下,省出来内存资源,就会把进程对应的数据和代码放到磁盘(换出),只留task_struct进行排队(挂起态),得到资源时再将代码和数据加载到内存(换入),之后从阻塞队列进入到运行队列

如果装过OS,应该可以看到C, D,E, swap(交换分区)

2.3.2 Linux中进程的状态

Linux源码是这样说的

c 复制代码
/*
 * The task state array is a strange "bitmap" of
 * reasons to sleep. Thus "running" is zero, and
 * you can test for combinations of others with
 * simple bit tests.
 */
static const char * const task_state_array[] = {
    "R (running)", /* 0 */
    "S (sleeping)", /* 1 */
    "D (disk sleep)", /* 2 */
    "T (stopped)", /* 4 */
    "t (tracing stop)", /* 8 */
    "X (dead)", /* 16 */
    "Z (zombie)", /* 32 */
};

R

S 浅度睡眠

D 深度睡眠

T

t

僵尸进程 Z 因为父进程创建子进程的目的就是让子进程为父进程做事情,当子进程完成使命要结束进程生命的时候,需要父进程获取子进程的任务完成情况等信息,获取完之后,子进程的task_struct以及数据代码占用的内存空间才能被系统回收,这中间等待父进程获取子进程信息的过程称为Z状态,子进程称为僵尸进程,如果父进程一直不接管子进程的各种信息,子进程一直没有被回收,就会导致内存泄漏

如果父进程结束生命,而子进程没有结束生命,子进程的父进程就会变为PID为1的进程的子进程,此时被1号进程(系统)收养的进程被称为孤儿进程

在后台运行的进程,此时在命令行输入可以执行指令,但是在前台运行的进程,在命令行输入指令不可以执行

状态+ 表示在前台运行,可以用Ctrl+C终止,没有+表示在后台运行,只能kill -9终止

我们看到bash处于阻塞态,因为命令行解释器在等待用户进行输入

sleep 浅度睡眠 可以随时响应需求

disk sleep 深度睡眠 不响应需求,一般是在向磁盘写入,如果此时OS发现内存严重过载,将进行kill,之后磁盘如果没有写入成功,给进程返回时发现进程不在了,一般情况下磁盘都会丢弃没有写入成功的数据,导致数据丢失;所以规定在磁盘写入时,不允许kill进程,此时进程是disk sleep状态,也就是D状态,写入任务进行完毕后,恢复S状态;如果系统中有1个让用户都感知到了D状态的进程,基本上OS快崩了,Windows下经常出现的"向Microsoft报告问题",进程无响应就是OS撑不住了,没办法给进程分配处理机等资源

可以使用命令dd进行验证

T stop 可用kil -19 暂停进程,kill -18 让进程继续

stop状态等待某一种资源或者被另一个进程控制,比如gdb调试下的断点

t tracing stop 可追踪



当我们将proc Ctrl+C关掉后,proc和子进程都没了,因为proc是bash的子进程,bash会进行回收等工作

如果父进程没了,子进程会被1号进程收养,该进程被称为孤儿进程

1号进程是系统

因为子进程结束的时候也要被释放资源,但是父进程没了,这个活就交给系统了

Linux内核管理task_struct既是链表也是多叉树,因为一个父进程可以创建多个子进程,而每个子进程又可以作为父进程创建子进程


同理也可以通过link1进行另外一种数据结构的链接

2.4 优先级

是什么

权限(有没有资格做某件事)VS优先级(已经具备做某件事的资格,优先级用于确定做某件事的先后顺序)

为什么

因为CPU资源是有限的,而进程是由成百上千的,进程之间是竞争关系,竞争性,因此OS需要合理调度进程谁先谁后获得CPU资源,确保良性竞争,优先级高的,优先调度;如果进程对应的代码长时间得不到访问,就会导致饥饿,甚至饿死了

怎么做

优先级数小的,优先级高,PRI(new)=PRI(old)+nice,通过调整nice值来调整优先级数,来调整优先级,但是PRI(old)Linux下就是80,nice的值是[-20,19],

ps -l 默认只会查询当前窗口运行的进程

ps -al 可以查其它窗口运行的进程

可以通过nice或renice更改进程优先级

在命令行输入top,接着输入r,再输入要更改的进程ID

输入要改为多少

改为-30,但是系统显示-20,超出极值按极值计算

普通用户没有更改nice的权限,通过su - 提权,输入root账号密码

那么是如果做的呢?通过位图,举例如下,就是一串二进制,每一个二进制0/1表征是否可用等状态;i是存储在第几个元素,pos是每个char的第几位

接着,runqueue结构体维护两个数组,长度为140,0-99用于实时进程,比如车载系统,100-139正好覆盖80+[-20,19],因此每来一个pri为60的进程就链在runqueue[100]的位置,其它优先级数的情况类似,假如现在要调度,从runqueue[100]开始,可是如果此时也有进程要进入呢,那就放在waitqueue,当runqueue空的时候,交换run和wait两个指针即可(swap(wait,run)),runqueue为空用位图表征,bitmap存了char bits[5],一共40位,表征100-139每个队列是否为空,如果isempty为0就表示runqueue已经,没有需要调度的进程;

接着,如果isempty不为0,那就从最低位开始(从低到高表示100-139每个指针表示的队列是否为空),调度非零位表征队列的进程

介绍一个多行注释的方法vim下

首先命令模式下,Ctrl+v,左下角会显示V-BLOCK

接着HJKL进行←↓↑→选中要注释的区域,不要用键盘上左右山下的四个键

接着I进入插入模式,输入//,接着按esc,注释选中区域

取消注释:首先Ctrl+v,接着HJKL进行←↓↑→选中要取消注释的区域,按d取消注释即可

牛刀小试

  1. 不属于冯诺依曼体系结构必要组成部分是:
    A.CPU
    B.Cache
    C.RAM
    D.ROM

B

  1. 冯诺依曼体系结构中数据输入设备的有?(多选)
    A.键盘
    B.显示器
    C.内存
    D.磁盘

AD

  1. 冯诺依曼体系结构计算机的基本原理是?
    A.信息存储
    B.存储智能
    C.数字控制
    D.存储程序和程序控制

D

  1. 操作系统的主要功能有()
    A.控制和管理计算机系统软硬件资源
    B.对汇编语言,高级语言和甚高级语言程序进行翻译
    C.管理用各种语言编写的源程序
    D.管理数据库文件

A

  1. linux的系统调用是指()
    A.由内核发起的调用
    B.glibc函数库里的函数
    C.由系统管理员运行的程序
    D.是用户进程调用内核功能的接口

D

  1. 下面关于系统调用的描述中,错误的是( )
    A.系统调用把应用程序的请求传输给系统内核执行
    B.系统调用函数的执行过程应该是在用户态
    C.利用系统调用能够得到操作系统提供的多种服务
    D.是操作系统提供给编程人员的接口
    E.系统调用给用户屏蔽了设备访问的细节
    F.系统调用保护了一些只能在内核模式执行的操作指令

B

内核态

  1. 下面的函数哪个是系统调用而不是库函数( )
    A.printf
    B.scanf
    C.fgetc
    D.read
    E.print_s
    F.scan_s

D

  1. 关于 linux 的进程,下面说法不正确的是()
    A.僵尸进程会被 init 进程接管,不会造成资源浪费;
    B.孤儿进程的父进程在它之前退出,会被 init 进程接管,不会造成资源浪费;
    C.进程是资源管理的最小单位,而线程是程序执行的最小单位。Linux 下的线程本质上用进程实现
    D.子进程如果对资源只是进行读操作,那么完全和父进程共享物理地址空间

A

  1. 在抢占式多任务处理中,进程被抢占时,哪些运行环境需要被保存下来?[多选]
    A.所有cpu寄存器的内容
    B.全局变量
    C.页表指针
    D.程序计数器

ACD

  1. 下列有关进程的说法中,错误的是? [多选]
    A.进程与程序是一一对应的
    B.进程与作业是一一对应的
    C.进程是静态的
    D.进程是动态的过程

ABC

  1. 系统感知进程的唯一实体是()
    A.进程id
    B.进程控制块
    C.进程管理器
    D.进程名

B

  1. 下面有关孤儿进程和僵尸进程的描述,说法错误的是?
    A.孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。
    B.僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
    C.孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。
    D.孤儿进程和僵尸进程都可能使系统不能产生新的进程,都应该避免

D

  1. 关于僵尸进程,以下描述正确的有?
    A.僵尸进程必须使用waitpid/wait接口进行等待
    B.僵尸进程最终会自动退出
    C.僵尸进程可以被kill命令杀死
    D.僵尸进程是因为父进程先于子进程退出而产生的

A

  1. 以下关于孤儿进程的描述正确的有()
    A.父进程先于子进程退出,则子进程成为孤儿进程
    B.孤儿进程会产生资源泄漏
    C.孤儿进程运行在系统后台
    D.孤儿进程没有父进程

AC

  1. 请问孤儿进程会被以下哪一个系统进程接管?
    A.syslogd
    B.init
    C.sshd
    D.vhand

B

  1. 以下描述错误的有
    A.守护进程:运行在后台的一种特殊进程,独立于控制终端并周期性地执行某些任务。
    B.僵尸进程:一个进程 fork 子进程,子进程退出,而父进程没有 wait/waitpid子进程,那么子进程的进程描述符仍保存在系统中,这样的进程称为僵尸进程。
    C.孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,这些子进程称为孤儿进程。(孤儿进程将由 init 进程收养并对它们完成状态收集工作)
    D.精灵进程:精灵进程退出后会成为僵尸进程

D

相关推荐
yuezhilangniao1 小时前
程序人生-杂谈-简单对比一下 学霸和linux科学设计
linux·程序人生·职场和发展
只想恰口饭1 小时前
程序人生-Hello’s P2P
linux·c语言·ubuntu
hoperest1 小时前
程序人生-Hello‘s P2P
linux·c语言·程序人生·ubuntu
quixoticalYan1 小时前
哈工大计算机系统大作业报告-程序人生-Hello’s P2P
linux·windows·程序人生·ubuntu·课程设计
czxyvX2 小时前
010-Linux内核进程调度队列(了解)
linux
一路往蓝-Anbo2 小时前
第 1 章:M33 领航——STM32MP257F-DK 硬件解密与启动逻辑重构
linux·stm32·嵌入式硬件·重构
暴力求解3 小时前
Linux--进程(四) 进程优先级与进程切换
linux·运维·服务器
Re_Virtual3 小时前
OpenEuler 20.03构建zabbix7.0 rpm包
linux·zabbix·openeuler
落羽的落羽4 小时前
【Linux系统】磁盘ext文件系统与软硬链接
linux·运维·服务器·数据库·c++·人工智能·机器学习