【Linux】可重入函數 volatile關鍵字 以及SIGCHLD信號

可重入函數 volatile關鍵字 以及SIGCHLD信號

  • 一、可重入函數
    • 1、引入
    • 2、可重入函數的判斷
  • 二、volatile關鍵字
    • 1、引入
    • 2、關于編譯器的優化的簡單討論
  • 三、SIGCHLD信號

一、可重入函數

1、引入

我們來先看一個例子來幫助我們理解什么是可重入函數:

假設我們現在要對一個鏈表進行頭插,在執行到第10行代碼時,突然進程的時間片到了,進程被切換了,一會等進程再度切換回來時,當前進程要處理信號,而信號處理函數是sighandler,而sighandler里面也進行了頭插,等進程從內核態返回到用戶態時,繼續執行第11行的代碼,這時我們再觀察鏈表的結構會發現鏈表中出現了節點丟失的問題,而造成這種問題的根源是我們的insert函數同時被兩個執行流給進入了。

node_t node1, node2, *head;
int main()
{...insert(&node1);...
}void insert(node_t*p)
{p->next = head;head = p;
}void sighandler(int signo)
{insert(&node2);
}

在這里插入圖片描述

由這個問題衍生出了一種函數分類的方式:

  • 如果一個函數同時被多個執行流進入所產生的結果沒有問題,該函數被稱為可重入函數
  • 如果一個函數同時被多個執行流進入所產生的結果有問題,該函數被稱為不可重入函數
  • 可重入函數主要用于多任務環境中,一個可重入的函數通常來說就是可以被中斷的函數,也就是說,可以在這個函數執行的任何時刻中斷它,轉入OS調度下去執行另外一段代碼,而返回控制時不會出現什么錯誤;
  • 不可重入的函數由于使用了一些系統資源,比如全局變量區,中斷向量表等,所以它如果被中斷的話,可能會出現問題,這類函數是不能運行在多任務環境下的。

2、可重入函數的判斷

如果一個函數符合以下條件之一則是不可重入的:

  1. 函數體內使用了靜態(static)的數據結構或者變量;
  2. 調用了mallocfree,因為malloc也是用全局鏈表來管理堆的。
  3. 調用了標準I/O庫函數。標準I/O庫的很多實現都以不可重入的方式使用全局數據結構。

二、volatile關鍵字

1、引入

volatile是C語言的一個關鍵字,該關鍵字的作用是保證內存數據的可見性

我們來先來看一段代碼,這里我們不加入volatile關鍵字并開啟編譯器優化選項,優化級別是-O2

這段代碼的意思是:我們讓進程一直運行,直到我們給進程發送2號信號以后,進程再退出。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>int flag = 0;void handler(int signo)
{printf("捕捉到了%d號信號\n", signo);// 將flag置為1flag = 1;printf("已經將flag置為%d\n", flag);
}int main()
{signal(2, handler);printf("進程正在運行...\n");while (!flag);  // 當flag == 1時,進程退出。printf("運行結束!\n");return 0;
}

運行結果:

在這里插入圖片描述
可以看到,我們明明都已經讓flag = 1了但是進程中的循環依然沒有結束,這時為什么呢?下面我們一起來分析這個過程:


代碼中的main函數和handler函數在觸發時是兩個獨立的執行流,而while循環是在main函數當中的,而且main執行流里面并沒有使用過handler函數(signal函數只是對2號信號進行了捕捉,沒有調用過handler函數),所以在編譯器編譯時檢測到在main函數中對flag變量沒有做過修改操作,而且由于while循環運行時需要頻繁使用flag變量,所以編譯器可以將flag變量的值用一個寄存器進行保存,以后每次使用flag變量直接去寄存器里面取數據,不必每次都要將內存中的flag搬運到寄存器里面然后讓CPU去計算。

可是不巧的是我們給當前進程發送了2號信號,讓另外一個執行流更改了內存中的flag變量,而由于編譯器的優化,認為flag變量不會改變導致內存中的flag變量改變以后也沒有將寄存器中的數據同步修改,而CPU運算使用的數據又是寄存器中的數據,這就導致了內存數據的不可見,于是while循環就會一直運行,導致了上面的問題。

在這里插入圖片描述

