上篇文章我们说过的操作系统进程的概念等(Linux 操作系统 进程(1)-CSDN博客),这篇我们就来了解进程最重要最直观的两个状态 : 进程状态 进程优先级
进程状态
kill命令
我们在查询进程的时候就可以看到当前进程的状态 “ “+”号 在进程状态信息中表示该进程属于前台进程组,即与当前终端相关联的进程组。不加就代表在后台运行,我们怎么让自己的程序在后台运行呢?
//使用死循环,保证程序不会因为自己原因退出
#include<unistd.h>
#include<stdio.h>
int main()
{
while(1)
{
sleep(5);
}
return 0;
}
当我们直接运行时
符合我们预期,使用ps来查看他的进程状态
也就是说,我们直接运行程序,我们的程序都是在前台运行,当我们运行程序的时候加上 &
我们可以看到,程序给了我们一段数字,当我们使用ps查询时,
这段数字就是进程的PID,我们会发现,进程的运行状态后面没有" + "号了,这个进程不与终端交互,但是 ,我们该如何 " 杀死 " 这个进程呢,由上面死循环程序可以知道这个进程是不会自己退出的,Kill命令就可以做到,因为我们前面让程序在后台运行时,得到了程序的PID,我们可以直接 kill + PID
,直接杀死进程
我们同样可以使用man命令查询kill的其他用处,或者是 kill -l查询
进程状态在Linux系统中的定义
在Linux对PCB的定义中可以看到
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
...
}
state就是进程的运行状态,Linux中定义了表示各种状态的宏
#define TASK_RUNNING 0x0000 // 运行态
#define TASK_INTERRUPTIBLE 0x0001 // 可中断的睡眠态
#define TASK_UNINTERRUPTIBLE 0x0002 // 不可中断的睡眠态
#define TASK_STOPPED 0x0004 // 停止态
#define TASK_TRACED 0x0008 // 被跟踪态
#define EXIT_DEAD 0x0010 // 进程已经结束
#define EXIT_ZOMBIE 0x0020 // 僵尸进程
看着这些宏的定义,看起来跟我们学习的操作系统进程状态的转换并不一样,因为那个只是概念也就是理想的状态,当我们程序运行的时候,为了保护数据,维护进程IO,会多出来很多状态,用于操作系统处理,这些宏并不直观,我们可以使用这段代码直观解释
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
R(running) : 程序运行的状态或者说是程序处于运行队列时的状态,不必多说 。
X(dead) :程序死亡。
S(sleeping):
概念意义上的阻塞状态,等待资源的过程,但是这个S可以被杀死,这个等待是操作系统层面的,是等待操作系统的资源,比如我们可以让进程等待我们输入数据 scanf
其实,我们的进程大多数时间都是在等待资源,cpu运行速度太快,我们很难能观察到运行状态,所以我们很多时候查看进程都是S状态。
D(disk sleep)
等待硬件上的IO,或者说是等待硬件的资源,这个等待无法被打断,防止存储的数据丢失,操作系统在运行的时候,资源不够的情况下会把一部分处于等待状态的进程" 杀掉 " (杀后台),用来给正在运行的程序或者说是优先级较高的程序分配资源,如果我们的进程正在与硬件进行IO,这时候被操作系统或者说kill命令" 杀掉 ",很大可能导致数据的丢失,而D状态就是为了避免这时候被操作系统" 杀掉 "
T (stopped)
阻塞状态,暂停进程,我们可以通过 kill 命令对其实现暂停
t 状态是程序调试的时候,现在一般理解与T,都是停止状体
Z(zombie)
僵尸进程,当子进程退出时,父进程不对子进程退出的状态进行接收,那么子进程就处于僵尸进程状态,维护退出状态需要数据维护,也属于进程信息,就保存在进程PCB里,如果进程一直不退出,这段PCB就需要操作系统一直使用资源维护,也就是资源浪费,那么父进程创建很多子进程但是不回收,就会浪费很多系统资源,就类似于指针指向的空间不释放,或者说是文件一直被打开,就是属于内存泄漏,这就需要父进程去接受子进程的退出信息。
孤儿进程
也就是父进程比子进程先退出,那么就需要进程去接收这个子进程的退出信息,那么当这个进程退出时,就会被其他父进程(也就是1号进程)领养,退出信息也就被这个进程回收
#include<unistd.h>
#include<stdio.h>
int main()
{
pid_t id = fork();
if(id<0)
{
perror("进程创建失败");
return 1;
}
else if(id==0)//子进程
{
sleep(10);
}
else{
sleep(5);//父进程
}
return 0;
}
当父进程先退出后,子进程的PPID就变成了 1 号进程
进程优先级
一个进程在cpu上分配资源的前后就是进程的优先级,那么也就是说进程优先级高的会先执行,那么跟操作系统有关的进程先执行,可以大大改善操作系统的性能
查看优先级
PRI:就是进程优先级,NI(nice)就是优先级修正值,也就是说PRI的值越小,进程优先级越高,进程会先被配资源,先执行,那么系统给我们修正值的原因就是我们的程序,也可以通过更改NI来控制部分程序的优先值,让我们认为重要的程序先执行,但是这需要root权限