日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

linux终端关闭时为什么会导致在其上启动的进程退出?

發(fā)布時間:2023/11/30 65 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux终端关闭时为什么会导致在其上启动的进程退出? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

現(xiàn)象

經(jīng)常在linux下開發(fā)的人應(yīng)該都有這樣的經(jīng)驗,就是在終端上啟動的程序,在關(guān)閉終端時,這個程序的進(jìn)程也被一起關(guān)閉了。看下面這個程序,為了使進(jìn)程永遠(yuǎn)運(yùn)行,在輸出helloworld后,循環(huán)調(diào)用sleep:

直接關(guān)閉這個終端,在另一個終端上查找該進(jìn)程,已經(jīng)找不到了:

這個行為看起來似乎是理所當(dāng)然的,也符合人的第一感覺:”在終端上啟動的程序是屬于終端的,所以當(dāng)關(guān)閉終端時,這個終端里的一包裹進(jìn)程都一起被解決掉了”。但這種說法是不能使一個會思考且充滿好奇心的人信服的。

下面我們就從linux進(jìn)程管理的細(xì)節(jié)來剖析其根本原因。

終端進(jìn)程

linux系統(tǒng)是基于進(jìn)程的,幾乎每個命令都可以在相應(yīng)的目錄下找到它們的程序,執(zhí)行一個命令相當(dāng)于啟動一個或多個程序,終端也不例外,在我centos下面終端對應(yīng)一個bash程序(不同操作系統(tǒng)終端的bash程序可能不一樣),它位于/usr/bin/下面:

每當(dāng)打開一個終端都會啟動一個bash進(jìn)程,我這里啟動了兩個終端,可以看到有兩個bash進(jìn)程:

終端進(jìn)程與啟動進(jìn)程的關(guān)系

linux系統(tǒng)里面所有的進(jìn)程的關(guān)系可以看做一個樹形結(jié)構(gòu),系統(tǒng)持續(xù)運(yùn)行,進(jìn)程的不斷啟動就是不斷fork的過程(fork是linux系統(tǒng)api,作用是復(fù)制自己來生成子進(jìn)程),從系統(tǒng)啟動、初始化、登錄終端、到執(zhí)行命令都是生成子進(jìn)程的過程:

init進(jìn)程是所有進(jìn)程的祖先,它的pid(進(jìn)程id)為1,ppid(父進(jìn)程id)也為1,因為它沒有父進(jìn)程,系統(tǒng)內(nèi)的其他進(jìn)程都是由它或者它的子進(jìn)程fork而來。

我們在linux上作業(yè)的終端對應(yīng)了一個bash進(jìn)程,在其上運(yùn)行的命令和程序都是bash的子進(jìn)程,或由bash的子進(jìn)程衍生。

用hw程序驗證一下,可以看到hw進(jìn)程的父進(jìn)程正好是bash進(jìn)程:

但這并不能解釋為什么終端關(guān)閉了在上面運(yùn)行的程序也跟著退出,因為在linux下,進(jìn)程之間的關(guān)系并不像線程那樣,當(dāng)主線程退出時,子線程一起被強(qiáng)制退出。進(jìn)程之間沒有主次的區(qū)別,但有父子關(guān)系,而父子進(jìn)程的運(yùn)行是相對獨(dú)立的,一方的退出不會導(dǎo)致另一方退出。

進(jìn)程session-揭開真相

在linux下,一個session是由一組進(jìn)程組構(gòu)成的,每個進(jìn)程組又由多個進(jìn)程構(gòu)成。

在一個bash上運(yùn)行的程序都?xì)w屬于一個session(除非特別處理),而這個bash就是這個session的leader。每個session又可以關(guān)聯(lián)一個控制終端(Controlling Terminal)。

