1. 程式人生 > >可重入函式,SIGCHILD訊號

可重入函式,SIGCHILD訊號

一、 可重入函式

概念:一個函式被多個執行流進入,不會出錯,這就叫可重入函式;否則,就叫不可重入函式。

1.如果一個函式只訪問自己的區域性變數或引數,當有多個執行流執行時,就不會互相影響。
2.首先它意味著這個函式可以被中斷,其次意味著它除了使用自己棧上的變數以外不依賴於任何環境(包括 static),這樣的函式就是可重入,可以允許有該函式的多個副本在執行,由於它們使用的是分離的棧,所以不會互相干擾。
3.如果確實需要訪問全域性變數(包括 static),一定要注意實施互斥手段。可重入函式在並行執行環境中非常重要,但是一般要為訪問全域性變數付出一些效能代價。

1.如何保證函式的可重入性?

1)在寫函式時候儘量使用區域性變數(例如暫存器、堆疊中的變數);
2)對於要使用的全域性變數要加以保護(如採取關中斷、訊號量等互斥方法),這樣構成的函式就一定是一個可重入的函式。

2.滿足下列條件的函式多數是不可重入(不安全)的:

1)函式體內使用了靜態的資料結構;
2)函式體內呼叫了malloc() 或者 free() 函式;
3)函式體內呼叫了標準 I/O 函式。

二、volatile限定符

編譯器的優化:

硬體級別的優化:
1.由於記憶體的訪問速度遠不及CPU的處理速度,所以引入硬體快取記憶體Cache,加速對記憶體的訪問;
2.現代CPU的指令並不一定嚴格按照順序執行,沒有相關性的指令可以亂序執行,以充分利用CPU的指令流水線,提高執行速度。

軟體級別的優化:
1.將記憶體變數快取到暫存器,調整指令順序

作用:告訴編譯器不要優化它所修飾的變數,會改變編譯結果

未被volatile修飾的變數每次操作時:
從記憶體中讀取----->放入暫存器----->CPU計算操作1---->CPU計算操作2…------>寫回記憶體

被volatile修飾的變數每次操作時:
從記憶體中讀取----->放入暫存器----->CPU計算操作------>寫回記憶體---->第二次從記憶體中讀取

也就是說,被volatile修飾的變數能夠保證每個執行緒能夠獲取該變數的最新值,從而避免出現數據髒讀的現象。

volatile可以修飾不返回的函式,這樣就不用把它的返回值載壓入堆疊了,起到一定的優化效果

三、SIGCHLD訊號

1.子程序在退出的時候,會給父程序傳送 “SIGCHLD” 訊號,該訊號的預設處理動作是忽略。

2.所以父程序可以自定義“SIGCHLD”訊號的處理動作,當子程序退出時,可以呼叫該預設處理動作;這樣父程序就不必阻塞等待了,提高了執行效率。

程式碼如下:

#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
using namespace std;

void handler(int signo)
{
    if(pid_t ret=(wait(NULL)) != -1)
    {
        cout << "child exit process" << ret <<endl;
    }
}
int main()
{
    struct sigaction act,oact;
    act.sa_handler=handler;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGCHLD,&act,&oact);
    pid_t id = fork();
    if(id == 0)
    {
        cout << "i am child run..." << endl;
        sleep(3);
        _exit(1);
    }
    else{
        while(1)
        {
            cout << "i am a parent:"<<getpid()<<endl;
            sleep(1);
        }
    }
}