【linux】系統程式設計-1-程式、管道和訊號

李柱明發表於2020-12-28


1. 程式

1.1 概念

  • 程式
    • 程式是存放在儲存介質上的一個可執行檔案
  • 程式
    • 程式是程式執行的過程,是程式在執行過程中分配和管理資源的基本單位
    • 程式是靜態的,程式是動態的。程式的狀態是變化的,其包括程式的建立、排程和消亡
  • 執行緒
    • 執行緒是CPU排程和分派的基本單位,它可與同屬一個程式的其他的執行緒共享程式所擁有的全部資源
    • 一個執行緒只能屬於一個程式,而一個程式可以有多個執行緒,但至少有一個執行緒
  • 程式ID
    • 程式ID 是一個16位的正整數,預設取值範圍是從 232768可以修改
    • PID數字為1的值一般是為特殊程式 init 保留
  • 父程式
    • 任何程式(除init程式)都是由另一個程式啟動,該程式稱為被啟動程式的父程式(ID號稱為:PID),被啟動的程式稱為子程式(ID號稱為:PPID),
    • 父程式號無法在使用者層修改

1.2 檢視程式

  • 檢視程式命令
    • ps -aux
      • 檢視系統程式
    • pstree
      • 將程式以樹狀關係列出來

1.3 啟動新程式

  • 介紹三種方法啟動新程式
    1. system() 函式
    2. fork() 函式
    3. exec() 函式

1.3.1 system() 函式

  • 可以理解為 啟動新程式
  • system()啟動了一個執行著/bin/sh的子程式
    • 說明 system() 函式依賴與 shell
  • int system (const char *string )
    • 效果就相當於執行 sh –c string
  • system() 函式的特點
    • 建立獨立程式,擁有獨立的程式碼空間,記憶體空間
    • 等待新的程式執行完畢,system才返回。(阻塞)
  • 例程
    • system 執行完才會返回,才會在當前終端列印出資料
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    pid_t result;
    printf("This is a system demo!\n\n");
    /*呼叫 system()函式*/
    result = system("ls -l");
    printf("Done!\n\n");
    return result;
}

1.3.2 fork() 函式

  • 可以理解為 複製程式
  • 標頭檔案
    1. #include<unistd.h>
    2. #include<sys/types.h>
  • pid_t fork( void);
    • 若成功呼叫一次則
      • 子程式返回 0
      • 父程式返回子程式 ID
    • 出錯返回 -1
  • 例程
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    pid_t result;
    printf("This is a fork demo!\n\n");
    /*呼叫 fork()函式*/
    result = fork();
    /*通過 result 的值來判斷 fork()函式的返回情況,首先進行出錯處理*/
    if(result == -1) {
        printf("Fork error\n");
    }
    /*返回值為 0 代表子程式*/
    else if (result == 0) {
        printf("The returned value is %d, In child process!! My PID is %d\n\n", result, getpid());
    }
    /*返回值大於 0 代表父程式*/
    else {
        printf("The returned value is %d, In father process!! My PID is %d\n\n", result, getpid());
    }
    return result;}

1.3.2 exce 系列函式

  • 可以理解為 替換程式
  • 呼叫 exec 並不建立新程式,所以前後的程式 ID 並未改變
  • exec 只是用另一個新程式替換了當前程式的正文、資料、堆和棧段
  • 在原程式中已經開啟的檔案描述符,在新程式中仍將保持開啟,除非它們的“執行時關閉標誌”(close on exec flag)被置位
  • 任何在原程式中已開啟的目錄流都將在新程式中被關閉
  • 舉個例子,A程式呼叫 exce 系列函式啟動一個程式B,此時程式B會替換程式A,程式A的記憶體空間、資料段、程式碼段等內容都將被程式B佔用,程式A將不復存在
