个人Linux操作系统学习笔记8 - 进程状态与僵尸进程
进程状态
操作系统教材中的状态的说明
运行,阻塞,挂起
struct task_struct
{
int state;
}

具体操作系统的状态(Linux)
一个CPU,一个调度队列
1.什么叫运行状态

2.什么叫阻塞状态
#include <stdio.h>
int main()
{
int a = 0;
scanf("%d", &a);
printf("a = %d\n", a);
return 0;
}
在这个进程运行时,如果不输入内容,scanf就会一直卡住进程,进程无法继续运行,这就是阻塞状态。
那么,scanf在等什么?
在等键盘输入,即——在等硬件就绪!
硬件是否就绪,哪个软件会最先知道?
操作系统!因为操作系统是硬件的管理者。
操作系统如何管理硬件?
先描述,再组织!
struct device
{
// 硬件的所有属性
struct device* next;
struct task_struct* wait_queue;
}
使用类似于task_struct一样的链表结构
挂起
磁盘中有swap分区
操作系统在内存不足时,会把阻塞状态的进程放到磁盘的swap分区中
数据和代码进入磁盘swap分区叫swap out
数据和代码进入内存叫swap in
task_struct依旧在内存中,因为内存资源不足,导致进程对应的数据或代码被交换到swap分区(磁盘),这个状态叫挂起!
将阻塞状态的进程挂起,叫阻塞挂起
将运行状态的进程挂起,叫运行挂起
举Linux系统为例
$ ps ajx
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
在查看进程时,STAT显示的是查询时进程的状态
(带IO的程序很容易查出S)
R: Running(运行状态)
运行状态
S: Sleeping(阻塞状态)
休眠状态,可被中断休眠(浅度睡眠)
可以使用kill命令杀掉进程
kill -9 [pid]
-9表示信号
前台进程
当一个进程在运行时,无论输入什么,shell都不再做解释,意味着这是一个前台进程
前台进程的STAT状态后面有一个+号
后台进程
使用&运行后台进程
./myprocess &
此时会发现STAT没有+号
后台进程无法使用Ctrl + C杀掉,只能使用发送kill的方式杀掉
D: disk sleep(Linux特有的状态)
不可被中断的休眠状态(深度睡眠)
当一个进程在访问磁盘IO时,不能被OS中断
T: Stopped(暂停状态)
使用kill命令中断进程
# 中断
kill -19 [pid]
# 继续
kill -18 [pid]
补充:kill命令发出的不同信号
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
前后台进程的最大区别是:前台进程可以获取键盘输入!
如果后台进程试图从键盘中获取输入,就会被暂停
t: tracing stop(追踪暂停)
使用gdb进行调试的本质就是启动一个子进程
$ gdb myprocess
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/code/myprocess...done.
(gdb) l
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4
5 int main()
6 {
7 while(1)
8 {
9 printf("process: %d\n", getpid());
10 sleep(1);
(gdb) b 8
Breakpoint 1 at 0x4005b1: file myprocess.c, line 8.
(gdb) r
Starting program: /home/code/myprocess
Breakpoint 1, main () at myprocess.c:9
9 printf("process: %d\n", getpid());
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
其中,在b 8后r,这个子进程就处于t状态
所以gdb的本质就是不断发送-18和-19信号调控进程的运行与暂停
X: dead(死亡状态)
进程会被操作系统释放掉
Z: zombie(僵尸状态)
指的是只保留进程的task_struct,用于让父进程或OS获取子进程的退出数据的状态
在X状态之前会先处于Z状态
进程要释放时,会先处于僵尸状态,让操作系统采集信息
随后再设置为死亡状态被操作系统清除
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
// 使用kill命令杀死子进程,并让父进程一直死循环,即可观察到僵尸状态
printf("father process: %d\n", getpid());
sleep(3);
pid_t id = fork();
if(id == 0)
{
// child
while(1)
{
printf("child process: %d, father is %d\n", getpid(), getppid());
sleep(1);
}
}
else
{
// father
while(1)
{
printf("father process: %d\n", getpid());
sleep(1);
}
}
return 0;
}
可以使用以下命令持续性观察:
while :; do ps ajx | head -1 && ps ajx | grep myprocess; sleep 1 ; done
1.进程退出了,退出信息是什么?
main函数的返回值或者进程运行时收到的信号值
2.进程退出了,退出信息保存在哪里
保存在进程自己的task_struct结构体中
long exit_state;
int exit_code, exit_signal;
3.检测Z状态进程,回收Z状态进程,本质是在做什么?
本质是在检查获取问题2中的数据
4.具体怎么回收?谁来回收?(简单提一下)
一般由父进程回收通过系统调用来回收
使用waitpid进程等待
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
如果父进程一直不回收呢?
那么这个进程永远处于Z状态,一直维持它的pcb,task_struct不会释放,这块内存永远被占用,引发内存泄漏!
如果对应的进程结束了,内存泄漏问题还在吗?
不在。进程结束后,代码的泄露的内存会被OS回收。
但是,常驻进程一定要注意内存泄露!
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)