目录
前言:
正文:
1.R运行状态(running)
2.睡眠状态(sleeping)
3.D磁盘休眠状态(Disk sleep)
4停止状态(stop)
5僵尸状态(Z)
6 孤儿进程
补充知识:前后台进程
前言:
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在linux内核里,进程有时候也叫任务)
下面的状态在kernel源码中的定义:
/** 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): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
- S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠 (interruptible sleep))。
- D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的 进程通常会等待IO的结束。
- T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可 以通过发送 SIGCONT 信号让进程继续运行。
- X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态
正文:
1.R运行状态(running)
运行态具体是指:当操作系统把CPU资源分配给进程时(cup对进程进行调度),进程会在运行队列里面排队(运行状态)在一个时间段内,所有的进程都会被执行,并发执行。在运行状态下,进程会执行它所分配的任务。当进程的时间片用完后,进程就会被挂起(带走自己的上下文),等待下一次CPU调度。
我们可以看一下在Linux下的R状态【查看进程状态的命令:ps ajx/aux 】。如下:
process.c
1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 int main()5 {6 pid_t id = fork();7 if(id < 0){ perror("fork");8 return 1;9 }10 else if(id == 0){11 // child12 printf("I am child, pid :[%d] ppid:[%d]\n", getpid(),getppid());13 sleep(20);14 15 }else{16 //parent17 printf("I am parent, pid: [%d] ppid:[%d]\n", getpid(),getppid());18 sleep(10);19 exit(0); 20 21 }22 return 0;23 }
Makefile
myprocess:process.c2 gcc -o myprocess process.c 3 4 .PHONY:clean5 clean:6 rm -r myprocess7 8 .PHONY:catP9 catP:10 ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep11
通过指令对进程状态进行查看
我们会发现不管是父进程还是子进程,我们看到的状态都是S+,这是因为对于cpu来说,将数据回显到显示器的任务在电光火石之间就完成了,我们很难捕捉到,所以我们大部分捕捉的动作他都是在等待队列中进行排队。
(R+是在运行队列中被操作系统管理这,也是在排队,他不一定被cpu调度)排队类似下图
为了捕捉,我们可以将打印语句屏蔽,让其一直死循环,这样我们就可以在调度的时间片内捕捉到其运行状态。
2.睡眠状态(sleeping)
睡眠状态是指:当进程被调用而等待某个事件的发生时,该进程就会进入睡眠状态。通俗来讲,当我们要完成某种任务的时候,任务条件不具备,需要进程进行某种等待。(也叫进程阻塞)
进程等待时,都是在等待CPU资源吗?答案是:并不是!
睡眠状态被认为是一种阻塞状态,因为该进程在等待发生的事件情况下不能真正地执行,所以CPU的资源也就会被阻塞。只有当该事件发生,进程才能被唤醒并继续执行。
补充:s+是一种前台进程,我们可以通过输入信号将其关闭,也就是键盘上的ctrl+c将进程杀死,(会在信号章节进行详细介绍)。
3.D磁盘休眠状态(Disk sleep)
D磁盘休眠状态(Disk sleep)表示进程因等待磁盘I/O操作时而被阻塞,并且进程等待的操作可能会很慢,也可能永远不会完成。当出现大量相同操作的时候,就会出现休眠状态。
例如有一个进程需要把数据写入磁盘,且数据量很大。而写入磁盘的过程相对来说并不是很快,进程需要等待一段时间。等待的过程中,同时也占用了CPU的资源, 我们都知道操作系统是不会浪费任何内存,所以他有杀死进程的功能。如果该进程写入的数据很重要,需要磁盘读取数据完成后的返回信息。但也可能会被误操作给删除。所以给他一个免死金牌D状态,该进程不可被操作系统终止。
在Linux系统中,有些进程可能预期会处于D状态,例如,磁盘I/O操作比较耗时的进程。而对于其他类型的进程,处于D状态通常是不正常的,需要进行诊断和解决。常用的解决方法包括重启系统、更换设备等。
4停止状态(stop)
T停止状态(stopped)表示进程已经被挂起,不再运行,但是它尚未完成或终止。进程可以进入停止状态是由于收到一个SIGSTOP、SIGTSTP、SIGTTIN或者SIGTTOU信号,以及调试器进程发送的SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGTRAP、SIGABRT、SIGBUS、SIGFPE、SIGSEGV、SIGPIPE、SIGALRM、SIGTERM、SIGURG、SIGXCPU、SIGXFSZ和SIGSTOP信号。
我们可以利用kill - 18 kill-19让进程 恢复或者暂停。
5僵尸状态(Z)
与死亡状态相对应的还有一个 僵尸 T 状态
通俗来说,僵尸状态 是给 父进程 准备的
当 子进程 被终止后,会先维持一个 僵尸 状态,方便 父进程 来读取到 子进程 的退出结果,然后再将 子进程 回收。
单纯的在 bash 环境下终止 子进程,是观察不到 僵尸状态 的,因为 bash 会执行回收机制,将 僵尸 回收。
- 我们可以利用
fork()
函数自己创建父子进程
关系,观察到这一现象
1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 int main()5 {6 pid_t id = fork();7 if(id < 0){ perror("fork");8 return 1;9 }10 else if(id == 0){11 12 // child 13 printf("I am child, pid :[%d] ppid:[%d]\n", getpid(),getppid());14 sleep(5);15 16 }else{17 //parent18 printf("I am parent, pid: [%d] ppid:[%d]\n", getpid(),getppid());19 sleep(20);20 exit(0);21 22 }23 return 0;24 }
~
~
~
6 孤儿进程
- 通过程序创建
父子进程
- 通过指令终止
父进程
,此时子进程
会被OS领养 子进程
的父进程
变为1号进程
子进程
就变成了一个孤儿进程
我们可以通过以下代码验证当父进程先退出的时候,子进程是不是被1号进程接管。
1 #include <stdio.h>2 #include <unistd.h>3 #include <stdlib.h>4 int main()5 {6 pid_t id = fork();7 if(id < 0){ perror("fork");8 return 1;9 }10 else if(id == 0){11 12 // child13 printf("I am child, pid :[%d] ppid:[%d]\n", getpid(),getppid());14 sleep(35);15 16 }else{17 //parent18 printf("I am parent, pid: [%d] ppid:[%d]\n", getpid(),getppid());19 sleep(5); 20 exit(0);21 22 }23 return 0;24 }
~
补充知识:前后台进程
Linux中运行的程序可以在前台(foreground)或者后台(background)运行。前台程序是在当前终端会话中运行的程序,而后台程序则是在系统内部运行的,没有与用户终端会话相关联。上述带 ‘+’ 号的就是在前台运行的程序,不带 ‘+’ 号的就是在后台运行的程序。
当用户在终端中输入运行一个程序的命令时,该程序默认以前台方式运行,它会在终端上打印输出并且用户必须等待程序执行完成。
如果在命令末尾加上 & 符号,就可以让程序在后台运行。具体如下图:
如果需要与后台程序交互,可以使用一些特殊的命令,如:jobs, fg等,来查看和控制程序的状态。 fg 指令就可吧后台进程转换到前台运行。例如:
$ jobs #查看后台运行的程序
$ fg %1 #将后台中的程序1转为前台运行