1.3.2.1 exce 系列函式說明
  • exec 系列函式有 6 個不同的 exec 函式
    1. int execl(const char *path, const char *arg, ...)
    2. int execlp(const char *file, const char *arg, ...)
    3. int execle(const char *path, const char *arg, ..., char *const envp[])
    4. int execv(const char *path, char *const argv[])
    5. int execvp(const char *file, char *const argv[])
    6. int execve(const char *path, char *const argv[], char *const envp[])
  • 函式說明
    • 名稱包含 l 字母的函式(execl、 execlp 和execle)接收引數列表”list”作為呼叫程式的引數
    • 名稱包含 p 字母的函式(execvp 和execlp)接受一個程式名作為引數,然後在當前的執行路徑中搜尋並執行這個程式
    • 名字不包含 p 字母的函式在呼叫時必須指定程式的完整路徑,其實就是在系統環境變數”PATH”搜尋可執行檔案
    • 名稱包含 v 字母的函式(execv、execvp 和 execve)的命令引數通過一個陣列”vector”傳入
    • 名稱包含 e 字母的函式(execve 和 execle)比其它函式多接收一個指明環境變數列表的引數,並且可以通過引數envp傳遞字串陣列作為新程式的環境變數,這個envp引數的格式應為一個以 NULL 指標作為結束標記的字串陣列,每個字串應該表示為”environment =virables”的形式

1.3 終止程式

  • 可以分為 5 種程式終止
    • 正常終止
      • main 函式返回
      • 呼叫 exit() 終止
      • 呼叫 _exit() 函式終止
    • 異常終止
      • 呼叫 abort() 函式終止
      • 由系統訊號終止

1.4 等待程式

  • 父程式中呼叫wait()或者waitpid()函式讓父程式等待子程式的結束

1.4.1 wait() 函式

  • wait()函式只是 waitpid() 函式的一個特例,在 Linux內部實現 wait 函式時直接呼叫的就是 waitpid 函式
  • pid_t wait(int *wstatus);
    • wait() 函式在被呼叫的時候,系統將暫停父程式的執行,直到有訊號來到或子程式結束
    • 如果在呼叫 wait() 函式時子程式已經結束,則會立即返回子程式結束狀態值
    • 子程式的結束狀態資訊會由引數wstatus返回
    • 該函式的返回值為子程式的PID
  • 注意
    • wait()要與fork()配套出現,且 fork() 呼叫先
    • 引數wstatus用來儲存被收集程式退出時的一些狀態
  • 可以使用以下巨集來判斷退出狀態
    • WIFEXITED(status) :如果子程式正常結束,返回一個非零值
    • WEXITSTATUS(status): 如果WIFEXITED非零,返回子程式退出碼
    • WIFSIGNALED(status) :子程式因為捕獲訊號而終止,返回非零值
    • WTERMSIG(status) :如果WIFSIGNALED非零,返回訊號程式碼
    • WIFSTOPPED(status): 如果子程式被暫停,返回一個非零值
    • WSTOPSIG(status): 如果WIFSTOPPED非零,返回一個訊號程式碼

1.4.2 waitpid() 函式

  • wait()函式只是 waitpid() 函式的一個特例,在 Linux內部實現 wait 函式時直接呼叫的就是 waitpid 函式
  • pid_t waitpid(pid_t pid, int *wstatus, int options);
    • pid:引數pid為要等待的子程式ID
      • pid < -1:等待程式組號為pid絕對值的任何子程式
      • pid = -1:等待任何子程式,此時的waitpid()函式就等同於wait()函式
      • pid = 0:等待程式組號與目前程式相同的任何子程式,即等待任何與呼叫waitpid()函式的程式在同一個程式組的程式
      • pid > 0:等待指定程式號為pid的子程式
    • wstatus:與wait()函式一樣
    • options:引數 options 提供了一些另外的選項來控制waitpid()函式的行為。如果不想使用這些選項,則可以把這個引數設為0

2. 管道

