国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux進(jìn)程信號 | 信號處理

這篇具有很好參考價值的文章主要介紹了Linux進(jìn)程信號 | 信號處理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

前面的文章中我們講述了信號的產(chǎn)生與信號的保存這兩個知識點,在本文中我們將繼續(xù)講述與信號處理有關(guān)的信息。

信號處理

之前我們說過在收到一個信號的時候,這個信號不是立即處理的,而是要得到的一定的時間。從信號的保存中我們可以知道如果一個信號之前被block,當(dāng)解除block的時候,對應(yīng)的信號會立即被遞達(dá)。因為信號的產(chǎn)生是異步的,當(dāng)前進(jìn)程可能在做更重要的事情,當(dāng)進(jìn)程從內(nèi)核態(tài)切換回用戶態(tài)的時候,進(jìn)程就會在OS的指導(dǎo)下進(jìn)行信號的檢測與處理。

用戶態(tài)、內(nèi)核態(tài)

首先我們先來講講這兩個狀態(tài),用戶態(tài):執(zhí)行自己寫的代碼的時候,進(jìn)程所處的狀態(tài);內(nèi)核態(tài):執(zhí)行OS的代碼的時候,進(jìn)程所處的狀態(tài)。

  • 當(dāng)進(jìn)程的時間片到了需要切換時,就要執(zhí)行進(jìn)程切換邏輯。
  • 系統(tǒng)調(diào)用Linux進(jìn)程信號 | 信號處理

之前在進(jìn)程地址空間中我們學(xué)習(xí)過進(jìn)程地址空間的相關(guān)知識,我們知道PCB連接到進(jìn)程地址空間,然后通過頁表的映射,映射到物理內(nèi)存中。之前我們只學(xué)習(xí)了用戶空間,里面有堆、棧、代碼等。我們知道操作系統(tǒng)也是一段代碼,而在進(jìn)程地址空間中的內(nèi)核空間就是存儲的OS的代碼與數(shù)據(jù)映射的地方,因此同樣需要一張內(nèi)核級的頁表。以32位的系統(tǒng)為例子,所有的進(jìn)程地址空間中的0-3GB都是不同的存放的是該進(jìn)程自己的代碼與數(shù)據(jù),匹配了自己的用戶級頁表;所有進(jìn)程的3-4GB都是一樣的存放的是OS的代碼與數(shù)據(jù),每一個進(jìn)程都可以看到同樣的一張內(nèi)核級頁表,所有進(jìn)程都可以通過統(tǒng)一的窗口看到同一個OS;OS運行的本質(zhì):其實都是在進(jìn)程的地址空間中運行的;所以所謂的系統(tǒng)調(diào)用,其實就如同調(diào)用.SO中的方法,在自己的地址空間中進(jìn)行函數(shù)跳轉(zhuǎn)并返回即可。

此時就會出現(xiàn)一個問題,正應(yīng)為OS的代碼與數(shù)據(jù)跟用戶的代碼與數(shù)據(jù)在同一個地址空間中,為了防止用戶隨意的訪問OS的數(shù)據(jù)與代碼,因此就有了用戶態(tài)與內(nèi)核態(tài)。當(dāng)執(zhí)行自己的代碼,對應(yīng)的狀態(tài)就是用戶態(tài),要對系統(tǒng)調(diào)用進(jìn)行訪問,OS就會對身份,執(zhí)行級別進(jìn)行檢測,檢測到不是內(nèi)核態(tài)就會終止進(jìn)程。在CPU中存在一種寄存器叫做CR3,里面有對應(yīng)的比特位,比特位為0表征正在運行的進(jìn)程是用戶態(tài),比特位為3表征正在運行的進(jìn)程級別是內(nèi)核態(tài)。由于用戶無法直接對級別進(jìn)行修改,因此OS提供的系統(tǒng)調(diào)用,內(nèi)部在正式執(zhí)行調(diào)用邏輯的時候會去修改執(zhí)行級別。

