1. 程式人生 > >訊號程式設計之訊號傳送及訊號處理函式遇到不可重入函式

訊號程式設計之訊號傳送及訊號處理函式遇到不可重入函式

kill函式

  • 函式原型: Int kill(pid_t pid, int siq)
  • 功能:既可以向自身傳送訊號,也可以向其他程序傳送訊號;
  • 引數:
    • pid>0 將訊號sig發給pid程序
    • pid=0 將訊號sig發給同組程序
    • pid=-1 將訊號sig傳送給所有程序,呼叫者程序有許可權傳送的每一個程序(除了1號程序之外,還有它自身)
    • pid<-1 將訊號sig傳送給程序組是pid(絕對值)的每一個程序

注意,如果在fork之前安裝訊號,則子程序可以繼承訊號。
補充:getpgrp()函式獲取程序組pid

sleep函式

函式原型:unsigned int sleep(unsigned int seconds);


功能:讓程序睡眠。原理和wait類似,都有可能被其他訊號打斷!

注意:
1)能被訊號打斷,然後處理訊號函式以後,就不再睡眠了。直接向下執行程式碼
2)sleep函式的返回值,是剩餘的秒數

  • 示例程式碼:
#include <sys/types.h>
#include <unistd.h>

#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h> #include <unistd.h> #include <sys/stat.h> #include <sys/wait.h> #include <sys/types.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <signal.h> void myhandle(int
num) { if (num == SIGINT) { printf("recv signal SIGINT \n"); } else if (num == SIGUSR1) { printf("recv signal SIGUSR1 \n"); } else { printf("recv signal id num : %d \n", num); } } int main(void) { pid_t pid; printf("main ....begin\n"); if (signal(SIGINT, myhandle) == SIG_ERR) { perror("func signal err\n"); return 0; } if (signal(SIGUSR1, myhandle) == SIG_ERR) { perror("func signal err\n"); return 0; } pid = fork(); if (pid == -1) { printf("fork err....\n"); return 0; } //子程序向父程序傳送訊號 //子程序向同組程序傳送訊號 /* if (pid == 0) { //pid = getpgrp(); pid = getppid(); //kill(pid, SIGUSR1); //向老爹發訊號 kill(0, SIGUSR1); //向程序組發訊號 //killpg(pid, SIGUSR1); exit(0); } */ if (pid == 0) { pid = getpgrp(); killpg(pid, SIGUSR1); exit(0); } int n = 3; do { printf("父程序開始睡眠\n"); n = sleep(n); printf("父程序開始喚醒\n"); } while (n > 0); //sleep(n); printf("sleep 函式執行完畢以後返回...\n"); return 0; }

raise函式

函式原型: int raise(int sig);
功能:給自己傳送訊號。raise(sig)等價於kill(getpid(), sig);

kill可以傳送訊號給其他程序

killpg函式

函式原型:int killpg(int pgrp, int sig);
功能:給程序組傳送訊號。killpg(pgrp, sig)等價於kill(-pgrp, sig);

sigqueue函式

函式原型:int sigqueue(pid_t pid, int sig, const union sigval value);
函式功能: 給程序傳送訊號,支援排隊,可以附帶資訊

pause函式

  • 將程序置為可中斷睡眠狀態。然後它呼叫核心函式schedule(),使linux程序排程器找到另一個程序來執行。
  • pause使呼叫者程序掛起,直到一個訊號被捕獲就返回,不再阻塞呼叫者程序。

alarm函式

功能:設定一個鬧鐘延遲傳送訊號。告訴linux核心n秒以後,傳送SIGALRM訊號;
函式原型:unsigned int alarm(unsigned int seconds);

注意:該鬧鐘只是一次有效,如果要重複有效,則需要在訊號處理函式裡面再次呼叫alarm函式設定鬧鐘。

void  myhandle(int num)
{
    printf("recv signal id num : %d \n", num);
    alarm(1);
}

int main(void)
{
    printf("main ....begin\n");
    //註冊訊號處理函式
    if (signal(SIGALRM, myhandle) == SIG_ERR)
    {
        perror("func signal err\n");
        return 0;
    } 

    alarm(1);
    while(1) 
    {
        pause();
        printf("pause return\n");
    }
    return 0;
}

訊號處理函式遇上可重入和不可重入函式

  所謂可重入函式是指一個可以被多個任務呼叫的過程,任務在呼叫時不必擔心資料是否會出錯。因為程序在收到訊號後,就將跳轉到訊號處理函式去接著執行。--也就是說可重入函式可以被多個程序或者函式同時呼叫,但不必關心內部資料和全域性資料是否會被修改。
  如果訊號處理函式中使用了不可重入函式,那麼訊號處理函式可能會修改原來程序中不應該被修改的資料,這樣程序從訊號處理函式中返回接著執行時,可能會出現不可預料的後果。不可重入函式在訊號處理函式中被視為不安全函式。
  滿足下列條件的函式多數是不可再入的:
  (1)使用靜態的資料結構,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()以及getpwnam()等等;
  (2)函式實現時,呼叫了malloc()或者free()函式
  (3)實現時使用了標準I/O函式的

結論:在訊號處理函式中,儘量不使用含有全域性變數和靜態變數的函式。特別是這個變數在程式中隨時可能讀寫。
man 7 signal 查詢可重入函式和不可重入函式。

#include <sys/types.h>
#include <unistd.h>


#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>


typedef struct _Teacher
{
    int age;
    int num;    
}Teacher;

Teacher g_t;
char *p = NULL:

void printfGlobTeacher()
{
    printf("g_t.age:%d \n", g_t.age);//含有全域性變數--不可重入--gt一直在改變
    printf("g_t.num:%d \n", g_t.num);
    //p = malloc(100);//容易引起不可重入
}

void  myhandle(int num)
{
    printf("recv signal id num : %d \n", num);

    printfGlobTeacher();
    alarm(1);
}

int main(void)
{
    Teacher t1, t2;
    t1.age = 30;
    t1.num = 30;
    t2.age = 40;
    t2.num = 40;
    printf("main ....begin\n");
    //註冊訊號處理函式
    if (signal(SIGALRM, myhandle) == SIG_ERR)
    {
        perror("func signal err\n");
        return 0;
    } 
    //間接遞迴
    //myhandle----->alarm=====>myhandle
    alarm(1);
    while(1) 
    {

        g_t = t1;

        g_t = t2;
    }
    return 0;
}