2.1 概念

  • 管道
    • 管道是 Linux 由 Unix 那裡繼承過來的程式間的通訊機制,它是Unix早期的一個重要通訊機制。
    • 其思想是,在記憶體中建立一個共享檔案,從而使通訊雙方利用這個共享檔案來傳遞資訊。由於這種方式具有單向傳遞資料的特點,所以這個作為傳遞訊息的共享檔案就叫做“管道”
  • 管道分類
    • 匿名管道(無名管道)(PIPE
    • 命名管道(有名管道)(FIFO

2.2 匿名管道

2.2.1 匿名管道特徵

  1. 沒有名字,因此不能使用 open() 函式開啟,但可以使用 close() 函式關閉
  2. 只提供單向通訊
  3. 只能用於具有血緣關係的程式間通訊,通常用於父子程式建通訊
  4. 管道是基於位元組流來通訊的
  5. 依賴於檔案系統,它的生命週期隨程式的結束而結束
  6. 寫入操作不具有原子性,因此只能用於一對一的簡單通訊情形
  7. 管道也可以看成是一種特殊的檔案,對於它的讀寫也可以使用普通的read()和write()等函式。但是它又不是普通的檔案,並不屬於其他任何檔案系統,並且只存在於核心的記憶體空間中,因此不能使用lseek()來定位

2.2.2 pipe() 函式

  • pipe() 函式用於建立一個匿名管道,一個可用於程式間通訊的單向資料通道。
  • 標頭檔案
    • #include <unistd.h>
  • 函式原型
    • int pipe(int pipefd[2]);
      • pipefd[0] 指向管道的 讀取
      • pipefd[1] 指向管道的
      • 返回 0:匿名管道建立成功
      • 返回 -1:建立失敗
  • 使用步驟
    1. 父程式呼叫 pipe() 函式建立匿名管道
    2. 父程式呼叫 fork() 函式啟動(建立)一個子程式
    3. 若想從父程式將資料傳遞給子程式
      1. 父程式:關閉讀取端
      2. 子程式:關閉寫端
    4. 若想從子程式將資料傳遞給父程式
      1. 父程式:關閉寫端
      2. 子程式:關閉讀取端
    5. 當不需要使用管道時,關閉所有埠即可
  • 例程
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_DATA_LEN 256
#define DELAY_TIME 1

int main()
{
    pid_t pid;
    int pipe_fd[2];                             //(1)
    char buf[MAX_DATA_LEN];
    const char data[] = "Pipe Test Program";
    int real_read, real_write;

    memset((void*)buf, 0, sizeof(buf));

    /* 建立管道 */
    if (pipe(pipe_fd) < 0)                  //(2)
    {
        printf("pipe create error\n");
        exit(1);
    }

    /* 建立一子程式 */
    if ((pid = fork()) == 0)
    {
        /* 子程式關閉寫描述符,並通過使子程式暫停 3s 等待父程式已關閉相應的讀描述符 */
        close(pipe_fd[1]);
        sleep(DELAY_TIME * 3);
        /* 子程式讀取管道內容 */ 
        if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
        {
            printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
        }
        /* 關閉子程式讀描述符 */
        close(pipe_fd[0]);
        exit(0);
    }
    else if (pid > 0)
    {
        /* 父程式關閉讀描述符,並通過使父程式暫停 1s 等待子程式已關閉相應的寫描述符 */
        close(pipe_fd[0]);
        sleep(DELAY_TIME);
        if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
        {
            printf("Parent write %d bytes : '%s'\n", real_write, data);
        }
        /*關閉父程式寫描述符*/
        close(pipe_fd[1]);
        /*收集子程式退出資訊*/
        waitpid(pid, NULL, 0);
        exit(0);
    }
}

2.3 命名管道

2.3.1 命名管道特徵

  1. 有名字,儲存於普通檔案系統之中
  2. 任何具有相應許可權的程式都可以使用 open() 來獲取命名管道的檔案描述符
  3. 跟普通檔案一樣:使用統一的 read()/write() 來讀寫
  4. 跟普通檔案不同:不能使用 lseek() 來定位,原因是資料儲存於記憶體中
  5. 具有寫入原子性,支援多寫者同時進行寫操作而資料不會互相踐踏
  6. 遵循先進先出(First In First Out)原則,最先被寫入 FIFO 的資料,最先被讀出來

2.3.2 建立命名管道命令

  • mkfifo
    • mkfifo test
      • test 檔案為命名管道檔案

2.3.3 fifo() 函式

  • fifo() 函式
  • 標頭檔案
    • #include <unistd.h>
  • 函式原型
    • int mkfifo(const char * pathname,mode_t mode);
      • pathname:命名管道檔案
      • mode:
        • O_RDONLY:讀管道
        • O_WRONLY:寫管道
        • O_RDWR:讀寫管道
        • O_NONBLOCK:非阻塞
        • O_CREAT:如果該檔案不存在,那麼就建立一個新的檔案,並用第三個引數為其設定許可權
        • O_EXCL:如果使用 O_CREAT 時檔案存在,那麼可返回錯誤訊息
      • 返回值:
        • 0:成功
        • EACCESS:引數 filename 所指定的目錄路徑無可執行的許可權
        • EEXIST:引數 filename 所指定的檔案已存在
        • ENAMETOOLONG:引數 filename 的路徑名稱太長
        • ENOENT:引數 filename 包含的目錄不存在
        • ENOSPC:檔案系統的剩餘空間不足
        • ENOTDIR:引數 filename 路徑中的目錄存在但卻非真正的目錄
        • EROFS:引數 filename 指定的檔案存在於只讀檔案系統內
  • 例程
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#define MYFIFO "myfifo"    /* 命名管道檔名*/

#define MAX_BUFFER_SIZE PIPE_BUF /* 4096 定義在於 limits.h 中*/

void fifo_read(void)
{
    char buff[MAX_BUFFER_SIZE];
    int fd;
    int nread;

    printf("***************** read fifo ************************\n");
    /* 判斷命名管道是否已存在,若尚未建立,則以相應的許可權建立*/
    if (access(MYFIFO, F_OK) == -1)
    {
        if ((mkfifo(MYFIFO, 0666) < 0) && (errno != EEXIST))
        {
            printf("Cannot create fifo file\n");
            exit(1);
        }
    }
    /* 以只讀阻塞方式開啟命名管道 */
    fd = open(MYFIFO, O_RDONLY);
    if (fd == -1)
    {
        printf("Open fifo file error\n");
        exit(1);
    }
    memset(buff, 0, sizeof(buff));
    if ((nread = read(fd, buff, MAX_BUFFER_SIZE)) > 0)
    {
        printf("Read '%s' from FIFO\n", buff);
    }
    printf("***************** close fifo ************************\n");
    close(fd);
    exit(0);
}

void fifo_write(void)
{
    int fd;
    char buff[] = "this is a fifo test demo";
    int nwrite;
    sleep(2);   //等待子程式先執行
    /* 以只寫阻塞方式開啟 FIFO 管道 */
    fd = open(MYFIFO, O_WRONLY | O_CREAT, 0644);
    if (fd == -1)
    {
        printf("Open fifo file error\n");
        exit(1);
    }
    printf("Write '%s' to FIFO\n", buff);
    /*向管道中寫入字串*/
    nwrite = write(fd, buff, MAX_BUFFER_SIZE);
    if(wait(NULL))  //等待子程式退出
    {
        close(fd);
        exit(0);
    }
}

int main()
{
    pid_t result;
    /*呼叫 fork()函式*/
    result = fork();
    /*通過 result 的值來判斷 fork() 函式的返回情況,首先進行出錯處理*/
    if(result == -1)
    {
        printf("Fork error\n");
    }
    else if (result == 0) /*返回值為 0 代表子程式*/
    {
        fifo_read();
    }
    else /*返回值大於 0 代表父程式*/
    {
        fifo_write();
    }
    return result;
}

3. 訊號

3.1 概念及特徵

  • 訊號(signal)
    • 又稱為軟中斷訊號,用於通知程式發生了非同步事件
    • 它是Linux系統響應某些條件而產生的一個事件
    • 它是在軟體層次上對中斷機制的一種模擬
    • 是一種非同步通訊方式
    • 在原理上,一個程式收到一個訊號與處理器收到一箇中斷請求可以說是一樣的
  • 訊號是程式間通訊機制中唯一的非同步通訊機制
  • 訊號產生
    • 訊號可能是由於系統中某些錯誤而產生
    • 也可以是某個程式主動生成的一個訊號

3.2 系統支援的訊號

  • 查詢系統支援的訊號種類命令:kill -l
  • linux支援62種訊號(沒有 32 號和 33 號訊號
    • 非實時訊號(不可靠):1-32
      • 沒有排隊功能,訊號可能被丟棄
      • 不會立即執行
      • 先放入該程式控制塊(PCB),待合適的時候處理
    • 實時訊號(可靠訊號):34-64
      • 有排隊功能

3.3 訊號處理

  • 訊號類似可分為三大型別:程式錯誤、外部事件以及顯式請求
  • 當訊號發生時,訊號可以採取如下三種操作:
    • 忽略訊號(SIGTOP 和 SIGKILL 是絕不能被忽略的)
    • 捕獲訊號
    • 讓預設訊號起作用
      • 終止程式並且生成記憶體轉儲檔案
      • 終止終止程式但不生成core檔案
      • 忽略訊號
      • 暫停程式
      • 若程式是暫時暫停,恢復程式,否則將忽略訊號

3.4 傳送訊號函式

  • kill()
  • raise()
  • alarm()

3.4.1 kill()

  • 命令:kill [訊號或選項] PID(s)
  • 函式
    • 標頭檔案:
      #include <sys/types.h>
      #include <signal.h>
      
    • 函式原型:int kill(pid_t pid, int sig);
      • pid 取值如下
        • pid > 1:將訊號sig傳送到程式ID值為pid指定的程式
        • pid = 0:訊號被髮送到所有和當前程式在同一個程式組的程式
        • pid = -1:將sig傳送到系統中所有的程式,但程式1(init)除外
        • pid < -1:將訊號sig傳送給程式組號為-pid (pid絕對值)的每一個程式
      • sig 為 訊號值
      • 返回值
        • 0:傳送成功
        • -1:傳送失敗

3.4.2 raise()

  • raise() 函式為程式向自身傳送訊號
  • 函式
    • 標頭檔案
      #include <signal.h>
      
    • 函式原型:int raise(int sig);
      • sig 為 訊號值
      • 返回值
        • 0:傳送成功
        • -1:傳送失敗

3.4.3 alarm()

  • alarm() 稱為鬧鐘函式,設定時間為 seconds 秒,時間到後,它就向程式傳送SIGALARM訊號。在時間未到時便重新呼叫 alarm() 函式,會更新到時值。
  • 函式
    • 標頭檔案
      #include <unistd.h>
      
    • 函式原型:unsigned int alarm(unsigned int seconds);

3.5 捕獲訊號函式

  • signal()、sigaction()等函式

3.5.1 signal()

  • signal()主要是用於捕獲訊號,可以改變程式中對訊號的預設行為
  • 函式
    • 標頭檔案
      #include <signal.h>
      
    • 函式原型
      typedef void (*sighandler_t)(int);
      sighandler_t signal(int signum, sighandler_t handler);
      
    • signum 是指定捕獲的訊號,如果指定的是一個無效的訊號,或者嘗試處理的訊號是不可捕獲或不可忽略的訊號(如SIGKILL),errno將被設定為EINVAL
    • handler 是一個函式指標,它的型別是 void(*sighandler_t)(int) 型別
    • handler 也可以是一個巨集定義
      • SIG_IGN:忽略該訊號
      • SIG_DFL:採用系統預設方式處理訊號

3.5.2 sigaction() *

  • 不推薦讀者使用signal(),而推薦使用 sigaction();
  • 函式
    • 標頭檔案
      #include <signal.h>
      
    • 函式原型:int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
      • signum:指定捕獲的訊號值

      • act:是一個結構體

        • sa_handler 是一個函式指標,是捕獲訊號後的處理函式
        • sa_sigaction 是擴充套件訊號處理函式,它也是一個函式指標,不僅可以接收到int 型的訊號值,還會接收到一個 siginfo_t 類 型的結構體指標,還有一個void型別的指標,還有需要注意的就是,不要同時使用 sa_handlersa_sigaction,因為這兩個處理函式是有聯合的部分(聯合體)
        • sa_mask 是訊號掩碼,它指定了在執行訊號處理函式期間阻塞的訊號的掩碼,被設定在該掩碼中的訊號,在程式響應訊號期間被臨時阻塞。除非使用 SA_NODEFER 標誌,否則即使是當前正在處理的響應的訊號再次到來的時候也會被阻塞
        • re_restorer 則是一個已經廢棄的成員變數,不要使用
        • oldact 返回原有的訊號處理引數,一般設定為NULL即可
        • sa_flags 是指定一系列用於修改訊號處理過程行為的標誌
          • SA_NOCLDSTOP 使父程式在它的子程式暫停或繼續執行時不會收到 SIGCHLD 訊號。即當它們接收到SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU(停止)中的一種時或接收到SIGCONT(恢復)時,父程式不會收到通知
          • SA_NOCLDWAIT 從Linux 2.6開始就存在這個標誌了,它表示父程式在它的子程式終止時不會收到 SIGCHLD 訊號,這時子程式終止則不會成為殭屍程式。
          • SA_NODEFER 一般情況下, 當訊號處理函式執行時,核心將阻塞該給定訊號。但是如果設定了 SA_NODEFER標記, 那麼在該訊號處理函式執行時,核心將不會阻塞該訊號
          • SA_RESETHAND 訊號處理之後重新設定為預設的處理方式。
          • SA_SIGINFO 從Linux 2.2開始就存在這個標誌了,使用 sa_sigaction成員而不是使用sa_handler 成員作為訊號處理函式。
        struct sigaction {
                                 void     (*sa_handler)(int);
                                 void     (*sa_sigaction)(int, siginfo_t *, void *);
                                 sigset_t   sa_mask;
                                 int        sa_flags;
                                 void     (*sa_restorer)(void);
             };
        
        • siginfo_t
        siginfo_t {
           int      si_signo;     /* 訊號數值 */
           int      si_errno;     /* 錯誤值 */
           int      si_code;      /* 訊號程式碼 */
           int      si_trapno;   /*導致硬體生成訊號的陷阱號,在大多數體系結構中未使用*/
           pid_t    si_pid;       /* 傳送訊號的程式ID */
           uid_t    si_uid;       /*傳送訊號的真實使用者ID */
           int      si_status;    /* 退出值或訊號狀態*/
           clock_t  si_utime;     /*消耗的使用者時間*/
           clock_t  si_stime;     /*消耗的系統時間*/
           sigval_t si_value;     /*訊號值*/
           int      si_int;       /* POSIX.1b 訊號*/
           void    *si_ptr;
           int      si_overrun;   /*計時器溢位計數*/
           int      si_timerid;   /* 計時器ID */
           void    *si_addr;      /*導致故障的記憶體位置 */
           long     si_band;
           int      si_fd;        /* 檔案描述符*/
           short    si_addr_lsb;  /*地址的最低有效位 (從Linux 2.6.32開始存在) */
           void    *si_lower;     /*地址衝突時的下限*/
           void    *si_upper;     /*地址衝突時的上限 (從Linux 3.19開始存在) */
           int      si_pkey;      /*導致的PTE上的保護金鑰*/
           void    *si_call_addr; /*系統呼叫指令的地址*/
           int      si_syscall;   /*嘗試的系統呼叫次數*/
           unsigned int si_arch;  /* 嘗試的系統呼叫的體系結構*/
        }
        

3.6 訊號集

  • 資料型別 sigset_t 是訊號集,訊號掩碼就是這種型別
  • 標頭檔案:#include <signal.h>
  • 函式
    1. int sigemptyset(sigset_t *set);
      • 將訊號集初始化為空,使程式不會遮蔽任何訊號
    2. int sigfillset(sigset_t *set);
      • 將訊號集初始化為包含所有已定義的訊號
    3. int sigaddset(sigset_t *set, int signum);
      • 新增一個訊號到訊號集中
    4. int sigdelset(sigset_t *set, int signum);
      • 從訊號集中刪除一個訊號
    5. int sigismember(const sigset_t *set, int signum);
      • 判斷一個訊號是否在訊號集中
  • 注意:
    • 一個應用程式,在使用訊號集前,必須對其進行初始化,即是呼叫 sigemptyset()sigfillset()

3.7 例子

  • 例程來自野火
  • 實驗現象
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

/** 訊號處理函式 **/void signal_handler(int sig)                    //(1){
    printf("\nthis signal numble is %d \n",sig);
    if (sig == SIGINT) {
        printf("I have get SIGINT!\n\n");
        printf("The signal is automatically restored to the default handler!\n\n");
        /** 訊號自動恢復為預設處理函式 **/
    }

}
int main(void){
    struct sigaction act;
    printf("this is sigaction function test demo!\n\n");
    /** 設定訊號處理的回撥函式 */
    act.sa_handler = signal_handler;
    /* 清空遮蔽訊號集 */
    sigemptyset(&act.sa_mask);
    /** 在處理完訊號後恢復預設訊號處理 */
    act.sa_flags = SA_RESETHAND;
    sigaction(SIGINT, &act, NULL);
    while (1)
    {
        printf("waiting for the SIGINT signal , please enter \"ctrl + c\"...\n\n");
        sleep(1);
    }
    exit(0);
}

參考:

  * 野火

相關文章