進(jìn)程是如何被調(diào)度的?

首先我們要講一下OS。OS的本質(zhì)是軟件,本質(zhì)是一個死循環(huán);OS時鐘硬件,每個很短的時間向OS發(fā)送時鐘中斷,然后OS要執(zhí)行對應(yīng)的中斷處理方法。進(jìn)程被調(diào)度就是時間片到了,然后OS將進(jìn)程對應(yīng)的上下文等進(jìn)行保存并切換,選擇合適的進(jìn)程,這通過系統(tǒng)函數(shù)schedule()函數(shù)執(zhí)行上面的保存任務(wù)。

內(nèi)核如何實現(xiàn)信號的捕捉

Linux進(jìn)程信號 | 信號處理

?如果信號的處理動作是用戶自定義函數(shù),在信號遞達(dá)時就調(diào)用這個函數(shù),這稱為捕捉信號。由于信號處理函數(shù)的代碼是在用戶空間的,處理過程比較復(fù)雜,舉例如下: 用戶程序注冊了SIGQUIT信號的處理函數(shù)sighandler。 當(dāng)前正在執(zhí)行main函數(shù),這時發(fā)生中斷或異常切換到內(nèi)核態(tài)。 在中斷處理完畢后要返回用戶態(tài)的main函數(shù)之前檢查到有信號SIGQUIT遞達(dá)。 內(nèi)核決定返回用戶態(tài)后不是恢復(fù)main函數(shù)的上下文繼續(xù)執(zhí)行,而是執(zhí)行sighandler函 數(shù),sighandler和main函數(shù)使用不同的堆??臻g,它們之間不存在調(diào)用和被調(diào)用的關(guān)系,是 兩個獨立的控制流程。 sighandler函數(shù)返回后自動執(zhí)行特殊的系統(tǒng)調(diào)用sigreturn再次進(jìn)入內(nèi)核態(tài)。 如果沒有新的信號要遞達(dá),這次再返回用戶態(tài)就是恢復(fù)main函數(shù)的上下文繼續(xù)執(zhí)行了。

sigaction

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);

當(dāng)某個信號的處理函數(shù)被調(diào)用時,內(nèi)核自動將當(dāng)前信號加入進(jìn)程的信號屏蔽字,當(dāng)信號處理函數(shù)返回時自動恢復(fù)原來的信號屏蔽字,這樣就保證了在處理某個信號時,如果這種信號再次產(chǎn)生,那么 它會被阻塞到當(dāng)前處理結(jié)束為止。 如果在調(diào)用信號處理函數(shù)時,除了當(dāng)前信號被自動屏蔽之外,還希望自動屏蔽另外一些信號,則用sa_mask字段說明這些需要額外屏蔽的信號,當(dāng)信號處理函數(shù)返回時自動恢復(fù)原來的信號屏蔽字。

下面我們看一個簡單的例子:

static void PrintPending(const sigset_t &pending) {
    cout << "當(dāng)期進(jìn)程的pending信號集:";
    for (int signo = 1; signo <= 31; ++signo) {
        if (sigismember(&pending, signo)) // 用于打印信號集
            cout << "1";
        else
            cout << "0";
    }
    cout << endl;
}

static void handler(int signo) { // 添加了static之后該函數(shù)只能在本文件中使用 
    cout << "對特定信號:" << signo << "執(zhí)行捕捉動作" << endl;
    int cnt = 10;
    while (cnt) {
        cnt--;
        sigset_t pending;
        sigemptyset(&pending);
        sigpending(&pending);
        PrintPending(pending);
        cout << "打印完成pending信號集" << endl;
        sleep(1);
    }
}
int main() {
    struct sigaction act, oldact;
    memset(&act, 0, sizeof(act));
    memset(&oldact, 0, sizeof(oldact));
    act.sa_handler = handler; 
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaddset(&act.sa_mask, 3); // 可以添加其他信號的阻塞方式,在自定義捕捉時將其余收到的信號阻塞
    sigaddset(&act.sa_mask, 4);
    sigaddset(&act.sa_mask, 5);
    sigaction(2, &act, &oldact);

    while (true) {
        cout << getpid() << endl;
        sleep(1);
    }
}

