1. 程式人生 > >linux子程序知道父程序退出的解決方案

linux子程序知道父程序退出的解決方案

在實際開發中難免會處理程序間的關係,最常見的是父子程序的相互監督。父程序等待子程序,或者自程序知道父程序執行是否結束,以方便釋放資源。

一、關於程序

程序是作業系統進行資源分配和排程的基本單位。linux系統使用fork建立程序,程序pid 0是swapper程序,程序pid 1是init程序,init程序是所有普通使用者程序的父程序。 fork在 <unistd.h>檔案中定義如下: pid_t fork(void); 當fork成功時會返回兩次,一次返回給父程序,一次返回給子程序。 如果fork呼叫成功,返回pid > 0 給父程序,返回pid == 0給子程序。 如果fork呼叫失敗,返回pid == -1給父程序,並且設定errno。

二、父程序與子程序的關係

通過fork後,為什麼父程序返回pid > 0,而子程序返回pid == 0呢?這是因為父程序無法知道自己有多少子程序,子程序建立後就獨立於父程序了,無法知道子程序的id。子程序能夠通過getppid獲取父程序的pid。當父程序比子程序提前退出時,子程序會成為孤兒程序,系統會將重置孤兒程序的父程序到init程序ppid == 1。如果子程序提前退出並且父程序沒有使用wait函式監聽處理結束的子程序,那麼該子程序會成為殭屍程序,即系統認為該程序存在,它佔用的程序資訊如程序pid等,會導致系統無法重複利用該pid號,可以這麼理解殭屍程序就是名存實亡的程序,系統認為程序還存在,而子程序已經完成他的執行任務了。

三、簡單的程序例程

simple_process.cpp
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    printf("start program %s, pid: %ld, ppid: %ld \n", argv[0], getpid(), getppid());
    pid_t pid = fork();
    if (-1 == pid) {
        printf("fork process failed. errno: %u, error: %s\n", errno, strerror(errno));
        exit(-1);
    }   
    if (pid > 0) { // parent 
        printf("parent precess\n");
        sleep(1);
/** 標記一、wait 等待子程序結束*/
//      int status;
//      wait(&status);
    } else { // child 
        printf("child process, pid: %ld, ppid: %ld\n", getpid(), getppid());
        for (int i = 0; i < 5; i++) {
            printf("child sleep %ds\n", i); 
            sleep(1);
        }   
        printf("##child process, pid: %ld, ppid: %ld\n", getpid(), getppid());
    }   
    return 0;
}
編譯執行 $ g++ simple_process.cpp $ ./a.out start program ./a.out, pid: 17164, ppid: 8564 parent precess child process, pid: 17165, ppid: 17164 child sleep 0s [[email protected] process-wait]$ child sleep 1s child sleep 2s child sleep 3s child sleep 4s ##child process, pid: 17165, ppid: 1 可以看到,當父程序提前結束時,子程序的父進臣變化為init程序,ppid == 1。

四、實現父程序監聽子程序執行結束

如果我們把“標誌一”的wait程式碼刪除註釋, 即 使用下面程式碼
     /** 標記一、wait 等待子程序結束 */
     int status;
     wait(&status);
編譯 $ g++ simple_process.cpp $ ./a.out start program ./a.out, pid: 28989, ppid: 8564 parent precess child process, pid: 28990, ppid: 28989 child sleep 0s child sleep 1s child sleep 2s child sleep 3s child sleep 4s ##child process, pid: 28990, ppid: 28989 因為wait函式的作用是等待子程序結束,並獲取子程序結束的狀態,所以父程序wait等待子程序結束後才繼續執行退出,故自程序的ppid一直是原來fork父程序的pid。因為wait函式是阻塞的,所以在實際開發中,需要監聽子程序退出,可以新建執行緒來監聽,並通過回撥函式處理。

五、實現子程序監聽父程序執行結束

fork建立程序以後子程序就成為獨立的執行單位(程序是作業系統資源分配和排程的基本單位),雖然子程序會繼承父程序原來的變數,但是子程序對這些變數的操作不會影響相應父程序變數的值等(這裡只是指簡單的int,long變數,如果是socket,file等共享資源那麼還可能會影響)。子程序已經無法通過普通方式知道父程序的執行狀態(正在執行還是執行結束,程序是否存活)。 子程序要知道父程序的狀態只能通過程序通訊方法解決,常用的程序間通訊:匿名管道,有名管道,訊號量,訊息佇列,訊號,共享記憶體,套接字。 本文將介紹使用套接字實現父程序結束自動結束子程序的方式,主要運用兩個特性。 1)socketpair會產生一個雙向的控制代碼,兩個控制代碼相當於通訊兩端,程式能夠通過一端寫入,然後再另一端控制代碼讀取內容。 2)程序執行退出時系統會自動回收資源,關閉該程序所佔有的檔案控制代碼,套接字控制代碼也是一種控制代碼。 程式碼實現
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <pthread.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

