linux 進程概念

来源:https://www.cnblogs.com/xiaoshiwang/archive/2019/04/29/10788716.html
-Advertisement-
Play Games

1,pcb:進程式控制制塊結構體:/usr/src/linux headers 4.15.0 29/include/linux/sched.h 進程id:系統中每個進程有唯一的id,在c語言中用pid_t類型表示,是個非負整數。 進程狀態:就緒,運行,掛起,停止等狀態 描述虛擬地址空間的信息 描述控制終 ...


1,pcb:進程式控制制塊結構體:/usr/src/linux-headers-4.15.0-29/include/linux/sched.h

  • 進程id:系統中每個進程有唯一的id,在c語言中用pid_t類型表示,是個非負整數。

  • 進程狀態:就緒,運行,掛起,停止等狀態

  • 描述虛擬地址空間的信息

  • 描述控制終端的信息

  • 進程執行時的當前工作目錄(current working directory)

  • umask掩碼

  • 文件描述符表,包含很多指向file結構體的指針

  • 和信號相關的信息

  • 用戶id和組id

  • 會話(session)和進程組

  • 進程可以使用的資源上限(Resource Limit)

    用【ulimit -a】查看:

    ys@ys:~$ ulimit -a
    core file size          (blocks, -c) 0
    data seg size           (kbytes, -d) unlimited
    scheduling priority             (-e) 0
    file size               (blocks, -f) unlimited
    pending signals                 (-i) 7743
    max locked memory       (kbytes, -l) 16384
    max memory size         (kbytes, -m) unlimited
    open files                      (-n) 1024
    pipe size            (512 bytes, -p) 8
    POSIX message queues     (bytes, -q) 819200
    real-time priority              (-r) 0
    stack size              (kbytes, -s) 8192
    cpu time               (seconds, -t) unlimited
    max user processes              (-u) 7743
    virtual memory          (kbytes, -v) unlimited
    file locks                      (-x) unlimited

2,環境變數

查看所有的環境變數:env

環境寫法:等號前後沒有空格

KEY=VALUE

常用的環境變數:

  • PATH:可執行文件的搜索路徑。比如可以直接敲ls,那是因為ls的可執行文件在環境變數PATH里,所以系統找得到。但是比如a.out執行文件,就不能直接敲a.out,因為a.out不在PATH里。
  • SHELL:當前使用的shell程式,通常是:/bin/bash
  • TERM:當前使用的終端類型,如果是圖像界面的話,一般為:xterm-256color
  • LANG:當前的語言和字元編碼,一般為:en_US.UTF-8
  • HOME:當前用戶所在的絕對路徑。

獲取環境變數:getenv

#include <stdlib.h>
char *getenv(const char *name);

返回值:

  • 成功:返回指針

  • 失敗:返回NULL

設置環境變數:setenv

#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);

刪除環境變數:unsetenv

#include <stdlib.h>
int unsetenv(const char *name);

設置環境變數和刪除環境變數一般不用系統函數,而是編輯【~/.bashrc】文件。

使用【exprot key=value】。

3,進程概念

創建進程:fork

#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
  • 返回值
    • 失敗:-1
    • 成功:返回2次
      • 父進程中返回:子進程的ID
      • 子進程返回:0

獲得當前進程自己id:getpid

獲得當前進程的父進程的id:getppid

ss
#include <unistd.h>

pid_t getpid(void);
pid_t getppid(void);

理解fork的小例子:

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

int main(){
  //char* val = getenv("HOME");
  printf("begin...\n");
  pid_t pid = fork();
  printf("end...\n");
}

執行結果:

ys@ys:~/test$ ./en 
begin...
end...
ys@ys:~/test$ end...

從執行結果發下:

1,【begin】被列印出1次;【end】被列印出2次。

2,第二個【end】跑到了shell的後面。

