💘作者:泠沫
💘博客主页:泠沫的博客
💘专栏:Linux系统编程,文件认识与理解…
💘觉得博主写的不错的话,希望大家三连(✌关注,✌点赞,✌评论),多多支持一下!!
🏠 进程概念
无特殊情况下,笔者所述内容均是基于Linux操作系统环境下的。
🚀 进程控制块PCB
在介绍什么是进程之前,我们先来了解什么叫程序?
其实我们平时写代码编写好的程序本质上就是一个文件,一个安安静静的存放在磁盘上的文件。
一旦我们要运行这个文件,根据之前我们学过的冯诺依曼体系结构,我们知道一个程序要想被运行,首先要将代码和数据加载到内存当中。由于操作系统要将很多个运行起来的程序都管理起来,所以一旦程序对应的代码和数据加载到内存当中,操作系统会立即给这些运行的程序创建一个结构体struct task_struct{},这个结构体里面存放的是该运行程序的相关信息,而这个结构体也被称为进程控制块PCB。
如图是Linux源码中对进程控制块的部分描述:
如此,操作系统对这个运行程序的管理就转变成了对进程控制块做管理。所以笔者认为,真正的进程的指的是:内核数据结构(task_struct)+ 进程所对应的代码和数据。
操作系统对于进程的管理,转变成对于一个个的进程控制块的管理,这就是先描述,再组织思想的体现。
🚀 进程查看与标识符
进程查看方法一:
ps axj | head -1 && ps axj | grep myproc
其中 ps axj 表示查看所有进程,通过管道 | head -1 表示选择第一行,grep是搜索指令
上图是采用循环打印的方式来查看进程。
进程查看方式二:
系统中的进程会在 /proc 这个目录文件下,因此我们可以直接通过查看该文件的方式找到我们所要查看的进程。
如果读者细心观察会发现,我在/proc目录下查看进程的时候用的是一个数字。这个数字其实就是进程的唯一标识符。
进程并不存在进程名字这样的概念,操作系统是通过进程标识符来区分每一个进程的。
查看某一进程标识符的系统调用接口是getpid(),查看其父进程的接口是getppid()。
#include<iostream>
#include<sys/types.h>
#include<unistd.h>
using namespace std;
int main()
{
while(true)
{
cout << "我是一个进程,我的pid是:"<< getpid() << "我的父进程pid是:"<< getppid() << endl;
sleep(1);
}
return 0;
}
🚀 进程创建fork()
fork()是一个系统调用接口,其作用是创建一个子进程。
该函数创建子进程成功返回值有两个!对于子进程,返回0。对于父进程,返回子进程的pid。所以我们可以根据返回值的不同来区分父子进程。
#include<iostream>
#include<cstdio>
#include<sys/types.h>
#include<unistd.h>
using namespace std;
int main()
{
pid_t id = fork();
if(id == 0)//子进程
{
while(1)
{
printf("我是子进程,pid:%d, ppid:%d\n",getpid(),getppid());
}
}
//父进程
while(1)
{
printf("我是父进程,pid:%d, ppid:%d\n",getpid(),getppid());
}
return 0;
}
在fork()调用之前,只有一个进程。在fork()调用之后,从原来的一个进程变成了两个进程。一个是父进程,另一个是子进程。对于fork()后续的代码,父子进程共享。
运行结果:
从上面的运行结果可以看出,父子进程各自执行自己的代码,死循环的打印语句。
🏠 进程状态
🚀 进程一般状态
Linux内核源代码:
-
R:指的运行就绪状态,也称为“运行中”状态。在R状态下,进程正在占用CPU资源并执行任务,处于活动状态。
-
S:指的是可中断睡眠状态。在S状态下,进程暂时停止运行,等待某些事件的发生。这些事件可能是IO操作(如等待文件读写的完成、等待网络数据的传输等),也可能是等待其他进程的信号或者定时器等。
-
D:指的不可中断睡状态,也被称为“休眠”或者“阻塞”状态。在D状态下,进程正在等待着一些事件的发生,这些事件一旦发生,进程就可以被唤醒并继续执行。和S状态不同的是,在D状态下,进程无法被中断,即使收到了终端信号都不会有反应。进入D状态的原因往往是进程在等待一些不可中断的IO操作的完成,如等待硬盘数据的读写、发送网络数据等。因为这些操作必须等待硬件设备的响应,所以即使进程收到终端信号也不能被中断。
-
T:指的是进程被暂停或等待某些事件的状态,也被称为停止状态。当进程收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU等信号时,它会进入到T状态。此外,如果一个进程在等待某个事件(如等待I/O操作完成或等待子进程结束),也会进入到T状态。
-
t:指的是进程被跟踪或调试的状态。这种状态只在特定情况下才会出现,如使用gdb工具进行调试时。
-
Z:指的是僵尸状态。 当一个进程的执行已经完成(即进程已经“死亡”),但是其父进程尚未从操作系统中获取其退出状态时,该进程就成为了“僵尸进程”。在这种情况下,内核将进程状态更改为“僵尸进程”,以表明它已经死亡,但仍然存在于进程表中,以便其父进程在适当的时间获得其退出状态。
-
X:死亡状态。
-
P:指的是进程被挂起但仍然占有内存的状态。当进程因某种原因无法继续执行时,操作系统会将其挂起至P状态,并且将其从调度队列中移除,这样其他进程就能够利用CPU和其他系统资源。当有某种事件或信号到达时,操作系统会重新将进程唤醒,将其恢复至运行状态。
-
I:指的中断状态。当CPU接收到硬件或软件的中断请求时,会进入I状态,然后开始执行中断处理程序。中断可以来自于外部设备(如键盘、鼠标、打印机等),或由CPU自身的指令或异常引起。在I状态下,CPU会停止执行当前的任务,并转而去执行中断处理程序,处理完毕后再返回之前的任务。
🚀 Linux僵尸进程
这里是介绍僵尸进程及其危害,这是一个问题,但在此处不讲解如何解决。如果感兴趣的读者可以阅读笔者的进程控制文章,在那里僵尸进程会得到解决。
-
僵尸进程的定义:
僵尸进程指的是子进程先于父进程退出,且父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。 -
僵尸进程的危害:
僵尸进程需要占用系统中的进程表项和其他资源,可能会导致系统资源的浪费和耗尽,进而影响系统的稳定性和性能,甚至导致系统崩溃。僵尸进程如果不得到释放会造成内存泄漏。此外,如果系统中有大量僵尸进程存在,还会增加系统运维人员的工作量,降低系统的可维护性。因此,及时清理僵尸进程,释放系统资源,提高系统的稳定性和性能是很重要的。 -
见识僵尸进程:
#include<iostream>
#include<cstdio>
#include<unistd.h>
#include<cstdlib>
int main()
{
int a = 0;
pid_t id = fork();
if(id == 0)
{
while(true)
{
printf("I am child process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
//exit(1);
}
}
while(true)
{
printf("I am parent process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
//exit(-1);
}
return 0;
}
🚀 Linux孤儿进程
-
孤儿进程的概念:
孤儿进程指的是父进程先于子进程退出,但子进程还在运行。那么这些子进程会被pid为1的进程init(systemd)所收养,进而成为孤儿进程。
-
见识孤儿进程:
#include<iostream>
#include<cstdio>
#include<unistd.h>
#include<cstdlib>
int main()
{
int a = 0;
pid_t id = fork();
if(id == 0)
{
while(true)
{
printf("I am child process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
//exit(1);
}
}
while(true)
{
printf("I am parent process, pid:%d, ppid:%d\n",getpid(),getppid());
sleep(1);
//exit(-1);
}
return 0;
}
孤儿进程被init收养后会从前台进程变为后台进程。
🏠 进程优先级
🚀优先级查看
进程优先级本质上就是一个整数,数字越小,优先级越高。
查看优先级的指令是:ps -al
🚀优先级更改
在Linux中,进程的优先级是可以允许修改的。
新的优先级 = 老的优先级 + nice值。Linux中就是允许修改nice值,进而对优先级进行修改。注意:这里的老的优先级默认一直是80。nice值的取值范围是[-20,19].
修改nice值的方法操作是:
- 先输入top命令。
- 进入top后按“r”–>输入进程PID–>输入nice值。
操作方式截图如下: