前言:
本文将逐步深入探讨进程相关内容。在进入进程讨论之前,我们需要再次回顾操作系统的基本概念。接下来,我们会介绍进程的定义、如何查看进程,以及在Linux中与进程相关的文件等问题。进程是一个庞大的主题,会持续更新多节,因此其知识点较为复杂,同学们需要特别注意。
首先,我们需要理解为什么需要操作系统。
操作系统的主要工作是管理软硬件资源。管理硬件是通过驱动程序实现的,这些程序将硬件数据组织成链表,操作系统可以直接修改链表信息,从而通过驱动程序管理硬件。这种方法是先描述再组织,最终实现对硬件的管理。然而,操作系统的根本目的是为用户提供良好、稳定、高效和安全的服务,因此需要采用相应的方法来管理各种资源。
从操作系统的结构图中我们可以看到,操作系统包括进程管理等多种管理功能。操作系统在管理进程时,允许多个进程同时存在。例如:
这些都是进程。要查看电脑中的进程,只需按esc + shift + ctrl打开任务管理器即可。这证明了进程是可以多重存在的。
那么,什么是进程呢?
如果将某个工程的数据和代码加载到内存中,代码是否会自动运行呢?答案是否定的,因为CPU并没有读取这些数据。当代码开始运行时,就产生了一个进程。进程不仅仅是正在运行的程序,它是由PCB(进程控制块)加上自己的代码和数据构成的。
那么,什么是PCB呢?PCB是一个包含多种数据类型的结构体,用于存储进程的相关信息。类比于操作系统管理硬件时需要硬件信息来构建链表,操作系统通过修改链表实现对硬件的管理。PCB的全称是process control block,即进程控制块。之所以称为控制块,是因为操作系统可以通过对PCB的操作来控制进程。
因此,我们得出结论:进程 = PCB + 自己的代码和数据。
为什么需要PCB的概念呢?
因为操作系统遵循“先描述再组织”的原则。通过PCB,操作系统可以在内存中以链表的形式连接多个进程,每个PCB包含指向下一个PCB的指针。这样,操作系统可以通过管理链表来管理进程。
具体的PCB结构体名称是task_struct。外国人将其称为task,因为他们认为这是任务的一种表现形式。
进程是动态运行的,我们如何理解动态运行呢?
在内存中,操作系统占据一定空间,用于管理各种软硬件。当不同的进程进入内存时,需要排队处理。因此存在task_queue,即进程队列。动态运行实际上是多个PCB排队的过程。
具体内容将在后续文章中详细介绍。
现在让我们来看看task_struct的内部属性:
无论是运行指令还是编写代码,本质上都是创建一个进程。指令执行得非常快,我们通常看不到其过程。那么,如何区分不同的进程呢?每个进程都有一个唯一的标识,即pid(进程ID),类似于学生的学号。
我们如何查看pid呢?
在回顾上篇文章中介绍的系统调用接口:
系统调用是操作系统提供的函数,我们从未调用过它,现在是时候调用我们人生中的第一个系统调用接口了。我们使用man手册查询可知:
从手册说明中我们了解到,2号接口是系统库函数调用,即我们即将学习的getpid:
#include <stdio.h> #include <sys> #include <unistd.h> int main() { printf("I am a process\nMy pid is %d\n",getpid()); return 0; }
有趣的是,getpid()函数需要两个头文件才能使用。
打印结果正常,每次运行的进程ID都不同,我们的初步目标已经达成。但进程肯定不止这些内容,我们应该输入ps -xaj来查看,这里先记住,xaj的顺序无关紧要:
#include <stdio.h> #include <sys> #include <unistd.h> int main() { while(1) { printf("I am a process\nMy pid is %d\n",getpid()); sleep(1); } return 0; }
我们修改代码为死循环,以便观察:
我们使用管道筛选出包含test的进程,前两个我们可以理解,但为什么grep也有呢?因为ps -xaj打开了进程,通过管道筛选,筛选也是一个进程。如果我们不想看到grep进程,可以使用:
使用grep -v grep反向筛选出不含grep的进程:
当然,直接使用ps -xaj就相当于Windows中的任务管理器,可以查看所有进程。
我们可以看到,打印出来的pid是14191,在打印出来的head -1中也有pid,也是14191,所以pid打印是没有问题的。
现在我们再来看ppid是什么?ppid的全称是parent process id,即父进程的ID。有人可能会问,父进程?这东西还有继承关系吗?我们可以在一个进程中创建多个进程,使用到的函数是fork():
这里有一个非常有趣的点,会颠覆你的编程观念,即返回值pid_t,本质上是unsigned int,这里先留个悬念。
我们来看一段有趣的代码:
#include <stdio.h> #include <sys> #include <unistd.h> int main() { printf("I am a father process!\n"); fork(); printf("I am a child process\n"); return 0; }
试问这段代码的运行结果是什么?
直接看结果:
可以发现打印了两次第二次的printf,我们可以这样理解:我是一个公司老板,在没有招员工之前一直做相同的事,招了员工之后,员工和我做相同的事,但我之前做的工作员工还需要做吗?不需要,所以第一行的printf不会执行,父进程原本的代码是要执行printf的,所以会打印两次child process。
while(1) { printf("This is parent process:%d\nThis is child process:%d\n",getppid(),getpid()); sleep(1); }
打印结果显示,父进程的ID是22252,子进程ID是22239,在ppid和pid下面也得到了验证。
更多内容请看后续文章!!!
感谢阅读!
以上就是初识Linux · 进程(2)的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号