一.系统调用

操作系统是个很矛盾的东西,它不相信任何人或用户,即不允许别人肆意妄为进入操作系统内部,又要为用户提供指令操作,开发操作,管理操作等服务。这时该怎么办呢?操作系统就会为用户提供相关的接口(函数)——system call,以实现系统调用。由于系统调用本质是调用相关接口(函数),就会有参数和返回值,用户提供参数给操作系统,操作系统提供返回值给用户,那么用户和操作系统间,就是通过数据在交互。

但对于小白来说,直接去调用这些系统的接口,是有难度的,这时,就有一个类似银行大堂经理的角色——  用户操作接口,通过提供库函数,外壳,指令等更加容易的方案,来帮助你调用。

二.进程的概念

1.了解进程

原图在笔者的码云loner的linux_storage。

补充一点:进程结束即销毁,因此即使是同一程序,每次分配到的pid是不同的。

2.task_struct的成员(属性)

①.概览

②.标示符

Ⅰ.如何获取进程的标示符?

首先得明晰,历史上执行的所有指令,工具,程序,运行起来全都是进程!

这里引入一个系统调用(其在man手册里的位置是二号手册)接口——getpid

示例:

3.再探进程

①.如何查询当前系统启动的进程?

Ⅰ.ps axj指令

可以用ps axj指令来查询:

想要查询当前特定程序的进程:ps axj | grep bin:

ps axj | head -1;ps axj | grep bin,

将特定程序进程各个属性以及它们的值列出来:

但为什么grep也会出现在这些值里?答案是grep指令本身也是一个进程,如果不想要其出现,可以用“|”多串联一条反选(但凡有grep的都不选)指令——grep -v

Ⅱ.la / proc指令

文件的方式将进程呈现出来。

proc目录里记录当前进程的所有信息,而每个数字目录(蓝色数字)都代表每个特定进程的pid,该目录内部包含进程运行的动态属性,进程结束,数字目录这个文件,以及内部的内容会被一并清除。

我们再认识一下,数字目录下的两个重要东西:
exe:会记录下进程对应的可执行文件的绝对路径,当把这个绝对路径从磁盘删除(rm)掉以后,进程仍在继续执行,这证明了程序想要执行,需要加载到内存上,此时支持进程跑的文件就是加载进内存的复印件。

cwd:可获取当前路径,从而让可执行程序位于当前路径下。

②.如何终结掉一个进程?


Ⅰ.ctrl + c就可以强行让正在执行的程序结束
Ⅱ.kill  -9  进程标示符

-9是一个信号编号,后面的文章会讲。

③.父父子子——子进程与父进程

上面在研究获取pid函数的时候我们会有疑问,ppid是个啥?答案是父进程的标示符。

怎么跟树一样,进程也分父子?是的。下面我们会逐步讲解父进程。

先来看一个现象,当运行两次同一程序的进程后,我们会发现两次的pid不同,但ppid却是相同的,这说明了什么呢?

基于这个疑问,我们用指令去查一下父进程的具体信息:

可以从command行看出,bash就是父进程,同时bash就是之前文章末尾shell提到的命令行解释器

bash是命令行解释器里具体的一种,用之前历史剧里的角色来看,贴身太监(shell)是个笼统的称谓,担任它的具体的人——吕芳,就是bash。bash仍旧是个进程,它就是父进程。
每次登录系统都会分配给当前用户一个bash。bash正常消亡时,是退出登录的时候,换而言之 ,bash这个父进程不能挂掉,挂掉就要重新登录了。因而,去查父进程的标示符时,它始终保持不变。

至于父进程与子进程还有kernal,这三者关于命令的处理,这里知识储备还不够,到后面的文章再做说明。先说明一点,我们用的诸如touch,pwd这些命令本身也是一个程序。运行命令=运行程序。

④.用fork创建进程

Ⅰ.子进程的夺舍行为

经fork创建的子进程的PCB是拷贝自父进程的,因此内容与父进程相同,从而能得到上面的红字结论。该子进程没有自己的代码和数据,因为当前没有新的程序加载!当然,子进程执行的是父进程还没有执行过的代码,也就是fork()里创建完子进程以后的部分代码,以及fork()往下的所有代码。

Ⅱ.子进程的返回值

如果子进程创建成功,则会返回两个值!一个是将子进程的pid返回给父进程,另一个0会返回给子进程:

接下来看一段代码,并引出三个疑问:


说明一下,父子进程的内容虽然相同,但其对应的fork返回值不同,pid和ppid也不同,根据if里的不同返回值(不同id)的条件,可让父子进程执行不同的代码。

第三个问题由于本篇所涉及知识储备不够,后续文章再做回答。

①.在OS中,子进程可以有多个,但父进程只能有一个(登陆时分配给用户),区分不同子进程,父进程靠的就是每个子进程返回给它的pid,因而子进程要返回pid给父进程。而由于只有一个父进程,就只用返回给各个子进程一个值0即可。

②.由于父子进程的代码完全相同,甚至包括fork(),但父进程已经把生成子进程的部分给执行掉了,当子进程再执行fork()的时候,就顺着没执行过的往下执行。子进程把没执行的部分,执行一次,就再return一次pid给父进程。父进程完完整整把所有代码执行一遍,return一次0给子进程。子进程和父进程虽然代码是共享(相同)的,但在运行上完全是独立的

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