通過在對2號信號實行自定義捕捉的時候給進(jìn)程發(fā)送3,4,5號信號,就可以通過打印信號集來查看該信號是否被阻塞。?Linux進(jìn)程信號 | 信號處理

其余知識點

可重入函數(shù)

我們以鏈表結(jié)點指針的頭插為例子:

一般頭插分為兩步首先將新節(jié)點插入在鏈表之前,然后再將頭指針指向新節(jié)點的地址,如果在第一步的時候進(jìn)行了信號的自定義動作保存了當(dāng)前函數(shù)執(zhí)行的狀態(tài),在自定義動作之中又執(zhí)行了一次鏈表頭插的動作,那么當(dāng)自定義動作處理結(jié)束之后,返回至用戶態(tài)函數(shù)執(zhí)行的地方,就會繼續(xù)原先的插入動作,那么我們在自定義函數(shù)中的插入結(jié)點就會丟失,導(dǎo)致內(nèi)存泄漏。Linux進(jìn)程信號 | 信號處理

?main函數(shù)調(diào)用insert函數(shù)向一個鏈表head中插入節(jié)點node1,插入操作分為兩步,剛做完第一步的 時候,因為硬件中斷使進(jìn)程切換到內(nèi)核,再次回用戶態(tài)之前檢查到有信號待處理,于是切換 到sighandler函數(shù),sighandler也調(diào)用insert函數(shù)向同一個鏈表head中插入節(jié)點node2,插入操作的 兩步都做完之后從sighandler返回內(nèi)核態(tài),再次回到用戶態(tài)就從main函數(shù)調(diào)用的insert函數(shù)中繼續(xù) 往下執(zhí)行,先前做第一步之后被打斷,現(xiàn)在繼續(xù)做完第二步。結(jié)果是,main函數(shù)和sighandler先后 向鏈表中插入兩個節(jié)點,而最后只有一個節(jié)點真正插入鏈表中了。

如果一個函數(shù)符合以下條件之一就是不可重入的:

  • 調(diào)用了malloc或free,因為malloc也是用全局鏈表來管理堆的。
  • 調(diào)用了標(biāo)準(zhǔn)I/O庫函數(shù)。標(biāo)準(zhǔn)I/O庫的很多實現(xiàn)都以不可重入的方式使用全局?jǐn)?shù)據(jù)結(jié)構(gòu)

volatile

下面我們來看一個關(guān)鍵字volatile,首先我們來看一個例子:

int quit = 0; // 保證內(nèi)存可見性

void handler(int signo) {
    printf("change quit from 0 to 1\n");
    quit = 1;
    printf("quit : %d\n", quit);
}

int main() {
    signal(2, handler);

    while(!quit); //注意這里我們故意沒有攜帶while的代碼塊,故意讓編譯器認(rèn)為在main中,quit只會被檢測

    printf("main quit 正常\n");

    return 0;
}

運行上述的代碼,就與我們之前學(xué)習(xí)的一樣會讓全局變量quit由0變1,進(jìn)行打印然后退出。

Linux進(jìn)程信號 | 信號處理

我們在編譯的時候是有優(yōu)化的級別的,可以根據(jù)不同的優(yōu)化級別記性優(yōu)化。我們選擇-O2來對上述的代碼記性優(yōu)化,可以發(fā)現(xiàn)我們雖然可以自定義捕捉信號,變量quit同樣也變成了1,但是卻無法讓程序退出。Linux進(jìn)程信號 | 信號處理

下面我們來解釋一下為什么??CPU匹配的運算種類只有兩種,算術(shù)運算與邏輯運算,while循環(huán)的代碼需要在CPU上執(zhí)行,因為只有CPU能夠進(jìn)行計算,因此需要我們先將quit加載到CPU中,然后再進(jìn)行真假的判斷,在CPU中還有記錄當(dāng)前程序位置的指針,當(dāng)判斷條件生效之后,指針就會指向下一句代碼。這就是為什么我們能夠退出的原因。Linux進(jìn)程信號 | 信號處理