分析:

  • 從執行結果1可以看出來,fork後,從1個進程,又分出了一個子進程。並且子進程不是從程式的開頭開始執行,而是從fork後面開始執行,這個理解至關重要。
  • 執行結果2現象的解釋,根本原因是父進程比子進程先執行結束了,導致了子進程成為了孤兒進程。最開始shell進程把執行權交給程式,也就是父進程,當父進程結束後,shell又接管終端,可是這個時間點,子進程還沒結束,子進程的列印語句就把【end】列印到了shell後面。

下麵的例子可以瞭解,getpid和getppid的用法:

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

int main(){
  //char* val = getenv("HOME");
  printf("begin...\n");
  pid_t pid = fork();

  //child process
  if(pid == 0){
    printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
  }
  //parent proces
  else if(pid > 0){
    printf("parent: pid=%d, child id:%d\n", getpid(), pid);
    sleep(1);//為了讓子進程先結束
  }
  printf("end...\n");
}

查看進程的命名:ps

  • 選項a:顯示更多信息
  • 選項u:顯示用戶信息
  • 選項x:和選項a一起使用
  • 選項j:能看到父進程
ys@ys:~$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.3 159928  7536 ?        Ss   13:53   0:02 /sbin/init spla
root         2  0.0  0.0      0     0 ?        S    13:53   0:00 [kthreadd]
ys        4029  0.0  0.0   4508   812 pts/0    S+   16:30   0:00 ./en
ys        4030  0.0  0.0   4508    80 pts/0    S+   16:30   0:00 ./en
ys        4043  0.1  0.2  29560  4936 pts/1    Ss   16:30   0:00 bash
ys        4051  0.0  0.1  44472  3700 pts/1    R+   16:30   0:00 ps aux

從下圖可以看出子進程4030的父進程是4029,父進程4029的父進程是1671,進程1671是bash(shell)程式。所以的進程都是從進程1:【/sbin/init splash】衍生出來的。

ys@ys:~$ ps ajx
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    0     1     1     1 ?           -1 Ss       0   0:02 /sbin/init splash
 1661  1671  1671  1671 pts/0     4029 Ss    1000   0:00 bash
 1671  4029  4029  1671 pts/0     4029 S+    1000   0:00 ./en
 4029  4030  4029  1671 pts/0     4029 S+    1000   0:00 ./en
 1661  4043  4043  4043 pts/1     4055 Ss    1000   0:00 bash
 4043  4055  4055  4043 pts/1     4055 R+    1000   0:00 ps ajx

給進程發送信號:kill

使用【kill -l】查看信號列表,發現9號信號就殺死進程的信號。

ys@ys:~$ kill -l
 1) SIGHUP   2) SIGINT   3) SIGQUIT  4) SIGILL   5) SIGTRAP
 6) SIGABRT  7) SIGBUS   8) SIGFPE   9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG  24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF 28) SIGWINCH    29) SIGIO   30) SIGPWR
31) SIGSYS  34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

由父進程創建5個子進程的例子:

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

int main(){
  int i = 0;
  pid_t pid = 0;
  for(i = 0; i < 5; ++i){
    pid = fork();
    if(pid == 0){
      //child process
      //printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
      break;
    }
    else if (pid > 0){
      //parent process
      //printf("parent: pid=%d, ppid=%d\n", getpid(), getppid());
    }
  }

  sleep(i);
  printf("proces:%d will die:pid=%d,ppid=%d\n", i,getpid(), getppid());
  
}

執行結果:

ys@ys:~/test$ ./fo
proces:0 will die:pid=2139,ppid=2138
proces:1 will die:pid=2140,ppid=2138
proces:2 will die:pid=2141,ppid=2138
proces:3 will die:pid=2142,ppid=2138
proces:4 will die:pid=2143,ppid=2138
proces:5 will die:pid=2138,ppid=1704

要點在【break】和【sleep(i)】這2個地方。

不加break的話,子進程也會創建子進程。

觀察【sleep(i);]這行,執行到這行的時間點,如果i=0,則說明是第一創建的子進程;

