网页制作模板的网站免费,中企动力会员控制平台,网络策划岗位要求,制作h5免费平台#x1f496;作者#xff1a;小树苗渴望变成参天大树#x1f388; #x1f389;作者宣言#xff1a;认真写好每一篇博客#x1f4a4; #x1f38a;作者gitee:gitee✨ #x1f49e;作者专栏#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法#x1f384; 如 果 你 … 作者小树苗渴望变成参天大树 作者宣言认真写好每一篇博客 作者gitee:gitee✨ 作者专栏C语言,数据结构初阶,Linux,C 动态规划算法 如 果 你 喜 欢 作 者 的 文 章 就 给 作 者 点 点 关 注 吧 文章目录 前言一、进程标识符1.1认识第一个系统调用接口1.2 第二个系统调用接口fork 二、总结 前言
上篇Linux我们讲解了什么是进程从硬件在到软件再到宏观上讲解了操作系统是怎么管理我们软硬件的大家应该已经有了一定的认识和了解今天我们就对进程展开来讲点东西我们的在Windows这样的操作系统是使用PCB来这个的结构体形成的结构去管理的但在Linux上我们是task_struct结构体去管理我们的软硬件的那我们结构体里面到底有什么在上节课我们只是把具体的属性给大家展示了但是并不知道什么意思今天我们先介绍其中一点话不多说我们开始进入正文。 task_ struct内容分类 标示符: 描述本进程的唯一标示符用来区别其他进程。 状态: 任务状态退出代码退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器: 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针还有和其他进程共享的内存块的指针 上下文数据: 进程执行时处理器的寄存器中的数据。 IO状态信息: 包括显示的I/O请求分配给进程的IO设备和被进程使用的文件列表。 记账信息: 可能包括处理器时间总和使用的时钟数总和时间限制记账号等。 其他信息 fork一会再说先做一个知识铺垫。
一、进程标识符
我们上篇的最后讲解了怎么去看一个进程我们来写一个死循环的程序防止他很快就结束进程不方便我们去查看他的进程 #includestdio.h#includeunistd.hint main(){while(1){printf(我是一个进程\n);sleep(1); }return 0;}第一通过proc这个文件夹去看但是这种方法不知道对应的是哪个进程。 第二种通过命令查看ps ajx | head -1 ps ajx | grep proc或者ps ajx | head -1 ps ajx | grep proc | grep -v grep 我们的进程task_struct就是一个结构体类型的结点ps的作用实际就是在遍历这个结构通过grep把找到的过滤出来。我们发现一个进程有这么多个属性我们值需要了解其中两个其余的不重要一个是PID(进程id)一个是PPID(父进程id)后面会介绍到这两者有什么关系
1.1认识第一个系统调用接口 通过计算机层状结构图我们知道了系统调用接口是直接对接操作系统的系统调用接口也是一个函数一会学习的两个接口就是来查看PID和PPID的我们来看代码 #includestdio.h#includesys/types.h#includeunistd.hint main(){while(1){printf(我是一个进程,PID:%d,PPID:%d\n,getpid(),getppid());//这两个系统调用接口都是一个无参函数哪个程序去调用就返回谁的PID和PPIDsleep(1); }return 0;}我们来写一个命令当成监视窗口while :; do ps ajx | head -1 ; ps ajx | grep proc | grep -v grep ; sleep 1 ; done 这是每个1秒显示一个进程信息。 大家看到我们通过系统调用接口也把我们的id给获取到了。
大家应该会发现我将自己写的程序运行了两次PID两次都不同PPID却是相同的这是为什么呢我们来看看这个程序PPID是什么。 这个程序的PPID是我们bash程序的PIDbash不就是我们之前说的王婆这就是一个外壳程序命令行解释器是我们每次登录的时候Linux操作系统自动就会运行这个bash程序形成了一个进程我们写的程序就是在这个进程创建的基础下跑当我们再次登录这个bash进程的PID又会发生变化而我们自己写的程序每次运行的PID不一样是正常的就好比你刚进校的学号每个人第二次考上进去的学号都是不一样的。 通过上面的解释操作系统是不会让你直接去在他的进程上创建子进程去运行的是通过bash进程我们写的程序就是他的子进程在bash进程下创建子进程去跑的为什么要这么做接下来我们讲到fork就可以来解决我刚才说的问题 1.2 第二个系统调用接口fork
这个接口就可以让我们更好的认识父子进程的关系以及上面的操作是怎么做到的我们先来看一个例子 大家看到我们加了fork之后居然打印了两次这个例子还不能说明什么我们来看看文档 这个返回值看上去非常的抽象是要返回两个值太不可思议了我们再来看一个例子 我们看到两个死循环都走起来了这下更不可思议了。一个程序怎么会执行两个死循环语句呢接下来我将通过四个问题来带大家解决疑惑 为什么fork需要给子进程返回0给父进程返回子进程的PID 1为什么要返回不同的值是为了区分让不同的执行流去执行不同的代码块就好比上面的代码。 2每个父进程可以有好多个子进程子进程只有一个父进程所以父进程需要知道子进程的PID来唯一标识要控制哪个子进程去执行。而返回0是为了标识子进程子进程通过getppid就可以找到父进程 fork干了什么事情 这下大家应该知道为什么要返回两次是如何做到分流的吧但是fork是一个函数怎么做到返回两次呢 一个函数是如何做到返回两次的 写时拷贝 **大家应该知道我们在Windows上我们启动了许多个软件但其中一个软件挂掉了会不会影响其他软件答案是不会的所以在任何平台上进程在运行的时候是具有独立性的**那我们刚才说到fork他也是创建一个子进程在上面我们说到父子进程的代码和数据是共享的那么就会导致如果子进程修改了数据那么可能会影响到我父进程的运行此时就不符合进程具有独立性的思想想问一下大家代码共享而数据不共享是不是具有独立性答案是具有因为你的代码最终操控都是数据只要操作的数据不是一样的就不会影响到其他进程的运行所以防止这种事情的发生我们父子进程的代码时共享数据不共享如下图 将父进程的数据拷贝到新的空间个给子进程自己玩就算子进程修改了数据也是他那一块空间的数据不会影响到父进程对应的数据就实现了进程之间的独立性。 此时又面临一个问题当内存空间严重不足时我们父进程还是把所有的数据都拷贝到子进程的数据空间里吗如果子进程没有使用这些数据那么不就造成资源浪费了吗内存要想办法给自己节省空间所以此时就引入了写时拷贝这个概念我们的父子进程一开始还是指向同一块数据的如果子进程想要修改数据内存在给其分配空间将要修改的数据先拷贝到新空间然后你在修改这样就做到了用多少拷多少。 这就好比C说的深浅拷贝你要想看看数据指向同一块空间也无所谓但你要想修改内容我给你在开辟一块空间你修改了不能影响到我原来的数据。 有了上面的写时拷贝我们再来谈谈fork函数返回值的问题上面只是说到怎么返回两次的父子进程分别去调用return就可以了那怎么做到返回不同的值原因就是return语句是写入写入就要修改数据此时内存就会将返回的数据拷贝到子进程的新数据区中然后让子进程进行修改在返回所以我们会返回两个同的值。 通过上面的例子我们发现bash进程也应该是操作系统fork出来的子进程那我们的操作系统为什么要这么做因为我们的操作系统也是一款软件在内存中也有自己的进程他不相信任何用户所以他需要创建子进程用户想要修改数据修改的是子进程指向的数据空间修改了不会影响我操作系统进程指向的数据空间这样就保证了操作系统的安全性 相信此时大家对fork的理解又加深了并且了解父子进程的关系了吧。
一个变量是如何接收两个不同的值 这个需要使用到我们进程地址空间的知识但是此时已经不影响我们对fork以及父子进程的理解了而且也知道fork的作用以及目的。
如果父子进程被创建好fork()往后哪个进程先运行呢 谁先运行是由调度器去决定调度器有自己的调度算法这个在后面的博客会介绍到他是怎么去调度的目前我们上面这个问题是不知道答案的不确定谁先被执行。 通过上面四个问题的解释我来给fork做一个小总结 fork有两个返回值父子进程代码共享数据各自开辟空间私有一份采用写时拷贝fork 之后通常要用 if 进行分流 二、总结
通过这节的学习我们知道了fork这个系统接口的用法以及作用可以说这个接口对我们的学习是又很大的帮助可以使我们分别去做不同的事也让我们知道函数还有又这样的功能是我们的认知又提高了一个档次接下来的一篇我还是介绍task_struct里面的属性-进程状态。一起来期待吧