?while循環(huán)是一種運算,這樣的運算是需要運算源的,每次都需要將數(shù)據(jù)從內(nèi)存加載到CPU中,編譯器發(fā)現(xiàn)在main函數(shù)中quit的值并沒有修改,而只是進(jìn)行判斷,編譯器就會認(rèn)為每次的quit數(shù)據(jù)都是一樣的,那么就會進(jìn)行優(yōu)化將數(shù)據(jù)第一次load進(jìn)CPU中,然后就不再進(jìn)行加載工作,只檢測CPU中寄存器的保存的quit數(shù)據(jù),相當(dāng)于讓CPU中的quit替換掉了內(nèi)存中的quit。這就導(dǎo)致了quit為什么進(jìn)行了修改但是并沒有退出的問題。為了告訴編譯器,保證每次檢測都要從內(nèi)存中進(jìn)行數(shù)據(jù)讀取不要用寄存區(qū)中的數(shù)據(jù)覆蓋,讓內(nèi)存數(shù)據(jù)可見,因此就有了volatile。

volatile 作用:保持內(nèi)存的可見性,告知編譯器,被該關(guān)鍵字修飾的變量,不允許被優(yōu)化,對該變量的任何操作,都必須在真實的內(nèi)存中進(jìn)行操作

SIGCHLD

在進(jìn)程等待的哪里我們學(xué)習(xí)過子進(jìn)程退出之后如果父進(jìn)程不進(jìn)行處理,子進(jìn)程就會變?yōu)榻┦M(jìn)程,然后我們就學(xué)習(xí)了waitpid和wait函數(shù)清理僵尸進(jìn)程。父進(jìn)程可以以非阻塞或者阻塞的方式進(jìn)行主動檢測,由于子進(jìn)程推出了,父進(jìn)程暫時不知道。子進(jìn)程在退出的時候會給父進(jìn)程發(fā)送SIGCHLD信號,該信號的默認(rèn)處理動作是忽略(SIG_DFL)什么都不做。

我們就可以使用自定義捕捉的方法進(jìn)行檢測?:Linux進(jìn)程信號 | 信號處理

那么我們就設(shè)想可以在自定義捕捉中進(jìn)行對僵尸進(jìn)行的處理,這樣就可以讓父進(jìn)程做自己的事情,可以自動對子進(jìn)程進(jìn)行回收。

pid_t id ;
void waitProcess(int signo) {
    printf("捕捉到一個信號: %d, who: %d\n", signo, getpid());
    sleep(5);
    while (1) {
        // 這里若設(shè)置的為0,那么如果有些子進(jìn)程退出了,有部分子進(jìn)程沒有退出導(dǎo)致自定義捕捉的函數(shù)無法返回會一直阻塞在里面,因此要設(shè)置為非阻塞的等待方式
        pid_t res = waitpid(-1, NULL, WNOHANG); // -1表示等待任意一個子進(jìn)程
        if (res > 0)
        {
            printf("wait success, res: %d, id: %d\n", res, id);
        }
        else break; // 如果沒有子進(jìn)程了?
    }
    printf("handler done...\n");
}

void handler(int signo) {
    printf("捕捉到一個信號: %d, who: %d\n", signo, getpid());
}

int main() {
    signal(SIGCHLD, waitProcess);
    // signal(SIGCHLD, handler);
    int i = 1;
    for (; i <= 10; i++) {
        id = fork();
        if (id == 0) {
            // child
            int cnt = 5;
            while (cnt)
            {
                printf("我是子進(jìn)程, 我的pid: %d, ppid: %d\n", getpid(), getppid());
                sleep(1);
                cnt--;
            }

            exit(1);
        }
    }
    // 如果你的父進(jìn)程沒有事干,你還是用以前的方法
    // 如果你的父進(jìn)程很忙,而且不退出,可以選擇信號的方法
    while (1) {
        sleep(1);
    }

    return 0;
}