void * thr_child(void *arg) {
    int sock = *((int*)arg);
    char buf[2] = {0};
    ssize_t len = 0;
    while (-1 == (len = read(sock, buf, sizeof(buf))) && EINTR == errno);
    printf("thr_child. pid: %ld, ppid:%ld\n", getpid(), getppid());
    exit(-1 == len ? -1 : 0);
}

int main(int argc, char *argv[]) {
    printf("start program: %s, pid: %ld, ppid: %ld\n", argv[0], getpid(), getppid());
    int fd[2] = {0};
    if (-1 == socketpair(PF_LOCAL, SOCK_STREAM, 0, fd)) {
        perror("socketpair failed\n");
        exit(-1);
    }

    pid_t pid = fork();
    if (-1 == pid) {
        perror("fork failed\n");
        exit(-1);
    }
    if (0 == pid) { // child
        printf("child process, pid: %ld, ppid: %ld\n", getpid(), getppid());
        close(fd[0]);
        int sock = fd[1];

        pthread_t pid;
        if (pthread_create(&pid, NULL, thr_child, (void *)&sock)) {
            perror("child. create thread failed\n");
            exit(-1);
        }

        for (int i = 0; i < 20; i++) {
            sleep(1);
            printf("child sleep: %ds\n", i + 1);
        }
    } else { // parent
        printf("parent process, pid: %ld, ppid: %ld\n", getpid(), getppid());
        close(fd[1]);
        int sock = fd[0];
        for (int i = 0; i < 5; i++) {
            sleep(1);
            printf("parent sleep: %ds\n", i + 1);
        }
    }

    printf("process %s finish\n", pid > 0 ? "parent" : "child");
    return 0;
}
編譯及執行 $ g++ -pthread notify_parent_quit.cpp $ ./a.out start program: ./a.out, pid: 31141, ppid: 8564 parent process, pid: 31141, ppid: 8564 child process, pid: 31142, ppid: 31141 parent sleep: 1s child sleep: 1s parent sleep: 2s child sleep: 2s parent sleep: 3s child sleep: 3s parent sleep: 4s child sleep: 4s parent sleep: 5s process parent finish child sleep: 5s thr_child. pid: 31142, ppid:1 可以看到,如果使用wait函式,那麼父程序會等待子程序sleep 20秒再自行結束。但是因為父程序結束時會關閉套接字控制代碼sock導致子程序read返回0表示達到檔案結尾,因為父程序沒有主動close套接字控制代碼sock,所以只有父程序退出時由作業系統關閉該控制代碼。上面的notify程式就是運用這點,在父程序結束時通知子程序的。通過執行緒函式thr_child可以知道,線上程退出前列印ppid時,子程序的父程序ppid == 1,即由於父程序結束導致子程序成為孤兒程序。 總結、socketpair只是其中一種簡單的實現方式。這裡只是使用簡單的例子說明,在實際應用中處理的情況會比較複雜。

相關推薦

linux程序知道程序退出解決方案

在實際開發中難免會處理程序間的關係,最常見的是父子程序的相互監督。父程序等待子程序,或者自程序知道父程序執行是否結束,以方便釋放資源。 一、關於程序 程序是作業系統進行資源分配和排程的基本單位。linux系統使用fork建立程序,程序pid 0是swapper程序,程序pi

linux程序程序的關係

子程序繼承父程序 使用者號UIDs和使用者組號GIDs 環境Environment 堆疊 共享記憶體 開啟檔案的描述符 執行時關閉(Close-on-exec)標誌 訊號(Signal)控制設定 程序組號 當前工作目錄 根目錄

Linux程序繼承程序的檔案描述符

