最新消息: USBMI致力于为网友们分享Windows、安卓、IOS等主流手机系统相关的资讯以及评测、同时提供相关教程、应用、软件下载等服务。

【Linux】

互联网 admin 24浏览 0评论

【Linux】

目录

描述进程-PCB

状态理论

Linux内核源代码的描述

R运行状态与S睡眠状态:

前台进程与后台进程

D磁盘休眠状态: 

T停止状态

X死亡状态

Z(zombie)-僵尸进程

僵尸进程的危害

进程状态总结

孤儿进程

进程优先级

Linux具体的优先级做法

PRI and NI 

PRI vs NI

用top命令更改已存在进程的nice

其他概念

独立性

竞争性

并行

并发

时间片(每个进程在运行时都会有的)

抢占与出让

切换


描述进程-PCB

        进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,即描述进程的结构体。课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。 task_struct作为Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

状态理论

  • 新建:(字面意思)

        新建是刚创建的出来进程,并未取得操作系统的允许,不能运行,也就是初步具有了进程的相关数据结构(task_struct)。(其实,Linux内核种并不具备这样的状态,task_struct创建出来就是立马运行的,只不过是操作系统为了理论的更完善,而添加的一个理论。所以:不要新建认为是不就绪的状态。

  • 运行:

        我们口中常所一个进程处于运行状态,运行是CUP正在执行代码?(是错的理论)

        task_struct结构体在运行队列中排队,就叫做运行态。运行不全是代码数据正在跑,一个进程在队列中还没有被调度,可以理解为在等待调度,这个时候其也是运行状态。

  • 阻塞:

        等待非CPU资源就绪,就称作阻塞状态

         这个队列中的进程等待的不是CPU资源,所以就不能称作为一个运行的进程,这个时候的状态称作阻塞状态。

  • 挂起: 

        当内存不足的时候,操作系统通过适当的置换进程的代码和数据到磁盘,这个时候进程状态就叫做挂起。

         例如:有一个进程,因为自己等待的某些资源,又长时间不满足,可能未来还要等相当长的时间,但是此时内存都不够了,于是操作系统。会将其的代码和数据换出到磁盘。于是就会换到磁盘专门为其准备的分区,SWAP分区中,此时描述其的 task_struct 还处于内存中。

  • 挂起阻塞

        进程正在等待某种资源(处于阻塞状态),而正在等期的期间,正好内存又没有空间了,于是又需要挂起。于是既阻塞又挂起,即:挂起阻塞。

注:挂起和阻塞都与CPU没有关系。 

Linux内核源代码的描述

        为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

R运行状态与S睡眠状态:

        R:对应上面的运行态。

        S:对应上面的阻塞状态,可中断睡眠(随时随地可以将其的睡眠状态打断 )。

#include<stdio.h>
int main()
{while(1)printf("I am a process!\n");return 0;
}

#include<stdio.h>
int main()
{while(1);return 0;
}

        不能以自己的感受,理解CPU的运算速度。在我们看来很复杂的行为,其实对于CUP就几纳秒的时间。在冯诺依曼体系中,显示屏属于外设。当进程向显示器中打印的时候,显示器是需要等待的,在这一瞬间却是整个进程状态绝大部分,就是S状态。然后将向显示器打印的printf删除之后就是R状态了。

前台进程与后台进程

前台进程:

        前台的任务一启动,键盘输入的命令就是没有任何效果的,并且可以被CTRL + c(^c)终止的进程。

后台进程: 

         我们可以通过加一个&让其变为在后台进行。

命令:./可执行程序 &


使得该可执行程序由前台进程变为后台进程。

        当让其在后台进行时,命令是可以正常执行的,CTRL + c(^c)是没有办法终止其的。

        终止其的方式很多,此处涉及不深,就使用一个简单的方法:

命令:(管理员信号)kill -9 进程的pid


杀死(终止)该进程

D磁盘休眠状态: 

        D:睡眠状态,磁盘睡眠,深度睡眠,不可被中断,不可以被被动唤醒。

        此时进程A,需要做的是等待磁盘给予结果(是存储成功,还是存储失败),此时就是等待某种资源就绪,那么进程就是阻塞状态。

#问:那么,其在某个阻塞队列中等待,那是否可以将其看作睡眠状态?

假设:其在阻塞队列中等待,看作睡眠状态。

        如果,其为睡眠状态。那么,当服务器压力过大的时候,操作系统会通过一定的手段,杀掉一些进程,来节省空间。当内存的空间满了,但又需要空间进行后续操作,操作系统发现,进程A并未执行,且一直处于等待执行下占用空间,于是干脆直接就把进程A杀死。

        于是,此时进程A死亡,但是硬盘还在为进程A做事。硬盘的执行结果需告诉进程A,但是进程A死亡,却又只能对进程A报告。于是硬盘没办法,并且硬盘还有其它的事,不可能一直等着,于是将数据抛弃。此时数据就流失了,此时由于进行的进程A已死亡,操作的用户也就无法知晓运行的结果。

       :因为资源的等待,使得进程A是不得不处于阻塞状态,等待硬盘的结果,所以操作系统不能将进程A杀掉!此时进程A的状态就是D深度随眠,不可中断的睡眠。操作系统不能杀这个进程,只能等待这个进程自己醒来(醒来:得到磁盘的结果,成功、失败)。处于这个状态的时候,甚至利用管理员信号,9号(kill -9 +该进程id)都不能杀掉。

T停止状态

        T:暂停状态。并不是等待某种资源,只是单纯的暂停进程。

#include<stdio.h>
int main()
{while(1){printf("I am a process!\n");sleep(5);}return 0;
}

命令:(管理员信号)kill -19 进程的pid


让该进程别运行了,处于停止状态去。

命令:(管理员信号)kill -18 进程的pid


让该进程别停止了,继续运行。


        T停止状态标准的使用就有:调试(当我们在调试一个程序的时候,其就处于进程暂停)。

Note

        有些地方是有状态T与t,其实是一样的,都是暂停,只不过t是专门体现打断点的调试状态。(但是也有可能是两个T,并不是t,与源代码有关)

        打断点停下来的时候,本质不是程序停下来,而是叫做进程暂停。

X死亡状态

        dead终止(瞬时性:很难捕捉到)

#问:是不是终止了,就立马释放数据就完了吗?

       :并不是这样的,因为可能会有很多个程序同时终止。这个时候,操作系统也只能 “慢慢” 的来。所以,其实死亡状态就是为提供一个标志,短暂的保持这么一个标志让操作系统看到。即:标志我这个进程已经死了,你可以回收了。

Z(zombie)-僵尸进程

  • 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,于是子进程进入Z状态。

        一个进程已经退出,但是还不允许被操作系统释放,处于一个被检测的状态 - 僵尸状态。进行检测方一般是父进程或者操作系统。

        当父进程交给子进程完成一些事,而子进程完成就释放了,哪父进程如何知道子进程完成的如何,此时就需要僵尸进程,子进程已经退出,但是需要经过父进程的调查,查看所交代的事完成的如何。即:僵尸进程是为了维护该状态,为了让父进程和操作系统来进行回收。(回收:只有调查完了(读走数据了),才能由Z状态变为X状态,操作系统才可以释放)。

利用fork创建一个僵尸进程:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{pid_t id = fork();if(id<0){perror("fork");return 1;}else if (id == 0){while(1){printf("I am child process!\n");sleep(1);break;}exit(0);//让子进程直接终止}else{while(1){printf("I am parent process!\n");sleep(1);}}return 0;
}

        僵尸进程,并不是所有的代码与数据都不能被释放,而是代码与数据是可以被释放的,因为其是不会再调动实行了。而描述它的数据结构PCB是保留的。

        我们知道子进程结束退出了,而如果父进程又一直不进行回收的话,子进程又只是为了等待父进程的回收,这样子进程就无法释放,而子进程又占用空间,所以,这就是内存泄漏。所以我们必须想办法去回收。

僵尸进程的危害

  1. 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就会一直处于Z状态!
  2. 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct中,换句话说,就是Z状态一直不退出,task_struct一直都要维护!
  3. 一个父进程创建了很多子进程,如果就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,就与定义一个结构体变量(对象)一样,是要在内存的某个位置进行开辟空间!
  4. 会内存泄漏! 子进程结束退出了,而如果父进程又一直不进行回收的话,子进程又只是为了等待父进程的回收,这样子进程就无法释放,而子进程又占用空间,所以,这就是内存泄漏。我们必须想办法去回收!

进程状态总结

        就绪 (TASK_RUNNING)就是R(运行状态),唯一的就是占不占有CPU资源,如果占有CPU资源 ,即有可能因为某些原因,而导致进程进入浅睡眠(S)或深睡眠(D)。占有CPU资源时,可能暂停(TASK_STOPPED)(T),可以由暂停让其继续运行。然后退出,可能为僵死(TASK_ZOMBIE)(Z)没有回收它。

孤儿进程

  • 父进程如果提前退出,那么子进程后退出,进入Z之后。父进程却已经退出,无法对子进程进行回收。是否就会导致内存泄漏?即:子进程无人回收,子进程却又处于等待回收状态。

  • 其实:父进程先退出,子进程就称之为“孤儿进程”。孤儿进程被1号init进程领养,当然就以init进程回收了。

利用fork创建一个孤儿进程

        父进程退出,子进程还在,子进程就叫做孤儿进程!孤儿进程就会被领养,被1号进程领养(init,系统本身),领养的原因:未来子进程退出的时候,父进程早已不在,需要领养进程来进行回收。

         需要注意,当子进程的父进程退出后,如果子进程成为孤儿进程,那么它会从前台进程变为后台进程。使用kill -9 +id即可。

        如果子进程结束,是很难看到父进程为1的Z状态的进程。

进程优先级

#问为什么要有优先级?

        答:就是因为要确定谁先享受到资源,谁后享受到资源,而资源就是CPU。因为,CPU是有限的,而进程太多,需要通过某种方式争取资源,优先级就是主要的竞争方式。

#问:什么是优先级?

        答:确认是谁应该先获得某种资源,谁后获得,利用一些数据表明优先级,而除了需要表明优先级,还要调度器评判优先级。优先级是调度器调度的主要参考。

Linux具体的优先级做法

        优先级 = 老的优先级 + nice值。

在linux系统中,用ps –l命令则会类似输出以下几个内容:

命令:ps -l


查看当前所登录的会话中的相关的进程

#include<stdio.h>
int main()
{while(1)printf("hello world.\n");return 0;
}

PRI and NI 

  • PRI比较好理解,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。
  • NI,是nice值,其表示进程可被执行的优先级的修正数值。
  • PRI值越小越快被执行,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值。
  • nice其取值范围是 -20 至 19 ,一共 40 个级别。

PRI vs NI

  • 需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
  • 可以理解nice值是进程优先级的修正修正数据。

用top命令更改已存在进程的nice

1. top

(退出输入q,即可)

2. 进入top后按“r”–>输入进程PID–>输入nice值

        优先级 = 老的优先级(80:每次都是80,是默认的)+ nice值。即:每次设置优先级,都要从进程最开始的优先级开始设置。

对于优先级是非常不建议自行调:

  1. 一般很少牵扯到一个进程优先级的问题,大部分情况下,优先级是系统默认的。
  2. 优先级只有操作系统最清楚,自行改再多,也不一定能按照自己的期望那样。

        Linux下由于老的优先级默认为80,nice值[-20, 19],所以优先级的范围是[60, 99],就40个优先级,之所以是这个范围是因为:操作系统不是为了能快速的调用某些进程,它的目的是为了能够更加均衡的,让所有的进程在一个时间段内较为均衡的获取到某种资源,尤其是CPU资源。CPU的调度不是为了谁更优先,而是尽量雨露均沾。

        而如果将优先级差异的调整过大,就可能会有一些恶意进程,通过恶意的调整自己的优先级,而导致CPU在调动时总是先调度它。这样会导致其他进程长时间得不到调度,而导致的不公平问题。

其他概念

  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。(只要有一个CPU一定是并发)

独立性

        就如同电脑中的多个软件同时进行(玩QQ又玩微信等),不可能说QQ崩了,然后微信也就崩了,因为这是两个程序。(此处只说进程本身出问题。如果是进程除了问题,而导致的连锁反应,那与此是另一回事)一样的,如果一个进程退出了,是不会影响另一个进程的。多个进程咋子同时运行的时候,它们的代码与数据几乎是不会影响的,即便是有些资源会共享,但是也不会互相影响。包括父子进程,挂了谁,都不会影响到另一个。

竞争性

        系统当中进程的数目众多,而CPU的资源是非常少的,甚至只有一个,所以进程与进程之间,本质就是竞争的。竞争后操作系统就要合理的给进程分配资源,所以便有了优先级。所以进程是具有竞争性的。

并行

        现在我们平时使用的电脑是一个CPU,但是对于其他的大型服务器,是有多个CPU的:

        即:同时在任意时刻,同时可能有两个以上的进程在被调度运行,这样的情况就叫做并行。

并发

#问:进程调用的时候,是不是直到将一个进程的代码和数据跑完之后,才执行下一个?

        答:不是,也不可能是。不可能让进程永久占有CPU,不然黑掉一个计算机就会很简单,就打一个死循环就行了。将会永远执行这个死循环。

时间片(每个进程在运行时都会有的)

        就是将一个进程放在CPU上,给你一个时间,比如:10ms(假设),在10ms内如果代码跑完了,呢就退出;没跑完别跑了,不着急先下去在后排队,以会再上来运行。所以写一个死循环操作系统不会被改掉,其他进程也可以跑的好好的。

抢占与出让

抢占低优先级的让位置给高优先级的。

        按照10ms(假设)时间片,如果在10ms外先下去,10ms内就结束,上下一个进程。可是现在的操作系统是支持抢占的(现在很多主流操作系统都是支持的),如果,一个进程的优先级是80,到这个进程的时候时间片是10ms,当正在执行第5ms的时候,来了一个优先级更高的60。此时,操作系统发现了,直接将优先级80的抢占下来,让给优先级高的先跑。一会再把优先级80的放上去跑。

出让进程主动让出位置。放在队列的其他地方,等下次调度。

基本概念:CPU执行代码的时候,并不是将一个进程的代码拿上去就不拿下来了,而是基于时间片,基于抢占,基于出让。每个进程都要在特定的时间段内,较为均衡的占有CPU资源。

        即:CPU在不断的被调动的时候,虽然任何一个时刻都只有一个进程在CPU上跑,但是在一个时间段内多个进程的执行都在推进,这种情况下叫做并发。(本质上:取决于CPU内进行各种快速的切换)

切换

切换的本质:CPU内的寄存器是只有一份的,但是寄存器需要存储的临时数据(上下文)是多份的,分别对应不同的进程!

        我们知道,每一个进程的上下文是不一样的,寄存器只有一份,那么根据并发,为下一个进程让出位置,那

#问:上下文数据能被抛弃吗?

        :绝而对不可以!

        当进程A暂时被切下来的时候,需要进程A顺便带走直接的上下文数据!带走暂时保存数据的是为了下一次回来的时候,能够恢复上去,以此继续按照之前的逻辑继续向后运行,就如同没有中断过一样。

【Linux】

目录

描述进程-PCB

状态理论

Linux内核源代码的描述

R运行状态与S睡眠状态:

前台进程与后台进程

D磁盘休眠状态: 

T停止状态

X死亡状态

Z(zombie)-僵尸进程

僵尸进程的危害

进程状态总结

孤儿进程

进程优先级

Linux具体的优先级做法

PRI and NI 

PRI vs NI

用top命令更改已存在进程的nice

其他概念

独立性

竞争性

并行

并发

时间片(每个进程在运行时都会有的)

抢占与出让

切换


描述进程-PCB

        进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合,即描述进程的结构体。课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct。 task_struct作为Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息

状态理论

  • 新建:(字面意思)

        新建是刚创建的出来进程,并未取得操作系统的允许,不能运行,也就是初步具有了进程的相关数据结构(task_struct)。(其实,Linux内核种并不具备这样的状态,task_struct创建出来就是立马运行的,只不过是操作系统为了理论的更完善,而添加的一个理论。所以:不要新建认为是不就绪的状态。

  • 运行:

        我们口中常所一个进程处于运行状态,运行是CUP正在执行代码?(是错的理论)

        task_struct结构体在运行队列中排队,就叫做运行态。运行不全是代码数据正在跑,一个进程在队列中还没有被调度,可以理解为在等待调度,这个时候其也是运行状态。

  • 阻塞:

        等待非CPU资源就绪,就称作阻塞状态

         这个队列中的进程等待的不是CPU资源,所以就不能称作为一个运行的进程,这个时候的状态称作阻塞状态。

  • 挂起: 

        当内存不足的时候,操作系统通过适当的置换进程的代码和数据到磁盘,这个时候进程状态就叫做挂起。

         例如:有一个进程,因为自己等待的某些资源,又长时间不满足,可能未来还要等相当长的时间,但是此时内存都不够了,于是操作系统。会将其的代码和数据换出到磁盘。于是就会换到磁盘专门为其准备的分区,SWAP分区中,此时描述其的 task_struct 还处于内存中。

  • 挂起阻塞

        进程正在等待某种资源(处于阻塞状态),而正在等期的期间,正好内存又没有空间了,于是又需要挂起。于是既阻塞又挂起,即:挂起阻塞。

注:挂起和阻塞都与CPU没有关系。 

Linux内核源代码的描述

        为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在 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):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。

R运行状态与S睡眠状态:

        R:对应上面的运行态。

        S:对应上面的阻塞状态,可中断睡眠(随时随地可以将其的睡眠状态打断 )。

#include<stdio.h>
int main()
{while(1)printf("I am a process!\n");return 0;
}

#include<stdio.h>
int main()
{while(1);return 0;
}

        不能以自己的感受,理解CPU的运算速度。在我们看来很复杂的行为,其实对于CUP就几纳秒的时间。在冯诺依曼体系中,显示屏属于外设。当进程向显示器中打印的时候,显示器是需要等待的,在这一瞬间却是整个进程状态绝大部分,就是S状态。然后将向显示器打印的printf删除之后就是R状态了。

前台进程与后台进程

前台进程:

        前台的任务一启动,键盘输入的命令就是没有任何效果的,并且可以被CTRL + c(^c)终止的进程。

后台进程: 

         我们可以通过加一个&让其变为在后台进行。

命令:./可执行程序 &


使得该可执行程序由前台进程变为后台进程。

        当让其在后台进行时,命令是可以正常执行的,CTRL + c(^c)是没有办法终止其的。

        终止其的方式很多,此处涉及不深,就使用一个简单的方法:

命令:(管理员信号)kill -9 进程的pid


杀死(终止)该进程

D磁盘休眠状态: 

        D:睡眠状态,磁盘睡眠,深度睡眠,不可被中断,不可以被被动唤醒。

        此时进程A,需要做的是等待磁盘给予结果(是存储成功,还是存储失败),此时就是等待某种资源就绪,那么进程就是阻塞状态。

#问:那么,其在某个阻塞队列中等待,那是否可以将其看作睡眠状态?

假设:其在阻塞队列中等待,看作睡眠状态。

        如果,其为睡眠状态。那么,当服务器压力过大的时候,操作系统会通过一定的手段,杀掉一些进程,来节省空间。当内存的空间满了,但又需要空间进行后续操作,操作系统发现,进程A并未执行,且一直处于等待执行下占用空间,于是干脆直接就把进程A杀死。

        于是,此时进程A死亡,但是硬盘还在为进程A做事。硬盘的执行结果需告诉进程A,但是进程A死亡,却又只能对进程A报告。于是硬盘没办法,并且硬盘还有其它的事,不可能一直等着,于是将数据抛弃。此时数据就流失了,此时由于进行的进程A已死亡,操作的用户也就无法知晓运行的结果。

       :因为资源的等待,使得进程A是不得不处于阻塞状态,等待硬盘的结果,所以操作系统不能将进程A杀掉!此时进程A的状态就是D深度随眠,不可中断的睡眠。操作系统不能杀这个进程,只能等待这个进程自己醒来(醒来:得到磁盘的结果,成功、失败)。处于这个状态的时候,甚至利用管理员信号,9号(kill -9 +该进程id)都不能杀掉。

T停止状态

        T:暂停状态。并不是等待某种资源,只是单纯的暂停进程。

#include<stdio.h>
int main()
{while(1){printf("I am a process!\n");sleep(5);}return 0;
}

命令:(管理员信号)kill -19 进程的pid


让该进程别运行了,处于停止状态去。

命令:(管理员信号)kill -18 进程的pid


让该进程别停止了,继续运行。


        T停止状态标准的使用就有:调试(当我们在调试一个程序的时候,其就处于进程暂停)。

Note

        有些地方是有状态T与t,其实是一样的,都是暂停,只不过t是专门体现打断点的调试状态。(但是也有可能是两个T,并不是t,与源代码有关)

        打断点停下来的时候,本质不是程序停下来,而是叫做进程暂停。

X死亡状态

        dead终止(瞬时性:很难捕捉到)

#问:是不是终止了,就立马释放数据就完了吗?

       :并不是这样的,因为可能会有很多个程序同时终止。这个时候,操作系统也只能 “慢慢” 的来。所以,其实死亡状态就是为提供一个标志,短暂的保持这么一个标志让操作系统看到。即:标志我这个进程已经死了,你可以回收了。

Z(zombie)-僵尸进程

  • 没有读取到子进程退出的返回代码时就会产生僵死(尸)进程
  • 僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,于是子进程进入Z状态。

        一个进程已经退出,但是还不允许被操作系统释放,处于一个被检测的状态 - 僵尸状态。进行检测方一般是父进程或者操作系统。

        当父进程交给子进程完成一些事,而子进程完成就释放了,哪父进程如何知道子进程完成的如何,此时就需要僵尸进程,子进程已经退出,但是需要经过父进程的调查,查看所交代的事完成的如何。即:僵尸进程是为了维护该状态,为了让父进程和操作系统来进行回收。(回收:只有调查完了(读走数据了),才能由Z状态变为X状态,操作系统才可以释放)。

利用fork创建一个僵尸进程:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{pid_t id = fork();if(id<0){perror("fork");return 1;}else if (id == 0){while(1){printf("I am child process!\n");sleep(1);break;}exit(0);//让子进程直接终止}else{while(1){printf("I am parent process!\n");sleep(1);}}return 0;
}

        僵尸进程,并不是所有的代码与数据都不能被释放,而是代码与数据是可以被释放的,因为其是不会再调动实行了。而描述它的数据结构PCB是保留的。

        我们知道子进程结束退出了,而如果父进程又一直不进行回收的话,子进程又只是为了等待父进程的回收,这样子进程就无法释放,而子进程又占用空间,所以,这就是内存泄漏。所以我们必须想办法去回收。

僵尸进程的危害

  1. 进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就会一直处于Z状态!
  2. 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct中,换句话说,就是Z状态一直不退出,task_struct一直都要维护!
  3. 一个父进程创建了很多子进程,如果就是不回收,就会造成内存资源的浪费!因为数据结构对象本身就要占用内存,就与定义一个结构体变量(对象)一样,是要在内存的某个位置进行开辟空间!
  4. 会内存泄漏! 子进程结束退出了,而如果父进程又一直不进行回收的话,子进程又只是为了等待父进程的回收,这样子进程就无法释放,而子进程又占用空间,所以,这就是内存泄漏。我们必须想办法去回收!

进程状态总结

        就绪 (TASK_RUNNING)就是R(运行状态),唯一的就是占不占有CPU资源,如果占有CPU资源 ,即有可能因为某些原因,而导致进程进入浅睡眠(S)或深睡眠(D)。占有CPU资源时,可能暂停(TASK_STOPPED)(T),可以由暂停让其继续运行。然后退出,可能为僵死(TASK_ZOMBIE)(Z)没有回收它。

孤儿进程

  • 父进程如果提前退出,那么子进程后退出,进入Z之后。父进程却已经退出,无法对子进程进行回收。是否就会导致内存泄漏?即:子进程无人回收,子进程却又处于等待回收状态。

  • 其实:父进程先退出,子进程就称之为“孤儿进程”。孤儿进程被1号init进程领养,当然就以init进程回收了。

利用fork创建一个孤儿进程

        父进程退出,子进程还在,子进程就叫做孤儿进程!孤儿进程就会被领养,被1号进程领养(init,系统本身),领养的原因:未来子进程退出的时候,父进程早已不在,需要领养进程来进行回收。

         需要注意,当子进程的父进程退出后,如果子进程成为孤儿进程,那么它会从前台进程变为后台进程。使用kill -9 +id即可。

        如果子进程结束,是很难看到父进程为1的Z状态的进程。

进程优先级

#问为什么要有优先级?

        答:就是因为要确定谁先享受到资源,谁后享受到资源,而资源就是CPU。因为,CPU是有限的,而进程太多,需要通过某种方式争取资源,优先级就是主要的竞争方式。

#问:什么是优先级?

        答:确认是谁应该先获得某种资源,谁后获得,利用一些数据表明优先级,而除了需要表明优先级,还要调度器评判优先级。优先级是调度器调度的主要参考。

Linux具体的优先级做法

        优先级 = 老的优先级 + nice值。

在linux系统中,用ps –l命令则会类似输出以下几个内容:

命令:ps -l


查看当前所登录的会话中的相关的进程

#include<stdio.h>
int main()
{while(1)printf("hello world.\n");return 0;
}

PRI and NI 

  • PRI比较好理解,即进程的优先级,或者通俗点说就是程序被CPU执行的先后顺序,此值越小进程的优先级别越高。
  • NI,是nice值,其表示进程可被执行的优先级的修正数值。
  • PRI值越小越快被执行,加入nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice。
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级,在Linux下,就是调整进程nice值。
  • nice其取值范围是 -20 至 19 ,一共 40 个级别。

PRI vs NI

  • 需要强调一点的是,进程的nice值不是进程的优先级,他们不是一个概念,但是进程nice值会影响到进程的优先级变化。
  • 可以理解nice值是进程优先级的修正修正数据。

用top命令更改已存在进程的nice

1. top

(退出输入q,即可)

2. 进入top后按“r”–>输入进程PID–>输入nice值

        优先级 = 老的优先级(80:每次都是80,是默认的)+ nice值。即:每次设置优先级,都要从进程最开始的优先级开始设置。

对于优先级是非常不建议自行调:

  1. 一般很少牵扯到一个进程优先级的问题,大部分情况下,优先级是系统默认的。
  2. 优先级只有操作系统最清楚,自行改再多,也不一定能按照自己的期望那样。

        Linux下由于老的优先级默认为80,nice值[-20, 19],所以优先级的范围是[60, 99],就40个优先级,之所以是这个范围是因为:操作系统不是为了能快速的调用某些进程,它的目的是为了能够更加均衡的,让所有的进程在一个时间段内较为均衡的获取到某种资源,尤其是CPU资源。CPU的调度不是为了谁更优先,而是尽量雨露均沾。

        而如果将优先级差异的调整过大,就可能会有一些恶意进程,通过恶意的调整自己的优先级,而导致CPU在调动时总是先调度它。这样会导致其他进程长时间得不到调度,而导致的不公平问题。

其他概念

  • 独立性: 多进程运行,需要独享各种资源,多进程运行期间互不干扰。
  • 竞争性: 系统进程数目众多,而CPU资源只有少量,甚至1个,所以进程之间是具有竞争属性的。为了高效完成任务,更合理竞争相关资源,便具有了优先级。
  • 并行: 多个进程在多个CPU下分别,同时进行运行,这称之为并行。
  • 并发: 多个进程在一个CPU下采用进程切换的方式,在一段时间之内,让多个进程都得以推进,称之为并发。(只要有一个CPU一定是并发)

独立性

        就如同电脑中的多个软件同时进行(玩QQ又玩微信等),不可能说QQ崩了,然后微信也就崩了,因为这是两个程序。(此处只说进程本身出问题。如果是进程除了问题,而导致的连锁反应,那与此是另一回事)一样的,如果一个进程退出了,是不会影响另一个进程的。多个进程咋子同时运行的时候,它们的代码与数据几乎是不会影响的,即便是有些资源会共享,但是也不会互相影响。包括父子进程,挂了谁,都不会影响到另一个。

竞争性

        系统当中进程的数目众多,而CPU的资源是非常少的,甚至只有一个,所以进程与进程之间,本质就是竞争的。竞争后操作系统就要合理的给进程分配资源,所以便有了优先级。所以进程是具有竞争性的。

并行

        现在我们平时使用的电脑是一个CPU,但是对于其他的大型服务器,是有多个CPU的:

        即:同时在任意时刻,同时可能有两个以上的进程在被调度运行,这样的情况就叫做并行。

并发

#问:进程调用的时候,是不是直到将一个进程的代码和数据跑完之后,才执行下一个?

        答:不是,也不可能是。不可能让进程永久占有CPU,不然黑掉一个计算机就会很简单,就打一个死循环就行了。将会永远执行这个死循环。

时间片(每个进程在运行时都会有的)

        就是将一个进程放在CPU上,给你一个时间,比如:10ms(假设),在10ms内如果代码跑完了,呢就退出;没跑完别跑了,不着急先下去在后排队,以会再上来运行。所以写一个死循环操作系统不会被改掉,其他进程也可以跑的好好的。

抢占与出让

抢占低优先级的让位置给高优先级的。

        按照10ms(假设)时间片,如果在10ms外先下去,10ms内就结束,上下一个进程。可是现在的操作系统是支持抢占的(现在很多主流操作系统都是支持的),如果,一个进程的优先级是80,到这个进程的时候时间片是10ms,当正在执行第5ms的时候,来了一个优先级更高的60。此时,操作系统发现了,直接将优先级80的抢占下来,让给优先级高的先跑。一会再把优先级80的放上去跑。

出让进程主动让出位置。放在队列的其他地方,等下次调度。

基本概念:CPU执行代码的时候,并不是将一个进程的代码拿上去就不拿下来了,而是基于时间片,基于抢占,基于出让。每个进程都要在特定的时间段内,较为均衡的占有CPU资源。

        即:CPU在不断的被调动的时候,虽然任何一个时刻都只有一个进程在CPU上跑,但是在一个时间段内多个进程的执行都在推进,这种情况下叫做并发。(本质上:取决于CPU内进行各种快速的切换)

切换

切换的本质:CPU内的寄存器是只有一份的,但是寄存器需要存储的临时数据(上下文)是多份的,分别对应不同的进程!

        我们知道,每一个进程的上下文是不一样的,寄存器只有一份,那么根据并发,为下一个进程让出位置,那

#问:上下文数据能被抛弃吗?

        :绝而对不可以!

        当进程A暂时被切下来的时候,需要进程A顺便带走直接的上下文数据!带走暂时保存数据的是为了下一次回来的时候,能够恢复上去,以此继续按照之前的逻辑继续向后运行,就如同没有中断过一样。

发布评论

评论列表 (0)

  1. 暂无评论