• linux进程状态


    父进程和子进程的关系是子进程是父进程的一个副本,创建子进程后,子进程会有自己的空间,然后把父进程的数据拷贝到子进程的空间里。

    谁先运行是不确定的,父子没有明确的先后顺序

    子进程退出时,会向父进程发送中断信号,只有父进程处理了信号,子进程的相关资源才会被系统回收

    fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
           fork之后内核会
    通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。

    fork时子进程获得父进程数据空间、堆和栈的复制,所以变量的地址(当然是虚拟地址)也是一样的。

    父进程和子进程;

      他们的关系是管理和被管理的关系,当父进程终止时,子进程也随之而终止。但子进程终止,父进程并不一定终止。

     进程状态:S(state)

    O:进程正在处理器运行,这个状态从来木见过.

    S:休眠状态(sleeping)

    R:等待运行(runable)R Running or runnable (on run queue) 进程处于运行或就绪状态

    I:空闲状态(idle)

    Z:僵尸状态(zombie)

    T:跟踪状态(Traced)

    B:进程正在等待更多的内存页

    D:不可中断的深度睡眠,一般由IO引起,同步IO在做读或写操作时,cpu不能做其它事情,只能等待,这时进程处于这种状态,如果程序采用异步IO,这种状态应该就很少见到了

     Z (task_dead - exit_zombie):退出状态,进程成为僵尸进程

    在Linux进程的状态中,僵尸进程是非常特殊的一种,它是已经结束了的进程,但是没有从进程表中删除。太多了会导致进程表里面条目满了,进而导致系统崩溃,倒是不占用其他系统资源。

    它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

    进程在退出的过程中,处于TASK_DEAD状态。在这个退出过程中,进程占有的所有资源将被回收,除了task_struct结构(以及少数资源)以外。于是进程就只剩下task_struct这么个空壳,故称为僵尸。

    之所以保留task_struct,是因为task_struct里面保存了进程的退出码、以及一些统计信息。而其父进程很可能会关心这些信息。比如在shell中,$?变量就保存了最后一个退出的前台进程的退出码,而这个退出码往往被作为if语句的判断条件。 
    当然,内核也可以将这些信息保存在别的地方,而将task_struct结构释放掉,以节省一些空间。但是使用task_struct结构更为方便,因为在内核中已经建立了从pid到task_struct查找关系,还有进程间的父子关系。释放掉task_struct,则需要建立一些新的数据结构,以便让父进程找到它的子进程的退出信息。

    子进程在退出的过程中,内核会给其父进程发送一个信号,通知父进程来“收尸”。 父进程可以通过wait系列的系统调用(如wait4、waitid)来等待某个或某些子进程的退出,并获取它的退出信息。然后wait系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉。

    这个信号默认是SIGCHLD,但是在通过clone系统调用创建子进程时,可以设置这个信号。

    如果他的父进程没安装SIGCHLD信号处理函数调用wait或waitpid()等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,子进程的尸体(task_struct)也就无法释放掉。

    如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果如果父进程是一个循环,不会结束,那么子进程就会一直保持僵尸状态,这就是为什么系统中有时会有很多的僵尸进程。

    当进程退出的时候,会将它的所有子进程都托管给别的进程(使之成为别的进程的子进程)。托管的进程可能是退出进程所在进程组的下一个进程(如果存在的话),或者是1号进程。所以每个进程、每时每刻都有父进程存在。除非它是1号进程。1号进程,pid为1的进程,又称init进程。

    linux系统启动后,第一个被创建的用户态进程就是init进程。它有两项使命: 
    1、执行系统初始化脚本,创建一系列的进程(它们都是init进程的子孙); 
    2、在一个死循环中等待其子进程的退出事件,并调用waitid系统调用来完成“收尸”工作;

    init进程不会被暂停、也不会被杀死(这是由内核来保证的)。它在等待子进程退出的过程中处于task_interruptible状态,“收尸”过程中则处于task_running状态。

    Unix/Linux 处理僵尸进程的方法:

    找出父进程号,然后kill 父进程,之后子进程(僵尸进程)会被托管到其他进程,如init进程,然后由init进程将子进程的尸体(task_struct)释放掉。

    僵尸进程解决办法:

    (1)改写父进程,在子进程死后要为它收尸。

    具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行 waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。

    (2)把父进程杀掉。

    父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。如:

    kill -9 `ps -ef | grep "Process Name" | awk '{ print $3 }'` 
    其中,“Process Name”为处于zombie状态的进程名。

    (3)杀父进程不行的话,就尝试用skill -t TTY关闭相应终端,TTY是进程相应的tty号(终端号)。但是,ps可能会查不到特定进程的tty号,这时就需要自己判断了。 
    (4)重启系统,这也是最常用到方法之一。

  • 相关阅读:
    node学习报错之883
    vue-cli4创建项目
    Decorator学习笔记
    合天网安实验室学习笔记----Linux基础
    IDF实验室解题学习笔记1
    QA笑话----杂思
    测试优先
    Python的IDE:Eclipse+PyDev配置
    JS实现浏览器的title闪烁
    JSTL实现分页
  • 原文地址:https://www.cnblogs.com/samulescollection/p/3359677.html
Copyright © 2020-2023  润新知