為了讓編譯器每次都要去內存取數據來進行計算,我們可以在flag變量前面加上volatile關鍵字。

#include <stdio.h>
...
volatile int flag = 0;void handler(int signo)
{...
}
int main()
{...
}

再次運行程序,發現運行結果符合預期!

在這里插入圖片描述

2、關于編譯器的優化的簡單討論

上面的代碼如果我們不開啟優化,就算不加上volatile關鍵字也是能正常運行的,可見編譯器的優化不是越高越好。

如何理解編譯器的優化?

編譯器的本質是將代碼翻譯成01的二進制序列,所以編譯器的優化是在你編寫的代碼上動手腳,也就是說編譯器的優化其實改變了一些最終翻譯成01二進制以后的執行邏輯。

三、SIGCHLD信號

在一前我們講過用waitwaitpid函數清理僵尸進程,父進程可以阻塞等待子進程結束,也可以非阻塞地查詢是否有子進程結束等待清理(也就是輪詢的方式)。采用第一種方式,父進程阻塞了就不能處理自己的工作了;采用第二種方式,父進程在處理自己的工作的同時還要記得時不時地輪詢一 下,也很麻煩。
《wait與waitpid的使用介紹》

上面使用waitwaitpid其實都是父進程主動檢查子進程是否處于僵尸狀態,那么有沒有一種方法能夠讓子進程主動告訴父進程自己處于僵尸狀態呢?

其實,子進程在終止時會給父進程發SIGCHLD信號,該信號的默認處理動作是忽略,父進程可以自定義SIGCHLD信號的處理函數,這樣父進程只需專心處理自己的工作,不必關心子進程了,子進程終止時會通知父進程,父進程在信號處理函數中調用waitwaitpid清理子進程即可。

下面就是一個對SIGCHLD信號的一個使用:

在父進程中我們創建了10個子進程,這10個子進程退出時都會給父進程發送SIGCHLD信號,由于父進程回收其中一個子進程時,其他子進程也有可能同時給父進程發送SIGCHLD信號,而pending表又沒有辦法同時存儲多個信號,所以我們就要進行循環回收子進程,而為了不影響父進程的執行流程我們可以選擇非阻塞等待。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>pid_t id = 0;void WaitProcess(int signo)
{printf("捕捉到了%d號信號,正在處理...\n", signo);while (1){pid_t ret = waitpid(-1, NULL, WNOHANG);if (ret > 0){printf("等待子進程%d成功,父進程%d\n", ret, id);}else{break;}}printf("WaitProcess, done\n");
}int main()
{signal(SIGCHLD, WaitProcess);int i = 0;// 創建10個子進程for (i = 0; i < 10; i++){id = fork();// 子進程if (id == 0){int cnt = 5;//睡眠cnt秒以后退出while (cnt--){printf("我是子進程,我的pid是:%d,ppid是:%d\n", getpid(), getppid());sleep(1);}exit(0);}}// 父進程一直休眠while (1){sleep(1);}return 0;
}

在這里插入圖片描述

