👉进程👈
什么是进程
在课本上,我们经常会看到一个运行起来(加载到内存)的程序就是进程、进程和程序相比具有动态性等等的说法。其实,这些说法都是不全面的。那么接下来,我们一起探讨什么是进程?
我们写代码经过编译和链接后,形成了可执行程序。而程序的本质是在磁盘上放着的文件。程序想要运行起来,首先需要加载到内存。那么,内存了肯定会有很多被加载进来的程序,那么操作系统如何管理这些程序呢?是不是先描述,在组织啊?
为了描述这些程序,就引入了进程控制块(Processing Control Block)的概念。而进程等于内核数据结构(struct task_struct)+ 进程对应的磁盘代码。
task_ struct 内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息。
查看和杀死进程
ps ajx | head -1 && ps ajx | grep '进程名' #查看指定进程 kill -9 进程的PID #杀死指定进程
注:PID (Process ID)是进程的标识符。grep 指令也是一个进程。进程在调度运行的时候,进程具有动态属性
注:getpid 函数是获得当前进程的 PID,而 getppid 函数是获得当前进程的父进程的 PID。
查看进程目录 ls /proc
注:删掉进程对应的可执行程序,进程还可以跑。因为进程跑起来就和可执行程序没有什么太大的关系了。Ctrl + C 可以退出前台程序。
如果我们把我们的进程退出再运行起来,如此重复。我们会发现一个现象:子进程的 ID 一直在变,而父进程的 ID 一直不变。这是为什么呢?
那么,我们就来看看这个父进程是什么。
其实该父进程就是 bash,该进程是云服务器开启时系统帮我们创建好的。一般情况下,命令行上启动的进程的父进程都是 bash。shell 以创建子进程的方式来跑我们的程序,子进程出问题了,父进程不会受到任何的影响。那 shell 是如何创建子进程将我们的程序跑起来的呢?这部分内容将在进程控制里讲解。接下来,我们来学习一下如何创建子进程。
创建子进程
fork 是一个创建子进程的函数。
注:vim 的批量化注释:先摁下 Ctrl + V 键,再连续摁下方向键(j - 向下,k - 向上)至你想要注释的内容,再摁下 Shift + i 键,最后输入 // 就可以实现批量化注释了。
上面只是一个小小的例子,我们通常不这么来写代码。
对于 fork 函数,我们重点研究它的返回值。如果子进程创建成功,将会给父进程返回子进程的 ID,给子进程返回 0。如果子进程创建失败,将会给父进程返回 -1。
知道了 fork 函数的返回值后,我们对上面的代码再进行修改并让程序跑起来。
上图的结果,很好地说明了子进程创建成功,将会给父进程返回子进程的 ID,给子进程返回 0。但我有一个问题,为什么同一个变量 id在后续不会被修改的情况下有不同的内容呢?现在这个知识点,无法给大家讲解,这里涉及了进程地址空间的内容。
fork 函数真正的用法是利用分支结构来让父子进程执行不同的代码,那我们再把上面的代码修改一下。
现在父子进程在一起运行,那么这就是多进程了。那现在问题来了,如果是 C语言的话,if - else if - else 结构能够同时成立吗?很明显不能!但是现在似乎可以了。其实真正的原因是 fork 函数执行之后,会有父子进程两个进程在执行后续的代码,也就是说 fork 函数之后的代码,被父子进程共享。那么,我们就可以通过返回值的不同,让父子进程执行后续共享代码的一部分。
👉总结👈
本篇博客主要讲解了冯诺依曼体系结构,什么是操作系统、库和函数调用的区别、什么是进程、如何查看和杀死进程以及如何创建子进程等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️