日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

僵尸进程与孤儿进程

發(fā)布時間:2025/3/8 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 僵尸进程与孤儿进程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

前言

進程就是運行起來的一個程序,但是進程并不局限于執(zhí)行起來的代碼,他的作用范圍還有很多,如存放數(shù)據(jù)的內存地址空間,執(zhí)行線程,打開的文件,掛起的信號,處理器狀態(tài)等。

進程在創(chuàng)建的時候開始存活,Linux系統(tǒng)會調用fork()方法復制一個現(xiàn)有進程來創(chuàng)建一個全新的進程,新產生的進程為子進程,創(chuàng)建者進程為父進程。當程序結束運行時,通過exit()系統(tǒng)調用退出執(zhí)行,該進程占用的資源包括內存空間,線程等被釋放掉。

進程家族樹

進程都是由其他進程創(chuàng)建出來的,每個進程都有自己的PID(進程標識號),在Linux系統(tǒng)的進程之間存在一個繼承關系,所有的進程都是init進程的后代??梢酝ㄟ^pstree命令查看到進程的族譜。系統(tǒng)中的每個進程必定會有一個父進程,可以在/proc文件系統(tǒng)中看到進程對應的父進程號,也可以通過ps -ef命令。

Linux進程涉及進程的所有操作都圍繞進程描述符展開,就是task_struct結構體。結構體包含好幾百個字段,該結構體可以完整的描述一個正在執(zhí)行的程序,如虛擬地址空間的信息,打開的文件,進程的執(zhí)行狀態(tài)和信息,命名空間,身份信息等。

在每個進程的task_struct中,用字段parent表示該進程的父進程,sibling代表兄弟進程鏈表,children代表子進程鏈表,理論上可以通過每一個進程獲取任何進程。

?

//結構體中相關字段struct?task_struct{...struct?task_struct?*parent;?/* 父進程 */?struct?list_head?children;?/* 子進程鏈表 */?struct?list_head?sibling;?/* 連接到父進程的子進程鏈表,兄弟進程 */?.... }

進程終結

當進程終結時候,系統(tǒng)需要釋放他所占有的所有資源。進程通過exit()系統(tǒng)調用結束進程,這個調用可能是來自進程內部的exit(),也可能來自外部的信號。在結束時候,該進程會使用該系統(tǒng)調用釋放自己的空間,包括引用的文件,內存描述符,還會給自己的父進程發(fā)送信號,給自己的子進程尋找一個父進程等操作。

調用結束后,此時該進程并沒有完全從系統(tǒng)上消失,進程的進程描述符依然存在于系統(tǒng)中,存在的唯一目的就是向父進程提供信息。

與自然規(guī)律相反,進程的收尾工作總是由該進程的父進程來做的,父進程會通過wait()系統(tǒng)調用來釋放該進程最后剩余的進程標識符,slab緩存等,該調用會阻塞當前父進程,直到某個子進程退出。

關于進程退出,可以結合看一下Linux bash怎么做的。

  • 首先ps命令獲取bash的進程PID

?

  • 再開一個bash頁面,查看上個bash進程的系統(tǒng)調用

$?sudo strace -p <Pid>

可以看到該進程的系統(tǒng)調用已經(jīng)被捕獲。

  • 輸入一條命令回車,觀察系統(tǒng)調用情況

    這里一條命令相當于一個bash的子進程,這里以tail為例??梢钥吹阶枞皆撓到y(tǒng)調用,也就是在等待回收子進程。

使用Ctrl+C結束進程tail時候,返回了tail進程的Pid。

了解了這些后,接下來理解僵尸進程和孤兒進程就很容易了。下面通過案例講解一下。

僵尸進程

當進程exit()退出之后,他的父進程沒有通過wait()系統(tǒng)調用回收他的進程描述符的信息,該進程會繼續(xù)停留在系統(tǒng)的進程表中,占用內核資源,這樣的進程就是僵尸進程。

接下來通過一個案例構造一個僵尸進程。

#include?<unistd.h> #include?<stdio.h>int?main?() {/*fpid表示fork函數(shù)返回的值,fork會返回兩次,一次是父進程,返回值是子進程的Pid,在子進程會返回0*/pid_t?fpid;fpid=fork();//fork后會出現(xiàn)兩個分支執(zhí)行下面的代碼,一個父進程,一個新的子進程if?(fpid <?0)printf("fork error!");else?if?(fpid ==?0) {?//printf("child id is %d\n",getpid());sleep(30);//睡眠30s,在父進程之前退出printf("child finally...");}else?{?//父進程printf("parent id is %d\n",getpid());sleep(60);printf("parend finally...");} }
  • 編譯并運行這段代碼。

$?gcc -o corpse corpse.c $?./corpose

  • pstree查看進程樹

$?pstree -p 9106

  • 等待子進程退出,查看子進程的狀態(tài)

$?cat?/proc/<Pid>/status

等到父進程退出之后,再來查看系統(tǒng),該僵尸進程在系統(tǒng)中找不到了。

? 父進程退出,沒有為子進程”收尸”,但是子進程也會一并退出,怎么做到的呢?這就涉及到了孤兒進程。

孤兒進程

當一個進程正在運行時,他的父進程忽然退出,此時該進程就是一個孤兒進程。作為一個進程,需要找到一個父進程,否則這種進程在退出之后沒人回收他的進程描述符,空耗內存。此時該進程會找到一個父進程,如果自己所在的進程組沒人收養(yǎng),那就作為init進程的子進程。

構造一個測試代碼:

#include?<unistd.h> #include?<stdio.h>int?main?() {/*fpid表示fork函數(shù)返回的值,fork會返回兩次,一次是父進程,返回值是子進程的Pid,在子進程會返回0*/pid_t?fpid;fpid=fork();//fork后會出現(xiàn)兩個分支執(zhí)行下面的代碼,一個父進程,一個新的子進程if?(fpid <?0)printf("fork error!");else?if?(fpid ==?0) {?//printf("child id is %d\n",getpid());sleep(100);}else?{?//父進程printf("parent id is %d\n",getpid());sleep(30);//睡眠30s,在子進程之前退出printf("parend finally...");} }
  • 編譯并運行

$gcc -o orphan orphan.c $./orphan

  • pstree查看進程樹

  • 等到父進程退出再看進程信息

    可以看到看到該進程的父進程變?yōu)?,也就是init進程。

Init進程會為每一個子進程使用wait系統(tǒng)調用,確保不會產生僵尸進程。這里的wait系統(tǒng)調用指的是waitpid(),會傳入一個要等待的進程Pid,等待的指定進程,而不阻塞當前進程去等待。

等到該進程退出后,該進程的進程描述符等信息會被init進程回收,不會形成僵尸進程。

? 回到上一個僵尸進程的案例中,父進程退出掉后,該進程會找到init作為父進程,init進程針對給進程調用wait()系統(tǒng)調用回收了該子進程。這就是查詢不到這個進程的原因。

處置方式

孤兒進程會由init進程收養(yǎng)作為子進程,所以不會有什么危害;僵尸進程會占用進程號,以及未回收的文件描述符占用空間,如果產生大量的僵尸進程,將會導致系統(tǒng)無法分配進程號,說明父進程的代碼編寫有問題。

$?ps?-aux|grep?Z

在理想情況下,可以通過kill命令將進程殺死該進程的父進程來結束僵尸進程。當然也要結合具體場景來對待。

總結

以上是生活随笔為你收集整理的僵尸进程与孤儿进程的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。