圖片:

  • hw進(jìn)程的ppid=5933,說明父進(jìn)程為第一個bash,這個bash的父進(jìn)程為gnome-ternimal進(jìn)程,gnome-ternimal是centos可視化界面的終端管理進(jìn)程,每打開一個終端,它都會啟動一個bash進(jìn)程,而用戶的命令也是直接由bash進(jìn)程執(zhí)行的。
  • hw程序和第一個bash同屬于一個session(sid=5933),這個sid等于bash的pid,所以第一個bash是這個session的leader。
  • 圖片中還顯示了bash和hw進(jìn)程擁有共同的終端設(shè)備pts/2,它是一種字符設(shè)備,不同于上面提到的gnome-ternimal進(jìn)程。
  • 當(dāng)控制終端(對應(yīng)gnome-ternimal)檢測到終端設(shè)備斷(對應(yīng)pts/2)開連接時,會通知設(shè)備的控制進(jìn)程,即發(fā)送SIGHUP信號給session leader(對應(yīng)bash進(jìn)程)。
  • bash進(jìn)程在收到SIGHUP后,將信號發(fā)給session下的所有進(jìn)程,導(dǎo)致用戶啟動的進(jìn)程退出。
  • 下面通過strace命令來驗證以上結(jié)論:

  • 跟蹤hw進(jìn)程(命令意為跟蹤pid為6367的進(jìn)程上與signal有關(guān)的系統(tǒng)調(diào)用):

    strace -e trace=signal -p 6367

  • 跟蹤bash進(jìn)程(命令意為跟蹤pid為5933的進(jìn)程上與signal有關(guān)的系統(tǒng)調(diào)用):

    strace -e trace=signal -p 5933

  • 關(guān)閉啟動hw程序的終端,觀察strace輸出.

  • hwd的strace如下,si_pid=5933說明是5933這個進(jìn)程發(fā)了SIGHUP給它,也就是bash進(jìn)程:

    bash的strace略微復(fù)雜:

  • kill(4294960929, SIGHUP)

    kill第一個參數(shù)是32位有符號整數(shù),轉(zhuǎn)換成int就是-6367,當(dāng)參數(shù)為負(fù)時表示發(fā)送給這個數(shù)絕對值的進(jìn)程組,即pgrp=6367的所有進(jìn)程,在上面的圖片中可以看到hw進(jìn)程正好屬于該進(jìn)程組。

  • kill(5933, SIGHUP)

    5933是自己的pid,bash在第一次收到SIGHUP時先把信號發(fā)給session內(nèi)其他進(jìn)程,然后再次發(fā)送SIGHUP命令給自己,將自己殺死,后面的si_pid=5933也證實了這一點。

  • 如何讓終端關(guān)閉時進(jìn)程不退出

    根據(jù)上面的結(jié)論,要使終端關(guān)閉時進(jìn)程不退出,有以下幾種情況:

  • 用戶進(jìn)程攔截SIGHUP信號。
  • 用戶進(jìn)程和bash進(jìn)程不在一個session。
  • 下面依次驗證這兩種情況

    攔截SIGHUP

    修改hw程序,忽略SIGHUP信號:

    signal(SIGHUP, SIG_IGN);
    • 1
    • 2

    執(zhí)行hw程序,并查看進(jìn)程,可以看到hw進(jìn)程和父進(jìn)程bash:

    關(guān)閉終端,在另一個終端查看進(jìn)程:

    bash進(jìn)程已經(jīng)退出,但hw進(jìn)程還在,符合預(yù)期!!而且hw進(jìn)程的ppid變成了1,說明hw在父進(jìn)程bash退出后變成孤兒進(jìn)程被init進(jìn)程收養(yǎng)。

    新建session&setsid

    為了使用戶進(jìn)程和bash不在同一個session,需要調(diào)用setsid方法,該方法的作用是新建一個新的session,并使自己成為leader。

    // 先fork int pid = fork(); if(pid > 0){// 父進(jìn)程, 直接退出return 1; }else if(pid == 0){// 子進(jìn)程// 創(chuàng)建新的sessionsetsid();//printf("Hello World!\n");printf("sleeping...\n");while(1){sleep(1);} }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    調(diào)用setsid前先fork,因為若不fork,hw作為進(jìn)程組的leader,是不允許重建session的,原因留給讀者自己思考。

    編譯并執(zhí)行hw,查看進(jìn)程:

    可以看到,相比之前,有幾個不同的地方:

  • 程序啟動完,返回終端,hw切換到后臺運(yùn)行。
  • hw進(jìn)程的父進(jìn)程不再是bash,而是init進(jìn)程。
  • hw沒有關(guān)聯(lián)的終端設(shè)備(pts/2)。
  • 關(guān)閉終端,看到bash已經(jīng)消失,但對hw進(jìn)程沒有任何影響:

    更簡單的方法

  • setsid命令,用setsid來啟動程序,這樣就不用修改任何代碼也可以做到使啟動的進(jìn)程在新的session中,并且終端關(guān)閉時,進(jìn)程不退出。

    setsid ./hw

  • nohup命令,被nohup啟動的程序會忽略SIGHUP信號。

    nohup ./hw

  • 其他

    命令行中&的作用:

    ./hw &
    • 1
    • 2

    &的作用是使程序在后臺運(yùn)行,輸入fg命令又可以使程序切換到前臺。雖然在后臺運(yùn)行,但并不能保證進(jìn)程在終端關(guān)閉時不退出。

    總結(jié)

    簡而言之,終端在關(guān)閉時會發(fā)送SIGHUP給對應(yīng)的bash進(jìn)程,bash進(jìn)程收到這個信號后首先將它發(fā)給session下面的進(jìn)程,如果你的程序沒有對SIGHUP信號做特殊處理,那么進(jìn)程就會隨著終端關(guān)閉而退出。

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

    總結(jié)

    以上是生活随笔為你收集整理的linux终端关闭时为什么会导致在其上启动的进程退出?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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