如果i=4,說明是最後創建的子進程,如果i=5,說明是父進程。所以根據i的值,進行sleep,就能夠實現先創建的子進程先執行完,後創建的後執行完,最後才是父進程執行完。

在當進程調用別的程式:execl和execlp

#include <unistd.h>
int execl(const char *path, const char *arg, .../* (char  *) NULL */);
int execlp(const char *file, const char *arg, .../* (char  *) NULL */);
  • execl
    • path:目標程式的所在路徑和程式名。例:【/bin/ls】
    • arg:目標程式的參數。註意第一個參數必須是程式自己的名字。
    • NULL:最後一個參數必須是NULL,告訴它參數列表到這裡結束了。
    • 返回值:
      • 成功:就不返回了,直接走了,也就是說,調用execl的語句之後的代碼都不被執行了。
      • 失敗:-1
  • execlp:多了個p的含義就是:這個函數自己會去搜索環境變數PATH。
    • file:目標程式名,不用加路徑。例:【ls】
    • arg:目標程式的參數。註意第一個參數必須是程式自己的名字。
    • NULL:最後一個參數必須是NULL,告訴它參數列表到這裡結束了。
    • 返回值:
      • 成功:就不返回了,直接走了,也就是說,調用execl的語句之後的代碼都不被執行了。
      • 失敗:-1

調用ls的例子:

#include <unistd.h>
#include <stdio.h>
int main(){
  execl("/bin/ls", "ls", "-l", "--color=auto", NULL);
  perror("ls");
  printf("not back\n");
}

孤兒進程和僵屍進程

孤兒進程:父進死了,被init進程領養,變成孤兒進程。

僵屍進程:子進程死了,但父進程沒有回收子進程的資源(pcb(大結構體)),變成僵屍進程。

回收僵屍進程的方法:不能再用kill去殺,殺死父進程,讓init領養,init負責回收。

查看僵屍進程:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
 1704  2683  2683  1704 pts/0     2683 S+    1000   0:00 ./zo
 2683  2684  2683  1704 pts/0     2683 Z+    1000   0:00 [zo] <defunct>

發現有【Z+】和《defunct》就是僵屍進程。

回收子進程:wait函數

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *wstatus);
  • 作用:

    • 阻塞等待(如果子線程還沒有死亡,則一直等到它死亡)
    • 回收子進程的資源(PCB結構體)
    • 查看死亡的原因
  • wstatus:傳出參數,裡面有子進程死亡的原因。

    查看wstatus的方法:使用下麵2組巨集。

    WIFEXITED(wstatus)
    WEXITSTATUS(wstatus)
    
    WIFSIGNALED(wstatus)
    WTERMSIG(wstatus)
    • 正常死亡:WIFEXITED(wstatus)返回真,使用WEXITSTATUS(wstatus)得到退出狀態。

      退出狀態的具體含義:return 後面的數字;或者exit 括弧里的數字。

    • 非正常死亡(被信號殺死):WIFSIGNALED(wstatus)返回真,使用WTERMSIG(wstatus)得到殺死它的信號(kill -l 顯示出來的數字)。

  • 返回值:

    • 成功:返回死亡子進程的pid
    • 失敗:-1

wait的例子:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <wait.h>
#include <stdlib.h>

int main(){
  int i = 0;
  pid_t pid = 0;

  pid = fork();
  if(pid == 0){
    //child process
    while(1){
      printf("child: pid=%d, ppid=%d\n", getpid(), getppid());
      sleep(3);
      //return 101;
      //exit(111);
      //sleep(2);
    }
  }
  else if (pid > 0){
    //parent process
    printf("parent: pid=%d, ppid=%d\n", getpid(), getppid());
    int wstatus;
    pid_t pid = wait(&wstatus);
    if(WIFEXITED(wstatus)){
      printf("child die pid=%d, status=%d\n",pid, WEXITSTATUS(wstatus));
    }
    if(WIFSIGNALED(wstatus)){
      printf("child die pid=%d, status=%d\n",pid, WTERMSIG(wstatus));
    }
  }
}

