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 */
};
在Linux中,可以将进程分为前台进程和后台进程,它们的区别在于与终端的交互方式和执行状态。
+
+
bg
将一个前台进程转为后台进程,或者使用命令jobs
查看当前所有的作业(包括前台和后台)。kill 进程ID
命令关闭对应的后台进程,比如kill 1234
运行状态(Running)是进程可以被调度执行的状态。当一个进程处于运行状态时,它的代码正在被 CPU 执行,即正在运行指令并处理各种任务。在 Linux 中,通常用
R
表示进程处于运行状态。
int main()
{
while (1)
{
;
}
return 0;
}
就是我们上次学习的阻塞状态,在Linux中,进程的睡眠状态(Sleeping)是指进程因等待某些事件而暂时停止执行。这个状态有时也被称为可中断睡眠(Interruptible Sleep),因为进程在这种状态下可以被中断,例如通过接收信号来唤醒。 睡眠状态的进程通常在等待某些事件的完成,例如:
需要注意的是,睡眠状态的进程是可以被中断的,也就是说,在等待事件的过程中,如果接收到一个信号,进程可能会被唤醒并处理该信号,之后可能会继续等待或执行其他操作。我们使用Ctrl+c
可以中断进程,因此,这种状态也称为可中断睡眠。
#include<stdio.h>
#include<unistd.h>
int main()
{
int a=0;
while(1)
{
printf("%d",a);
sleep(2);
}
return 0;
}
在这个程序中,主循环是一个无限循环
while(1)
,它不会主动放弃 CPU,因此进程会一直处于运行状态(R)。但是,在每次循环迭代中,程序会调用printf
函数打印a
的值,并使用sleep(2)
函数让进程休眠 2 秒。 在sleep(2)
调用期间,进程暂时停止执行,等待指定的时间结束后再继续执行。虽然进程在休眠期间处于不活动状态,但是它并没有主动释放 CPU。因此,进程会被标记为睡眠状态(S),表示它正在等待特定事件的完成,即等待sleep
函数定时器计时结束。
但是如果我们把sleep()
去掉后,会发现还是S
状态
在这样的程序中,主循环是一个无限循环
while(1)
,它不会主动放弃 CPU,因此进程会一直处于运行状态(R)。但是,由于printf
函数涉及输出操作,这可能会导致进程在等待标准输出设备的 I/O 操作完成时陷入睡眠状态(S)。 当程序运行时,printf
函数将数据输出到标准输出设备(通常是终端),并且在数据传输过程中,可能需要等待设备的响应。在这段等待期间,进程暂时停止执行,处于睡眠状态。因此,即使主循环一直在运行,但是由于进程在某些时刻需要等待设备响应,因此会被标记为睡眠状态(S)。(CPU执行是很快的)
也是阻塞状态。D磁盘休眠状态(Disk sleep)是Linux系统中的一种进程状态,有时也称为不可中断睡眠状态(uninterruptible sleep)。进程进入这种状态通常是因为正在等待某些IO操作的完成,比如磁盘读写操作,网络请求等。在D状态下的进程是无法被中断或者唤醒的,直到IO操作完成为止。
也可以理解磁盘休眠状态的进程是有免死金牌的,能防止CPU因为资源不足而删除这个正在等待的进程 因此,即使系统资源紧张或CPU负载高,磁盘休眠状态下的进程仍然会被系统保留,不会被强制删除。这种机制确保了IO操作的完整性和系统的稳定性。
在Linux系统中,当一个进程接收到
SIGSTOP
信号时,它会被暂停(停止)执行,进入停止状态。在这种状态下,进程的执行被暂时挂起,不会继续执行,也不会被调度到CPU上运行。 停止状态下的进程不会消耗CPU资源,也不会响应任何信号,直到接收到SIGCONT
信号后才会继续执行。停止状态的进程可以通过ps
命令或者类似的工具查看,通常会显示为T
状态。 要将一个进程从停止状态恢复到运行状态,可以向该进程发送SIGCONT
信号。这样进程就会从停止状态恢复到运行状态,继续执行
在Linux系统中,kill
指令用于向进程发送信号。通过kill
指令,可以向指定的进程发送不同的信号,从而影响进程的行为。常用的kill
指令格式如下:
kill [options] <PID>
其中,<PID>
是要发送信号的进程的进程ID(Process ID)。可以使用ps
指令或者pgrep
指令来查找进程的进程ID。
-9
:发送SIGKILL信号,强制终止进程。
-15
(或不加选项):发送SIGTERM信号,请求进程正常终止。
kill -l
是用来列出系统支持的信号列表的命令
-19
SIGSTOP
(编号为19):发送SIGSTOP
信号会使进程停止执行,进程将被挂起,直到接收到SIGCONT
信号继续执行。SIGSTOP
信号不能被捕获、忽略或阻塞,是一种强制停止进程的信号。
-18
SIGCONT
(编号为18):发送SIGCONT
信号会使之前被停止的进程继续执行。这个信号用于恢复被SIGSTOP
或者类似信号暂停的进程的执行。
对应的就是我们之前讲解的终止状态
在Linux系统中,"死亡状态(dead)"通常指的是进程已经终止(terminated)并且退出,但其进程描述符(process descriptor)还未被释放。这种状态通常在进程终止后,其父进程还未对其进行处理或回收资源时出现。 当一个进程终止后,其进程描述符会保留一段时间,直到父进程调用
wait()
或waitpid()
等系统调用来回收子进程的资源。在这段时间内,进程的状态会被标记为"死亡状态(dead)"。一旦父进程回收了子进程的资源,进程描述符就会被释放,进程完全被清除。 因为是一个瞬间的动作,我们很难看到该状态
在Linux系统中,当一个进程终止后,其进程描述符==(PCB并不会立即被释放)。此时,该进程会变成一个僵尸进程(Zombie Process)。僵尸进程是指一个进程已经终止==,但其父进程还未对其进行处理或回收资源。 在Linux系统中,当父进程读取了子进程的退出状态后,子进程的状态会从僵尸状态(Zombie)变为终止状态(Terminated),通常用X表示。这意味着父进程已经处理了子进程的退出状态信息,并且子进程的资源已经被释放,不再占用系统资源。因此,及时处理子进程的退出状态是非常重要的,可以避免僵尸进程的积累,提高系统的稳定性和性能。 而bash会自动读取子进程的退出状态
孤儿进程是指父进程先于子进程结束而结束,导致子进程成为孤儿进程。在Linux系统中,孤儿进程会被init进程(进程ID为1的进程)接管。当父进程先于子进程结束时,子进程的父进程ID会被修改为1,即init进程的进程ID,这样子进程就成为了孤儿进程。
孤儿进程的产生通常发生在父进程没有等待子进程结束就提前结束的情况下。为了避免产生孤儿进程,父进程在创建子进程后应该等待子进程结束,并及时处理子进程的终止状态。这样可以确保子进程在父进程结束时能够正常退出,而不会成为孤儿进程。
task_struct
{
//...
int PRI;
int nice;
//...
};
ps -al
-a
选项:显示所有进程,包括其他用户的进程。默认情况下,ps
命令只显示当前用户的进程,使用 -a
选项可以显示所有用户的进程。
-l
选项:以长格式显示进程信息。长格式包括更多的字段,如进程状态、进程 ID、父进程 ID、优先级、CPU 使用情况、内存使用情况等。
nice
值的确是影响进程优先级的修正因子,通过调整 nice
值,可以间接地影响进程的优先级,从而影响其在CPU上的执行顺序。为什么要有-20到19的这个限制?
top:进入top后按“r”–>输入进程PID–>输入nice值 使用
top
命令可以实时监视系统的运行情况,包括进程的资源占用情况、进程列表等。按下r
键后,可以对指定进程的优先级进行调整。 在按下r
键后,按照提示输入要调整优先级的进程的PID,然后输入要为其设置的新的nice
值。输入完毕后,top
将会尝试修改指定进程的优先级,根据新的nice
值重新计算其优先级。这样可以实现对指定进程执行优先级的调整。
现代操作系统采用时间片轮转的方式来调度进程执行,而不是等待一个进程的代码完全执行完毕后再切换到下一个进程。这种方式能够实现多任务的并发执行,提高系统的响应速度和资源利用率。
进程的切换与调度是操作系统中非常重要的部分,它涉及到如何有效地利用CPU资源,保证系统的响应速度和吞吐量。
进程切换指的是从一个正在执行的进程切换到另一个进程的过程。当操作系统决定将CPU的控制权从当前进程转移到另一个进程时,就需要进行进程切换。进程切换包括以下几个关键步骤:
进程调度是操作系统根据一定的调度策略从就绪队列中选择下一个要执行的进程的过程。调度策略的选择会影响系统的性能、响应速度和资源利用率。
queue[140]
:这个数组用于存储不同优先级的进程队列。每个队列按照先进先出(FIFO)规则进行排队调度。数组的下标表示进程的优先级,因此可以直接根据优先级来访问对应的进程队列,提高了访问效率。
bitmap[5]
:为了快速判断哪些队列是非空的,使用了一个位图来表示每个队列的状态。每个比特位对应一个队列,如果该队列非空,则对应的比特位为1;否则为0。这样,查找非空队列的操作变得高效,时间复杂度为常数级别。
活动队列
过期队列
好啦,进程相关的知识,也大概梳理完毕了。