/* tcp_server.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/type

fork -- 程序共享程序開啟的檔案描述符

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define FILE_PATH "file_point" int main(int a

Linux作業系統之PIPE_BUF——程序讀出資料不相互交叉的問題

問題描述: 常量PIPE_BUF(在limits.h中定義)規定了核心的管道緩衝區大小,為4096byte。 父程序建立的3個子程序同時寫一個管道,要求寫入的位元組數超過PIPE_BUF,父程序從管道讀出的子程序寫入的資料不能相互交叉。 程式碼如下: #inclu

python 開啟程序的兩種方法、以及查詢程序程序

方法1 先輸出主程序,再輸出子程序是因為 p.start() 只是給系統傳送就緒訊號有延時,所以主程序都執行完了,可能子程序才到他開始執行 from multiprocessing import Process # 建立程序的模組 def

fork呼叫後,程序程序是否共享變數

回顧fork呼叫 fork系統呼叫從已存在的程序中生成一個新的程序,這個新的程序就是子程序,我們可以通過fork系統呼叫的返回值來區分子程序還是父程序。 一個程序,包括程式碼、資料和分配給程序的資源。fork()函式通過系統呼叫建立一個與原來程序幾乎完全相

WIN通過程序獲取程序ID

// ParentPid.cpp : Defines the entry point for the console application. // 對著你的專案點選右鍵,依次選擇:屬性、配置屬性、常規,然後右邊有個“專案預設值”,下面有個2個MFC的使用選項 #incl

併發伺服器中的fork函式、 程序程序的區別

#include <unistd.h> int fork(void) 返回:在子程序當中為0,在父程序當中表示為子程序ID,若出錯則返回-1 fork函式的特性: 1.fork之前開啟的所有的描述符(檔案描述符、裝置描述符、sockfd、管道中的描述符等等)在f

程序學習——程序程序ID

今天在測試共享記憶體時編寫了一個程序測試的程式碼,在呼叫fork函式建立子程序時,在父程序中返回子程序pid,在子程序中返回0;具體程式碼如下: #include <unistd.h> #include <stdio.h> int

關於在使用sparksql寫程序是報錯以及解決方案:org.apache.spark.sql.AnalysisException: Duplicate column(s): "name" found, cannot save to file.

文件加載 mod 但是 路徑 win 錯誤 寫入 技術分享 over 說明:   spark --version : 2.2.0   我有兩個json文件,分別是emp和dept: emp內容如下: {"name": "zhangsan", "age": 26, "dep

Python 中子程序程序

from multiprocessing import Process import time 複製程式碼 從程式中啟動程序的兩種方式: def task(i): print('%s start!'% i) time.sleep(2) print('%s stop!' % i) 複

獲取Windows程序程序編號

    Windows API中沒有提供直接獲取指定程序父程序編號的函式。為此可以通過程序快照來遍歷所有程序,從而獲取程序的父程序編號。本文通過NTDLL.dll中未公開API NtQueryInformationProcess來獲取程序的相關資訊,其中就包含了程序的父程序編

框架頁面中,從頁面重新整理頁面問題解決

上次做的一個專案的主頁面是那種框架的效果, 左邊點什麼右邊的子頁面對應各個頁面。 我開始要做的功能是當點選某個連線時,且session超時的時候.右邊就出來一個登陸錯誤頁面, 上面有個Button跳轉到登陸頁面。 直接寫的跳轉,就出現了框架巢狀框架的效果- - 後來這一句話

ubuntu中孤兒程序程序pid並不是1??

    剛剛寫了一個孤兒程序,順手列印了他的ppid,居然發現不是1,什麼鬼??!!! 因為在發現這個結果之前這個程序已經跑了很多遍了,新fork的程序都沒有退出,以為是因為這個原因所以結果跟我想的一樣。      然後sudo reboot,接

Linux PHP安裝遇見的問題及解決方案

linux php安裝出現錯誤 解決方法 遇到的問題與解決方案 問題一:報以下錯誤./configure以下錯誤發生Sorry, I cannot run apxs. Possiblereasons follow:1. Perl is not installed2. apxs was not fou

Linux中yum命令映象源出錯解決方案

好頭疼,最近虛擬機器中的linux系統一直不能安裝東西,只要install就報找不到可用的連結源。。。但是要安裝東西啊,所以就著手解決下。 解決方法:更換源 1.備份 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yu

u盤在linux系統下檔案只讀方式的解決方案

          我用的時ubuntu系統,經常使用u盤來傳送檔案,最近不知道為什麼,u盤在window可以正常使用,但是到linux下檔案就變成了只讀方面了,沒法進行復制,貼上了.上網查詢,發現方

[Linux]Ubuntu安裝pip及其各種bug解決方案

原創文章,歡迎轉載。轉載請註明:轉載自 祥的部落格 原文連結:https://blog.csdn.net/humanking7/article/details/84392790 文章目錄 環境 1. 安裝`pip` 2. 檢視`pi

linux下安裝redis出現錯誤及其解決方案

測試於:Redis2.6.17 | CentOS 5.7 安裝: 通過wget方式直接在linux上下載Redis wget http://download.redis.io/releases/red