分析:

  • 正常死亡,代碼用return和exit來模擬的
  • 被信號殺死:在另一終端里使用【kill pid】後,返回的WTERMSIG(wstatus)為15(SIGTERM);如果用【kill -9 pid】則WTERMSIG(wstatus)為9(SIGKILL)

回收子進程:waitpid

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *wstatus, int options);
  • pid
    • < -1:回收組ID為-pid的子進程
    • -1:回收所有的子進程
    • 0:回收和調用進程的組id相同的組id的所有子進程。
    • 》0:回收指定pid的子進程
  • wstatus:和wait一樣
  • options:
    • 0:與wait功能相同。
    • WNOHANG:如果執行waitpid的時點,子進程還沒結束,會立刻返回,不阻塞。
  • 返回值:返回狀態發生了變化的子進程的id。
    • 調用時設置了WNOHANG並且還指定了1個或者多個子進程
      • 如果有子進程退出了,返回退出了的子進程的pid
      • 如果沒有子進程退出,返回0
    • 失敗:-1(沒有子進程了,子進程都被回收完了)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、後臺運行jar包程式,輸入:nohup java -jar /路徑/程式.jar & 2、後臺終止jar包程式,輸入:ps -ef | grep java,查看使用java命令的進程,再輸入:kill pid 即可終止運行 ps -ef|grep指令介紹:ps命令:將某個進程顯示出來,是Linu ...
  • 零、說明 對應代碼drivers/mmc/core/host.c,drivers/mmc/core/host.h。 為底層host controller driver實現mmc host的申請以及註冊的API等等,以及host相關屬性的實現。 一、API總覽 1、mmc host分配、註冊相關 mm ...
  • 零、說明 對應代碼drivers/mmc/core/bus.c。 抽象出虛擬mmc bus,實現mmc bus的操作。 一、API總覽 1、mmc bus相關 mmc_register_bus & mmc_unregister_bus 用於註冊和卸載mmc bus(虛擬mmc匯流排)到設備驅動模型中。 ...
  • 一、host相關 1、struct mmc_host struct mmc_host是mmc core由host controller抽象出來的結構體,用於代表一個mmc host控制器。 數據結構如下: ocr值各個位代表的電壓意義如下: host屬性2(mmc_host caps2)支持的屬性如 ...
  • 一、概念 1、mmc的概念 mmc有很多種意義,具體如下: mmc MultiMedia Card,多媒體存儲卡, 但後續泛指一個介面協定(一種卡式),能符合這介面的記憶體器都可稱作mmc儲存體。 主要特性如下: 工作電壓:高電壓為2.7~3.6 V,低電壓為1.65~1.95 V,可選。 mmc匯流排 ...
  • 如圖: 1、查找USE_STDPERIPH_DRIVER,發現這個巨集出現在stm32f4xx.h頭文件中,並且有如下代碼: 也就是說,通過已經定義了USE_STDPERIPH_DRIVER巨集載入stm32f4xx_conf.h這個頭文件; 而stm32f4xx_conf.h文件中包含了各種外設驅動頭 ...
  • mv要是不明白什麼意思,你就把它想象成Windows裡面剪切文件夾/文件,然後再去粘貼的操作,你就會明白的。 1. 移動一個文件夾(rightr文件夾,移動到/201904/a目錄) 出現這個錯誤的原因是:文件夾命名重覆,移動的這個文件夾與移動到文件夾裡面的文件夾命名重覆 開始敲一個命令就出現一個錯 ...
  • 1.簡述 Yellow dog Updater, Modified由Duke University團隊,修改Yellow Dog Linux的Yellow Dog Updater開發而成,是一個基於RPM包管理的字元前端軟體包管理器。能夠從指定的伺服器自動下載RPM包並且安裝,可以處理依賴性關係,並 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...