Linux進(jìn)程信號 | 信號處理Linux進(jìn)程信號 | 信號處理

由于UNIX 的歷史原因,要想不產(chǎn)生僵尸進(jìn)程還有另外一種辦法:父進(jìn)程調(diào) 用sigaction將SIGCHLD的處理動作置為SIG_IGN,這樣fork出來的子進(jìn)程在終止時會自動清理掉,不 會產(chǎn)生僵尸進(jìn)程,也不會通知父進(jìn)程。系統(tǒng)默認(rèn)的忽略動作和用戶用sigaction函數(shù)自定義的忽略 通常是沒有區(qū)別的,但這是一個特例。此方法對于Linux可用,但不保證在其它UNIX系統(tǒng)上都可用。文章來源地址http://www.zghlxwxcb.cn/news/detail-489132.html

到了這里,關(guān)于Linux進(jìn)程信號 | 信號處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • Linux進(jìn)程 ----- 信號處理

    Linux進(jìn)程 ----- 信號處理

    目錄 前言 一、信號的處理時機 1.1 處理時面臨的情況 1.2 “合適”的時機 二、用戶態(tài)與內(nèi)核態(tài) 2.1 概念理論 2.2 再現(xiàn) 進(jìn)程地址空間 2.3 信號處理過程 三、信號的捕捉 3.1 內(nèi)核實現(xiàn) 3.2 sigaction 四、信號部分小結(jié) 從信號產(chǎn)生到信號保存,中間經(jīng)歷了很多,當(dāng)操作系統(tǒng)準(zhǔn)備對信號進(jìn)

    2024年03月21日
    瀏覽(24)
  • Linux——信號處理函數(shù)與阻塞狀態(tài)的進(jìn)程

    這篇博客記錄一下我在編寫一個簡單的多進(jìn)程回聲服務(wù)器的時候出現(xiàn)的問題。 這個問題就在于忽略了幾個有關(guān)于信號處理函數(shù)的基本常識: 用通俗的話講信號注冊函數(shù)(signal、sigaction)的功能:進(jìn)程告訴操作系統(tǒng),當(dāng)以后收到向信號注冊函數(shù)傳入的信號時,你幫我調(diào)用一下信號

    2024年02月13日
    瀏覽(22)
  • 【探索Linux】—— 強大的命令行工具 P.18(進(jìn)程信號 —— 信號捕捉 | 信號處理 | sigaction() )

    【探索Linux】—— 強大的命令行工具 P.18(進(jìn)程信號 —— 信號捕捉 | 信號處理 | sigaction() )

    在Linux系統(tǒng)中,信號是進(jìn)程之間通信的重要方式之一。前面的兩篇文章已經(jīng)介紹了信號的產(chǎn)生和保存,本篇文章將進(jìn)一步探討信號的捕捉、處理以及使用sigaction()函數(shù)的方法。信號捕捉是指進(jìn)程在接收到信號時采取的行動,而信號處理則是指對接收到的信號進(jìn)行適當(dāng)?shù)奶幚磉壿?/p>

    2024年02月05日
    瀏覽(23)
  • 【linux 多線程并發(fā)】多線程模型下的信號通信處理,與多進(jìn)程處理的比較,屬于相同進(jìn)程的線程信號分發(fā)機制

    ? 專欄內(nèi)容 : 參天引擎內(nèi)核架構(gòu) 本專欄一起來聊聊參天引擎內(nèi)核架構(gòu),以及如何實現(xiàn)多機的數(shù)據(jù)庫節(jié)點的多讀多寫,與傳統(tǒng)主備,MPP的區(qū)別,技術(shù)難點的分析,數(shù)據(jù)元數(shù)據(jù)同步,多主節(jié)點的情況下對故障容災(zāi)的支持。 手寫數(shù)據(jù)庫toadb 本專欄主要介紹如何從零開發(fā),開發(fā)的

    2024年01月17日
    瀏覽(22)
  • 【信號】信號處理與進(jìn)程通信:快速上手

    【信號】信號處理與進(jìn)程通信:快速上手

    目錄 0. 信號概述 1. 產(chǎn)生信號的方式: 1.1 當(dāng)用戶按某些終端鍵時,將產(chǎn)生信號。 1.2 硬件異常將產(chǎn)生信號。 1.3 軟件異常將產(chǎn)生信號。 1.4 調(diào)用kill函數(shù)將發(fā)送信號。 1.5 運行kill命令將發(fā)送信號。 2. 信號的默認(rèn)(缺?。┨幚矸绞?2.1 終止進(jìn)程: 2.2 缺省出來: 2.3 停止進(jìn)程: 2.

    2024年02月12日
    瀏覽(24)
  • FPGA信號處理系列文章——深入淺出理解多相濾波器

    提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔 多相濾波是,按照相位均勻劃分把數(shù)字濾波器的系統(tǒng)函數(shù)H(z)分解成若干個具有不同相位的組,形成多個分支,在每個分支上實現(xiàn)濾波。 采用多相濾波結(jié)構(gòu),可利用多個階數(shù)較低的濾波來實現(xiàn)原本階數(shù)較

    2024年02月05日
    瀏覽(57)
  • 【linux】信號——信號保存+信號處理

    【linux】信號——信號保存+信號處理

    自我名言 : 只有努力,才能追逐夢想,只有努力,才不會欺騙自己。 喜歡的點贊,收藏,關(guān)注一下把! 上一篇博客,我們已經(jīng)學(xué)過信號預(yù)備知識和信號的產(chǎn)生,今天講講信號保存+信號處理以及其他補充知識。 補充一些概念。 實際執(zhí)行信號的處理動作稱為 信號遞達(dá)(Delive

    2024年02月04日
    瀏覽(35)
  • 【TCP/IP】多進(jìn)程服務(wù)器的實現(xiàn)(進(jìn)階) - 信號處理及signal、sigaction函數(shù)

    【TCP/IP】多進(jìn)程服務(wù)器的實現(xiàn)(進(jìn)階) - 信號處理及signal、sigaction函數(shù)

    目錄 信號 signal函數(shù) sigaction函數(shù) 用信號來處理僵尸進(jìn)程 ???????? 在之前我們學(xué)習(xí)了如何處理“僵尸進(jìn)程”,不過可能也會有疑問:調(diào)用wait和waitpid函數(shù)時我們關(guān)注的始終是在子進(jìn)程上,那么在父進(jìn)程上如何實現(xiàn)對子進(jìn)程的管控呢?為此,我們引入一個概念——信號處理。

    2024年02月08日
    瀏覽(19)
  • 【Linux從入門到精通】信號(信號保存 & 信號的處理)

    【Linux從入門到精通】信號(信號保存 & 信號的處理)

    ? 本篇文章接著信號(初識信號 信號的產(chǎn)生)進(jìn)行講解。學(xué)完信號的產(chǎn)生后,我們也了解了信號的一些結(jié)論。同時還留下了很多疑問: 上篇文章所說的所有信號產(chǎn)生,最終都要有OS來進(jìn)行執(zhí)行,為什么呢? OS是進(jìn)程的管理者 。 信號的處理是否是立即處理的? 在合適的時候。

    2024年02月09日
    瀏覽(21)
  • Linux——信號處理

    在Linux系統(tǒng)中, 信號處理 是一個非常重要的概念,它允許 操作系統(tǒng)在特定事件發(fā)生時 通知進(jìn)程。信號可以由 硬件異常、用戶輸入、軟件條件 等多種來源產(chǎn)生。為了有效地處理這些信號,Linux提供了一系列的系統(tǒng)調(diào)用和函數(shù),其中 signal 、 sigaction 和 sigprocmask 是三個核心的函

    2024年03月09日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包