1. 阻塞
历程由于等待某种条件就绪,而导致的一种不推进的状态
1. 举例
- 偶然候电脑卡,是由于开启了太多软件,为什么启动太多程序会卡呢?
- 启动了太多的程序相称于启动了太多的历程,操作体系在用你的cpu调度时,调度不外来了,当前正在调度的在运行,没有调度的相称于卡了
- 阻塞就是历程卡住了
2. 为什么要阻塞?
- 历程要通过等待的方式,等详细的资源被别人使用完成后,再被自己使用
- 阻塞:历程等待某种资源就绪的过程
- 以硬件为例,资源可以看作磁盘、网卡、显卡等外设,
比如去银行存钱,存钱申请的单子没了,直接去存,工作职员就会说由于存钱的单子没有就绪所以不能存,再去等一会,等有单子了再来存
- 没有继承执行存钱的行为,当前所处状态为阻塞状态
3.操作体系层面上怎样理解历程等待某种资源就绪呢?
资源
操作体系对于磁盘、网卡、显卡等 资源通过 先描述,在组织进行管理,把设备用结构体描述起来,再用链表组织起来
管理的本质详细解释点击这里
历程
存在大量的历程,操作体系要进行管理,也要先描述,在组织,就存在了大量的task_struct的结构体,每一个历程都是由task_struct定义出来的对象
历程组成的理解点击这里
- 将dev结构体内添加一个队列指针,若历程必要在设备资源等待,则把task_struct链接到设备资源队列的尾部
4. 总结
阻塞:阻塞就是不被调度
肯定是由于当进步程等待某种资源就绪
肯定是历程task_struct结构体必要在某种操作体系管理的资源下进行排队
2.挂起
- 当历程被CPU调度时进行下载,由于网断了,导致下载停止,从而使历程链接到网卡设备队列的尾部,造成阻塞
从而使CPU调用其他历程
- 若体系中内存的资源特别紧张,把占据内存的并且闲置的数据和代码,互换到磁盘当中,把内存中这部门的代码和数据释放,当该历程等待的资源就绪时,再把存入磁盘的数据和代码换入内存中,并把历程放入CPU中运行
- 把代码和数据暂时性由操作体系互换到磁盘时,此时的历程称之为挂起状态
3.Linux历程状态
- 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 */
- };
复制代码
- struct task_struct
- {
- int status;//0代表R状态 1代表s状态 4代表T状态
- //....
- }
复制代码 task_struct 是一个结构体,内部会包罗各种属性,就有状态,通过0、1、2等数字找到对应的历程状态
1. R状态
历程只要是R状态,就肯定是在CPU运行吗?
不肯定
- 每个历程当中有自己的运行队列,只要该历程在队列中进行排队,运行的历程就会在运行的队列中排队,CPU调度历程时,从队列中挑选指定的历程运行就可以了,这种运行状态就叫做R状态
- R状态不直接代表历程在运行,代表该历程在运行队列中排队
证明当进步程运行状态
生成程序
创建makefile并输入如下内容
- 1 mytest:test.c//nakefile
- 2 gcc -o mytest test.c
- 3 .PHONY:clean
- 4 clean:
- 5 rm -f mytest
复制代码 创建test.c并输入如下内容
- #include<stdio.h>//test.c
- 2 int main()
- 3 {
- 4 while(1)
- 5 {
- 6 printf("我在运行吗\n");
- 7 }
- 8 return 0;
- 9 }
复制代码
- [yzq@VM-8-8-centos my]$ make
- gcc -o mytest test.c
- [yzq@VM-8-8-centos my]$ ./mytest
复制代码 make产生可执行程序mytest,./mytest执行可执行程序
查察历程
赋值SSH渠道生成终端2
在保证终端1中的mytest运行的情况下,在终端2中输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep
创建终端的方法及指令详细含义点击这里
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 30533 30533 25615 pts/0 30533 S+ 1002 0:00 ./mytest
复制代码 发现此时历程状态为S+ (+号后面会说)
S称为休眠状态,不是R状态
test.c代码修改如下
- #include<stdio.h>//test.c
- 2 int main()
- 3 {
- 4 while(1)
- 5 {
- 6 // printf("我在运行吗\n");
- 7 }
- 8 return 0;
- 9 }
-
复制代码 在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 31695 31695 25615 pts/0 31695 R+ 1002 0:10 ./mytest
复制代码
- 发现历程状态由S+变为R+
- printf循环打印,就代表要频繁访问显示器设备,循环中的printf本质就是向外设打印消息,当CPU执行printf代码时,频繁打印外设不肯定就绪,历程可能在外设中排队,等资源就绪时,再把历程放入CPU上把效果写入外设中
- 分析第一次查到的S状态是阻塞状态的一种,是以休眠状态进行阻塞的
2. S休眠状态——可停止休眠
本质是一种阻塞状态
修改test.c代码如下
- 1 #include<stdio.h>
- 2 int main()
- 3 {
- 4 while(1)
- 5 {
- 6 int b=0;
- 7 scanf("%d",&b);
- 8 printf("%d\n",b);
- 9 }
- 10 return 0;
- 11 }
复制代码
在保证终端1中的mytest运行的情况下,在终端2中再次输入指令
ps axj | head -1 && ps axj | grep mytest | grep -v grep
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 5793 5793 25615 pts/0 5793 S+ 1002 0:00 ./mytest
复制代码
- 历程状态为S,历程没有被调度,等键盘资源 即在键盘中输入数据
- [yzq@VM-8-8-centos my]$ ./mytest
- 10
- 10
- ^C//可以被终止
复制代码
- 在终端1中ctrl c,停止mytest运行,S状态被停止
3.D休眠状态 ——不可停止休眠
想要往磁盘写入100MB的数据,由于磁盘写入数据很慢,所以历程把自己设置成阻塞状态,若内存资源特别紧张,操作体系就想要把这个阻塞状态的历程干掉,可是此时磁盘依旧还在写入数据,如许做就会使磁盘写入数据失败,终极就会使100MB的数据丢失
- 若该历程的休眠状态为D状态,使操作体系无法干掉历程,就能解决这个问题
4.T状态——暂停状态
使用kill下令,向指定的历程发信号
- [yzq@VM-8-8-centos my]$ 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
复制代码
- 9号信号SIGKILL 表现干掉 历程
- 18号信号 SIGCONT 表现让当进步程继承运行
- 19号信号 SIGSTOP 表现暂停一个历程
暂停历程
test.c内容如下
- 1 #include<stdio.h>
- #include<unistd.h>
- 2 int main()
- 3 {
- 4 int count=0;
- 5 while(1)
- 6 {
- 7 printf("还在运行吗:%d\n",count++);
- sleep(1);
- 8 }
- 9 return 0;
- 10 }
复制代码 保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep ,并输入 kill-19+PID值,终端1自动停止运行
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 14791 14791 25615 pts/0 14791 S+ 1002 0:00 ./mytest
复制代码 再次在终端2中查询历程,发现历程状态由S+变为T
继承历程
若想让历程继承运行,在终端2中使用 kill -18 +PID值
带加号的问题
- yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 14791 14791 25615 pts/0 25615 S 1002 0:00 ./mytest
复制代码
- 在暂停历程,又继承历程后,当进步程状态 为S,不带加号了
- 在终端1使用ctrl c,程序不停止了
- 历程状态带+,历程是在前台运行的,可以使用ctrl c 停止
- 历程状态不带+,历程是在背景运行的,可以正常执行shell指令,但在背景继承还会执行自己的代码
- 此时若想停止历程,使用 kill -9 +PID值 干掉历程
5. X状态(死亡状态)&&Z状态(僵尸状态)
- X死亡状态只是一个返回状态,你不会在任务列表里看到这个状态,所以这里这是举例时提及,但不会验证
- 我们创建历程,是为了历程帮我们服务,同时也关心效果,而main函数的返回值是历程的退出码
查察当进步程退出码
用于判定历程效果是否正确 echo $?
修改test.c的内容如下
- include<stdio.h>
- 2 #include<unistd.h>
- 3 int main()
- 4 {
- 5 int count=2;
- 6 if(count==2)
- 7 {
- 8 return 0;
- 9 }
- 10 else
- 11 {
- 12 return 4;
- 13 }
- 14 }
- ~
复制代码 使用make生成可执行程序mytest,./mytest执行可执行程序
- [yzq@VM-8-8-centos my]$ make
- gcc -o mytest test.c
- [yzq@VM-8-8-centos my]$ ./mytest
- [yzq@VM-8-8-centos my]$ echo $?0
复制代码
对于Z状态的理解
假如一个历程退出了,立马X状态,立马退出,你作为父历程,有没偶然机拿到退出效果呢?
- linux当历程退出的时候,一般历程不会立即彻底退出,而是要维持一个状态叫做Z状态,也叫做僵尸状态
- 方便后续父历程读取子历程退出的退出效果
怎样让我们看到僵尸状态呢?
举例
- 假设你在某一天看到路上有一个人躺着,你报了警,警员来了后,先封锁现场,再来人确认躺者是否死亡,法医确认这个人的死因,然后关照家属等一系列事情才能进行
- 被120、法医检查时,这个人所处的状态就叫僵尸状态
- 为了方便别人从他的残留信息中甄别他死亡的原因是什么
验证Z状态
修改test.c内容如下
- #include<stdio.h>
- 2 #include<unistd.h>
- 3 int main()
- 4 {
- 5 pid_t id=fork();
- 6 if(id==0)
- 7 {
- 8 //子进程
- 9 while(1)
- 10 {
- 11 printf("我是子进程,我在运行,pid:%d,ppid:%d",getpid(),getppid());
- 12 sleep(1);
- 13 }
- 14 }
- 15 else if(id>0)
- 16 {
- 17 //父进程
- 18 while(1)
- 19 {
- 20 printf("我是父进程,我在运行,pid:%d,ppid:%d",getpid(),getppid());
- 21 sleep(1);
- 22 }
- 23 }
- }
复制代码 保证在保证终端1中的mytest运行的情况下,在终端2中再次输入指令ps axj | head -1 && ps axj | grep mytest | grep -v grep
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 18251 18251 25615 pts/0 18251 S+ 1002 0:00 ./mytest
- 18251 18252 18251 25615 pts/0 18251 S+ 1002 0:00 ./mytest
- [yzq@VM-8-8-centos my]$ kill -9 18252
- [yzq@VM-8-8-centos my]$ ps axj | head -1 && ps axj | grep mytest | grep -v grep
- PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND
- 25615 18251 18251 25615 pts/0 18251 S+ 1002 0:00 ./mytest
- 18251 18252 18251 25615 pts/0 18251 Z+ 1002 0:00 [mytest] <defunct>
复制代码
- 当父子历程都运行时,两者历程状态都为S+
- 当使用 kill - 9 +PID值将子历程干掉后,再次使用指令查询历程,发现子历程为僵尸状态,父历程为S+
僵尸状态危害
- 在父子历程中,若父历程不停不读取,子历程就会处于Z状态
- 在父子历程中,若子历程不停处于僵尸状态(即退出子历程,但不采取子历程),就会在内存中不停生存该历程
- 若父历程创建多个子历程,就是不采取,就会造成内存资源的浪费
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |