1. 程式人生 > >十八、Linux 進程與信號---進程介紹

十八、Linux 進程與信號---進程介紹

控制 HR struct bsp 進程控制 start 否則 文件寫入 內核

18.1 進程的概念

  • 程序:程序(program)是存放再磁盤文件中的可執行文件
  • 進程
    • 程序的執行實例被稱為進程(process)
    • 一個程序的執行實例可能由多個
    • 進程具有獨立的權限和職責。如果系統中某個進程崩潰,它不會影響到其余的進程。
    • 每個進程運行在其各自的虛擬地址空間中,進程之間可以通過由內核控制的機制相互通訊
  • 進程ID
    • 每個 Linux 進程都一定由一個唯一的數字標識符,稱為進程ID(process ID),進程ID總是一非負整數   

18.2 內核中的進程結構體

  每一個啟動進程都有一個 task_struct 結構,這是個結構體,命名為進程表項(或 進程控制塊)如下:

  技術分享圖片

  啟動一個程序操作文件的過程如下:進程啟動創建一個 task_struct 進程表項,進程表項中有一個成員指向文件描述符表

  技術分享圖片

18.3 進程的啟動和終止

  進程的啟動和退出如下:

  main 運行的時候,內核會幫忙啟動一個例程(C start template),啟動例程會幫忙啟動 main 函數,並幫忙收集命令行參數,環境參數等等,調用結束以後,main 返回給啟動例程。

  若是調用 exit() 函數,就不會返回給main 函數或其他函數了。exit() 直接調用內核所提供的系統調用退出函數。

  atexit 函數為註冊退出函數。

  技術分享圖片

18.3.1 內核啟動特殊例程

  • 啟動例程
    • 在進程的 main 函數執行之前內核會啟動
    • 該例程放置在 /lib/libc.so.*** 中
    • 編譯器在編譯時會將啟動例程編譯進可執行文件中
  • 啟動例程的作用

    • 搜集命令行的參數傳遞給 main 函數中的 argc 和 argv
    • 搜集環境信息構建環境表並傳遞給 main 函數
    • 登記進程的終止函數  

18.3.2 進程的終止方式

  • 正常終止

    • 從main 函數返回 return
    • 調用 exit (標準C 庫函數)
    • 調用 _exit 或 _Exit(系統調用)
    • 最後一個線程從其啟動例程返回
    • 最後一個線程調用 pthread_exit  
  • 異常終止

    • 調用 abort
    • 接受一個信號並終止
    • 最後一個線程對取消請求做處理響應  
  • 進程返回   

    • 通常程序運行成功返回0,否則返回 非 0
    • 在 shell 中可以查看進程返回值( echo $?) 

  進程終止方式區別

  技術分享圖片

18.3.2.1 內核登記進程終止函數---atexit 函數

1 #include <stdlib.h>
2 int atexit (void (*function)(void));
  • 相關函數 _exit,exit,on_exit
  • 函數說明:
    • atexit()用來設置一個程序正常結束前調用的函數。
    • 當程序通過調用exit()或從main中返回時,參數function所指定的函數會先被調用,然後才真正由exit()結束程序。
  • 函數功能
    • 向內核登記終止函數  
  • 返回值:
    • 如果執行成功則返回0,否則返回-1,失敗原因存於errno中。
  • 其他說明
    • 每個啟動的進程都默認登記了一個標準的終止函數
    • 終止函數在進程終止時釋放進程所占用的一些資源
    • 登記的多個終止函數執行順序是以 棧的方式執行,先登記的後執行  

18.3.2.2 例子

 

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <string.h>
 4 #include <stdlib.h>
 5 #include <fcntl.h>
 6 
 7 //定義進程的終止函數
 8 void term_fun1(void)
 9 {
10     printf("first term function\n");
11 }
12 
13 void term_fun2(void)
14 {
15     printf("second term function\n");
16 }
17 
18 void term_fun3(void)
19 {
20     printf("third term function\n");
21 }
22 
23 int main(int argc, char *argv[])
24 {
25     if(argc < 3) {
26         fprintf(stderr, "usage: %s file [exit | _exit | return]\n", argv[0]);
27         exit(1);
28     }
29 
30     //向進程登記終止函數
31     atexit(term_fun1);
32     atexit(term_fun2);
33     atexit(term_fun3);
34 
35     FILE *fp = fopen(argv[1], "w+");
36     fprintf(fp, "hello world");
37 
38     if(!strcmp(argv[2], "exit")) {
39         exit(0);//標準C 的庫函數
40     } else if(!strcmp(argv[2], "_exit")) {
41         _exit(0);//系統調用
42     } else if(!strcmp(argv[2], "return")) {
43         return 0;
44     } else {
45         fprintf(stderr, "usage: %s file [exit | _exit | return]\n", argv[0]);
46     }
47 
48     exit(0);
49 }

  技術分享圖片

  技術分享圖片

  技術分享圖片

  結果是前兩種可以正常運行,並且對文件可以正常寫入,但是第三種方式無輸出,且文件創建了,但沒有寫入。

  而且可以看見終止方式是以棧的方式進行調用的。

  可以知道選用 _EXIT和_exit 系統調用的時候,不會進行調用。之所以沒有輸出,代碼中向文件寫入數據的時候,表現為全緩存,這些數據可能還存放在緩存中,並沒有從緩存中釋放出來。帶代碼中加入 fclose 函數或程序終止後,會自動清緩存,但是系統調用 _exit 和 _EXIT 不會自動刷新緩存。

 

十八、Linux 進程與信號---進程介紹