事實上,由于UNIX 的歷史原因,要想不產生僵尸進程還有另外一種辦法:父進程調用signalSIGCHLD的處理動作置為SIG_IGN,這樣fork出來的子進程在終止時會自動清理掉,不會產生僵尸進程,也不會通知父進程。系統默認的忽略動作和用戶用signal函數自定義的忽略 通常是沒有區別的,但這是一個特例。此方法對于Linux可用,但不保證在其它UNIX系統上都可用。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>pid_t id = 0;int main()
{// 對SIGCHLD設置為忽略,這樣產生的子進程退出時不會形成僵尸狀態。signal(SIGCHLD, SIG_IGN);int i = 0;// 創建10個子進程for (i = 0; i < 10; i++){id = fork();// 子進程if (id == 0){int cnt = 5;//睡眠cnt秒以后退出while (cnt--){printf("我是子進程,我的pid是:%d,ppid是:%d\n", getpid(), getppid());sleep(1);}exit(0);}}// 父進程一直休眠while (1){sleep(1);}return 0;
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38070.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38070.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38070.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

EthGlobal 巴黎站 Chainlink 獲獎項目介紹

在 Web3 中&#xff0c;每一周都至關重要。項目的發布、版本的發布以及協議的更新以驚人的速度推出。開發者必須保持學習&#xff0c;隨時了解最新的工具&#xff0c;并將所有他們所學的東西&#xff08;無論是舊的還是新的&#xff09;聯系起來&#xff0c;以構建推動 Web3 技…

PLUS操作流程、應用與實踐,多源不同分辨率數據的處理、ArcGIS的應用、PLUS模型的應用、InVEST模型的應用

PLUS模型是由中國地質大學&#xff08;武漢&#xff09;地理與信息工程學院高性能空間計算智能實驗室開發&#xff0c;是一個基于柵格數據的可用于斑塊尺度土地利用/土地覆蓋(LULC)變化模擬的元胞自動機(CA)模型。PLUS模型集成了基于土地擴張分析的規則挖掘方法和基于多類型隨機…

Word轉PDF在線轉換如何操作?分享轉換技巧

現如今&#xff0c;pdf轉換器已成為大家日常辦公學習必不可少的工具&#xff0c;市場上的pdf轉換器主要有兩種類型&#xff0c;一種是需要下載安裝的&#xff0c;另一種是網頁版&#xff0c;打開就可以使用的&#xff0c;今天小編給大家推薦一個非常好用的網頁版pdf轉換器&…

基于jvm-sandbox的imock開發指南

基于jvm-sandbox的imock開發指南 團隊今年的指標是為公司提供一個方法級的mock平臺&#xff0c; 這個重要的任務落在了我的身上。 0、明確團隊的需求 支持java后端服務方法級別的mock&#xff0c;對沒有測試環境的第三方服務進行mock&#xff0c;增加團隊覆蓋率。 啟用&#x…

PDF換行的難度,誰能解決?

換行的時候確認不了長度&#xff1a; import java.awt.*;public class Test {public static void main(String[] args) {String str1 "淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘淘";String str2 "AAAAAAAAAAAAAAAAAAAAAAAAA…

實驗篇——亞細胞定位

實驗篇——亞細胞定位 文章目錄 前言一、亞細胞定位的在線網站1. UniProt2. WoLFPSORT3. BUSCA4. TargetP-2.0 二、代碼實現1. 基于UniProt&#xff08;不會&#xff09;2. 基于WoLFPSORT后續&#xff08;已完善&#xff0c;有關代碼放置于[python爬蟲學習&#xff08;一&#…

一零七零、Redis基礎穩固篇

Redis是什么&#xff0c;優缺點&#xff1f; Redis本質是一個K-V類型的內存數據庫 純內存操作&#xff0c;每秒可處理超過10w的讀寫操作 優點&#xff1a; 讀寫性能極高 非阻塞IO 單線程 支持持久化 支持事務 數據結構豐富 缺點&#xff1a; 容易受到物理內存的限制 主機宕機可…

【JAVA】日志

輸出語句日志輸出位置只能是控制臺可以將日志信息寫入文件或數據庫中取消日志需要修改代碼&#xff0c;靈活性差只需修改日志文件多線程性能較差性能較好 日志規范接口&#xff1a;Commons Logging(JCL)、Simple Logging Facade for Java(slf4j) 日志實現框架&#xff1a;Log…

軟件測試簡歷撰寫與優化,讓你面試邀約率暴增99%!

如何撰寫一份優秀的簡歷呢&#xff1f;&#xff1f;這是一個求職者都會遇到的問題&#xff0c;今天就來詳細帶大家寫一份軟件測試工程師職位的簡歷&#xff01;希望能給各位軟件測試求職者一個帶來幫助&#xff01; 個人簡歷是求職者給招聘單位發的一份簡要介紹。包含自己的基本…

linux系統服務學習(一)Linux高級命令擴展

文章目錄 Linux高級命令&#xff08;擴展&#xff09;一、find命令1、find命令作用2、基本語法3、*星號通配符4、根據文件修改時間搜索文件☆ 聊一下Windows中的文件時間概念&#xff1f;☆ 使用stat命令獲取文件的最后修改時間☆ 創建文件時設置修改時間以及修改文件的修改時間…

【Vue】Vue2創建移動端項目實戰教程,創建移動端項目保姆級教程,設置axios,utils工具包,vue.fonfig.js配置項 (下)

系列文章目錄 這里是創建移動端項目 【Vue】Vue2.x創建項目全程講解&#xff0c;保姆級教程&#xff0c;手把手教&#xff0c;Vue2怎么創建項目&#xff08;上&#xff09; 【Vue】Vue2創建移動端項目實戰教程&#xff0c;創建移動端項目保姆級教程&#xff0c;接上一篇創建Vue…

2023牛客暑期多校訓練營9 B.Semi-Puzzle: Brain Storm

文章目錄 題目大意題解求解回溯 參考代碼 題目大意 給定兩個數 a , m a,m a,m &#xff0c;求滿足 a u ≡ u ( m o d m ) a^u \equiv u (mod\ \ m) au≡u(mod m) 的一個解。 ( 1 ≤ a , m ≤ 1 0 9 , 0 ≤ u ≤ 1 0 18 ) (1\leq a,m \leq10^9 ,0\leq u\leq 10^{18}) (1≤a…

玩賺音視頻開發高階技術——FFmpeg

隨著移動互聯網的普及&#xff0c;人們對音視頻內容的需求也不斷增加。無論是社交媒體平臺、電商平臺還是在線教育&#xff0c;都離不開音視頻的應用。這就為音視頻開發人員提供了廣闊的就業機會。根據這些年來網站上的音視頻開發招聘需求來看&#xff0c;音視頻開發人員的需求…

如何優雅的使用Mock Server

事出有因 昨天跟同事討論我們在用的rap2(一個集接口編寫和mock server的開源項目)和剛上線了一個easy-mock的server&#xff0c;到底哪個好用。 我們主要討論的點有個兩個&#xff1a; 接口的一致性、 編碼的無侵入性。 背景 自從前后端分離后&#xff0c;完成前后端的分工…

【計算機視覺|生成對抗】條件生成對抗網絡(CGAN)

本系列博文為深度學習/計算機視覺論文筆記&#xff0c;轉載請注明出處 標題&#xff1a;Conditional Generative Adversarial Nets 鏈接&#xff1a;[1411.1784] Conditional Generative Adversarial Nets (arxiv.org) 摘要 生成對抗網絡&#xff08;Generative Adversarial…

Windows 11 家庭中文版找不到組策略文件gpedit.msc

最近因為調整日期問題需要用到組策略文件gpedit.msc,但是發現找不到文件 在按鍵盤 winR 打開運行界面輸入 gpedit.msc 回車 Windows找不到文件’gpedit.msc’。請確定文件名是否正確后&#xff0c;再試-次。 檢查電腦Windows系統版本 是 Windows 11 家庭中文版 果斷早網上搜…

C++模板元編程入門案例

C++模板元編程(Template Metaprogramming)是一種在編譯時進行計算和代碼生成的技術,它使用C++的模板機制來實現。 下面是一個簡單的C++模板元編程的示例,展示了如何在編譯時計算一個數的階乘。 #include <iostream> template <int N> struct Factorial { …

docker 學習--02 常用命令

docker 學習–02 常用命令 文章目錄 docker 學習--02 常用命令1. 幫助啟動類命令1.1啟動docker1.2 停止docker1.3 重啟docker1.4 查看docker1.5 設置開機自啟1.6 查看docker概要信息1.7 查看docker總體幫助文檔1.8 查看docker命令幫助文檔 2. 鏡像命令2.1 列出本地主機上有的鏡…

Jmeter 參數化的幾種方法

目錄 配置元件-用戶自定義變量 前置處理器-用戶參數 配置元件-CSV Data Set Config Tools-函數助手 配置元件-用戶自定義變量 可在測試計劃、線程組、HTTP請求下創建用戶定義的變量 全局變量&#xff0c;可以跨線程組調用 jmeter執行的時候&#xff0c;只獲取一次&#xff0…

kafka 02——三個重要的kafka客戶端

kafka 02——三個重要的kafka客戶端 1. 前言1.1 關于 Kafka 的安裝1.2 常用客戶端簡介1.3 依賴 2. AdminClient2.1 Admin Configs2.2 AdminClient API2.2.1 設置 AdminClient 對象2.2.2 創建 topic 獲取 topic 列表2.2.3 刪除topic2.2.4 查看 topic 的描述信息2.2.5 查看 topi…