tft每日頭條

 > 科技

 > linux進程使用教程學習

linux進程使用教程學習

科技 更新时间:2024-09-07 09:53:11

linux進程使用教程學習(關于Linux進程編程入門)1

Linux 進程篇一、進程相關概念

了解進程的時候先來了解幾個問題,明白以下問題,就懂了進程的概念

1.什麼是程序,什麼是進程,兩者之間的區别?
  1. 程序是靜态的概念,gcc xxx.c -o pro 磁盤中生成pro文件,叫做程序 程序如:電腦上的圖标
  2. 進程是程序的一次運行活動, 通俗點說就是程序跑起來了,系統中就多了一個進程
2.如何查看系統中有哪些進程?
  1. 使用ps指令查看 : ps-aux 在ubuntu下查看, 在實際工作中,配合grep來查找程序中是否存在某一個進程grep 過濾進程 :ps -aux | grep init 就隻把帶有init的進程過濾出來
  2. 使用top指令查看,類似windows任務管理器
3.什麼是進程标識符?

每一個進程都有一個非負整數表示的唯一ID,叫做pid,類似身份證

pid =0 :稱為交換進程(swapper)作用:進程調度pid=1 :init 進程作用:系統初始化

  • 編程調用getpid函數獲取自身的進程标識符;

#include<sys/types.h> #include<unistd.h> pid_t getpid(void); pid_t getppid(void);

getpid示例代碼:

#include<stidio.h> #include<sys/types.h> #include<unistd.h> int main() { pid_t pid; pid = getpid(); printf("my pid is %d\n",pid); return 0; }

  • getppid獲取父進程的進程标識符;
4. 第一個進程 init 進程

linux進程使用教程學習(關于Linux進程編程入門)2

在這裡插入圖片描述

Linux内核啟動之後,會創建第一個用戶級進程init,由上圖可知, init 進程 (pid=1) 是除了 idle 進程(pid=0,也就是 init_task) 之外另一個比較特殊的進程,它是 Linux 内核開始建立起進程概念時第一個通過 kernel_thread 産生的進程,其開始在内核态執行,然後通過一個系統調用,開始執行用戶空間的 / sbin/init 程序。

5.什麼叫父進程,什麼叫子進程?

進程A創建了進程B,那麼A叫做父進程,B叫做子進程,父進程是相對的概念,理解為人類中的父子關系

6. c程序的存儲空間是如何分配的?

gcc xxx.c -o a.out 當執行 ./a.out 時候,操作系統會劃分一塊内存空間,如何分配呢?如下圖:

linux進程使用教程學習(關于Linux進程編程入門)3

二、創建進程函數fork的使用

==pid_t fork(void);== 功能:使用fork函數創建一個進程

fork函數調用成功,返回兩次 返回值為0 ,代表當前進程是子進程 返回值非負數,代表當前進程為父進程 調用失敗 ,返回-1

1. fork();示例代碼

#include<stdio.h> #include<sys/types.h> #include<unistd.h> int main() { pid_t pid; pid = getpid(); fork(); printf("my pid is %d\n",pid); return 0; }

打印出了兩遍 my pid 說明,有了兩個進程!執行了兩次打印pid

linux進程使用教程學習(關于Linux進程編程入門)4

2. 查看父進程/子進程代碼:#include<stdio.h>#include<sys/types.h>#include<unistd.h>int main(){pid_t pid;pid_t pid2;pid = getpid();printf("brfore fork pid is %d\n",pid);fork();pid2 = getpid();printf("brfore fork pid is %d\n",pid2);if(pid == pid2){printf("this is father print\n");}else{printf("this is child print , child pid is =%d\n",getpid());}return 0;}

linux進程使用教程學習(關于Linux進程編程入門)5

父子進程都會進入if 中,但是輸出結果會不同 在fork之前的pid 是8915 是父進程 ,fork之後pid是子進程 8916

3. 用返回值來判斷父/子進程代碼(1):

返回值為0 ,代表當前進程是子進程 返回值非負數,代表當前進程為父進程

#include<sys/types.h> #include<stdio.h> #include<unistd.h> int main() { pid_t pid; printf("father: id=%d\n",getpid()); pid = fork(); if(pid > 0){ printf("this is father print ,pid =%d\n",getpid()); }else if (pid == 0){ printf("this is child print, child pid = %d\n",getpid()); } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)6

在這裡插入圖片描述

4. 用返回值來判斷父子進程代碼(2):#include<sys/types.h>#include<stdio.h>#include<unistd.h>int main(){pid_t pid;pid_t pid2;pid_t retpid;pid = getpid();printf("before fork: pid = %d\n",pid);retpid = fork();pid2 = getpid();printf("after fork:pid = %d\n",pid2);if(pid == pid2){printf("this is father print :retpid = %d\n",retpid);}else{printf("this is child print :retpid =%d,child pid= %d\n",retpid,pid2);}return 0;}

linux進程使用教程學習(關于Linux進程編程入門)7

這樣更清楚明了的看到fork 返回值:9915>0 是父進程 父進程号是9114 fork 返回值:=0 是子進程 子進程号是9915

三、進程創建後 發生了什麼事?

linux進程使用教程學習(關于Linux進程編程入門)8

在這裡插入圖片描述

1 在内存空間中fork後發生了什麼?

linux進程使用教程學習(關于Linux進程編程入門)9

在這裡插入圖片描述

2. ./demo4 運行的程序父進程是誰?int main(int argc, const char *argv[]){while(1);return 0;}

./ demo4 編譯運行後,我們ps -ef 查看進程ID

linux進程使用教程學習(關于Linux進程編程入門)10

由上圖可知,./demo4 進程的進程ID是12677,父進程ID是12587,即進程bash:==bash的父進程是gnome-terminal,所以我們打開1個Linux終端,其實就是啟動了1個gnome-terminal進程。我們在這個終端上執行./a.out其實就是利用gnome-terminal的子進程bash通過execve()将創建的子進程裝入a.out:==

四、創建新進程的實際應用場景1. fork創建子進程的一般目的:
  • 一個父進程希望複制自己,使父、子進程同時執行不同的代碼段。這在網絡服務進程中是常見的——父進程等待客戶端的服務請求。當這種情求達到時,父進程調用fork,使子進程處理此請求。父進程則繼續等待下一個服務請求到達。
  • 一個進程要執行一個不同的程序。這對shell是常見的情況,在這種情況下子進程從fork返回後立即調用exec。

linux進程使用教程學習(關于Linux進程編程入門)11

在這裡插入圖片描述

2. 模拟socket 創建進程(服務器對接客戶端的應用場景)示例代碼:#include<stdio.h>#include<unistd.h>#include<sys/types.h>int main(){pid_t pid;int data;while(1){printf("please input a data\n");scanf("%d",&data);if(data ==1 ){pid = fork();if(pid >0){}else if(pid == 0){while(1){printf("do net request,pid=%d\n",getpid());sleep(3);}}}else{printf("wait, do noting\n");}}return 0;}

輸入非1時候,模拟沒有客戶端進行交互

linux進程使用教程學習(關于Linux進程編程入門)12

輸入1時候,模拟有客戶端進行交互 ,創建子進程來進行交互,子進程号為:9756

linux進程使用教程學習(關于Linux進程編程入門)13

模拟多個客戶端進行交互時 ,創建多個子進程來進行交互,子進程号為:9756 / 9758 / 9759

linux進程使用教程學習(關于Linux進程編程入門)14

查看系統進程:

linux進程使用教程學習(關于Linux進程編程入門)15

3. fork總結:

一個現有進程可以調用fork函數創建一個新進程。

#include cunistd.h> pid_t fork(void); 返回值:子進程中返回0。父進程中返回子進程ID.出錯返回-1

由fork創建的新進程被稱為子進程(child process)。fork函數被調用一次,但返回兩次。兩次返回的唯一區别是子進程的返回值是0,而父進程的返回值則是新子進程的進程ID。将子進程ID返回給父進程的理由是:因為一個進程的子進程可以有多個,并且沒有一個函數使一個進程可以獲得其所有子進程的進程ID。fork使子進程得到返回值0的理由是:一個進程隻會有一個父進程,所以子進程總是可以調用getppid以獲得其父進程的進程ID(進程ID0總是由内核交換進程使用,所以一個子進程的進程ID不可能為0)。

子進程和父進程繼續執行fork調用之後的指令。子進程是父進程的副本。例如,子進程獲得父進程數據空間、堆和棧的副本。注意,這是子進程所擁有的副本。父、子進程并不共享這些存儲空間部分。父、子進程共享正文段。由于在fork之後經常跟随着exec,所以現在的很多實現并不執行一個父進程數據段、棧和堆的完全複制。作為替代,使用了寫時複制(Copy-On-Write,COW)技術。這些區域由父、子進程共享,而且内核将它們的訪問權限改變為隻讀的。如果父、子進程中的任一個試圖修改些區域,則内核隻為修改區域的那塊内存制作一個副本,通常是虛拟存儲器系統中的一“頁”。Bach和McKusick等對這種特征做了更詳細的說明。

五、vfork創建進程1. vfork函數 也可以創建進程,與fork有什麼區别?

關鍵區别一:vfork直接使用父進程存儲空間,不用拷貝關鍵區别二:vfork保證子進程先運行,當子進程調用exit退出後,父進程才執行

2. fork 進程調度 父子進程:

#include<stdio.h> #include<sys/types.h> #include<unistd.h> int main() { pid_t pid; pid = fork(); if(pid >0){ while(1){ printf("this is father print pid is %d\n",getpid()); sleep(3); } }else if(pid == 0){ while(1){ printf("this is child print pid is =%d\n",getpid()); sleep(3); } } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)16

在這裡插入圖片描述

3. vfork 進程調度 父子進程:#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>int main(){pid_t pid;int cnt=0;pid = vfork();if(pid >0){while(1){printf("this is father print pid is %d\n",getpid());sleep(1);}}else if(pid == 0){while(1){printf("this is child print pid is =%d\n",getpid());sleep(1);cnt ;if(cnt == 3 ){exit(0);}}}return 0;}

vfork保證子進程先運行,當子進程調用3次 exit退出後,父進程才執行

linux進程使用教程學習(關于Linux進程編程入門)17

4. 子進程改變cnt值,在父進程運行時候也被改變#include<stdio.h>#include<sys/types.h>#include<unistd.h>#include<stdlib.h>int main(){pid_t pid;int cnt=0;printf("cnt=%d\n",cnt);pid = vfork();if(pid >0){while(1){printf("cnt=%d\n",cnt);printf("this is father print pid is %d\n",getpid());sleep(1);}}else if(pid == 0){while(1){printf("this is child print pid is =%d\n",getpid());sleep(1);cnt ;if(cnt == 3 ){exit(0);}}}return 0;}

linux進程使用教程學習(關于Linux進程編程入門)18

在這裡插入圖片描述

六、ps 常帶的一些參數

下面對ps命令選項進行說明:

命令參數說明-e顯示所有進程.-f全格式。-h不顯示标題。-l長格式。-w寬輸出。-a顯示終端上的所有進程,包括其他用戶的進程。-r隻顯示正在運行的進程。-u以用戶為主的格式來顯示程序狀況。-x顯示所有程序,不以終端機來區分。

ps -ef 顯示所有進程,全格式形式查看進程:

ps -ef 的每列的含義是什麼呢?

linux進程使用教程學習(關于Linux進程編程入門)19

在這裡插入圖片描述

命令參數說明UID:程序被該 UID 所擁有,指的是用戶IDPID:就是這個程序的 IDPPID :PID的上級父進程的IDC :CPU使用的資源百分比STIME :系統啟動時間TTY:登入者的終端機位置TIME :使用掉的 CPU時間。CMD:所下達的指令為何

七、進程退出1. 子進程退出方式

正常退出:

  1. Mian函數調用return
  2. 進程調用exit(),标準c庫
  3. 進程調用_exit()或者——Exit(),屬于系統調用
  4. 進程最後一個線程返回
  5. 最後一個線程調用pthread_exit

異常退出:

  1. 調用abort
  2. 當進程收到某些信号時候,如ctrl C
  3. 最後一個線程對取消(cancellation),請求作出響應

不管進程如何終止,最後都會執行内核中的同一段代碼。這段代碼為相應進程關閉所有打開描述符,釋放它所使用的存儲器等。

對上述任意一種終止情形,我們都希望終止進程能夠通知其父進程它是如何終止的。對于三個終止函數(exit、_exit和_Exit),實現這一點的方法是,将其退出狀态作為參數傳送給函數。【如上面示例裡面寫到的cnt==3情況下,exit(0); 這個0就是子進程退出狀态。】在異常終止情況下,内核(不是進程本身)産生一個指示其異常終止原因的終止狀态。在任何一種情況下,該終止進程的父進程都能用wait或者waitpid取得其終止狀态。

正常退出的三個函數:

#include<stdlib.h> void exit(int status); #include<unistd.h> void _exit(int status); #include<stdlib.h> void _Exit(int status);

記得在結束子進程的時候要手動退出,不要使用break;會導緻數據被破壞。 三種退出函數種,更推薦exit(); exit是 _exit 和_Exit 的一個封裝, 會清除,沖刷緩沖區,把緩存區數據進程處理在退出。

2. 等待子進程退出

==為什麼要等待子進程退出?==

創建子進程的目的就是為了讓它去幹活,在網絡請求當中來了一個新客戶端介入,創建子進程去交互,幹活也要知道它幹完沒有.比如正常退出(exit/_exit /_Exit)為 完成任務 若異常退出 (abort)不想幹了, 或被殺了

所有要等待子進程退出,而且還要收集它退出的狀态 等待就是調用wait函數 和 waitpid函數

3. 僵屍進程

子進程退出狀态不被收集,會變成僵死進程僵屍進程

正如以下例子,就是子進程退出沒有被收集,成了僵屍進程:

#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> int main() { pid_t pid; int cnt=0; printf("cnt=%d\n",cnt); pid = vfork(); if(pid >0){ while(1){ printf("cnt=%d\n",cnt); printf("this is father print pid is %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ printf("this is child print pid is =%d\n",getpid()); sleep(1); cnt ; if(cnt == 3 ){ exit(0); } } } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)20

運行三次子進程後,退出,父進程一直運行

linux進程使用教程學習(關于Linux進程編程入門)21

結果:在查看進程時發現,父進程11314正在運行 “S ” 而子進程11315 停止運行 “z ” z表示zombie(僵屍)

4. 等待函數:wait(狀态碼); 的使用:

#include<sys/types.h> #inlcude<sys/wait.h> pid_t wait(int *status); //參數status 是一個地址 pid_t waitpid(pid_t pid , int *status ,int options); int waitid(idtype_t idtype ,id_t id ,siginfo_t *infop, int options);

  • 如果其所有子進程都還在運行,則阻塞。:通俗的說就是子進程在運行的時候,父進程卡在wait位置阻塞,等子進程退出後,父進程開始運行。
  • 如果一個子進程已終止,正等待父進程獲取其終止狀态,則會取得該子進程的終止狀态立即返回。
  • 如果它沒有任何子進程,則立即出錯返回。

status參數:是一個整型數指針 非空:子進程退出狀态放在它所指向的地址中。空:不關心退出狀态

檢查wait 和 waitpid 所返回的終止狀态的宏

宏說明WIFEXITED (status)若為正常終止子進程返回的狀态,則為真。對于這種情況可執行WEXITSTATUS(status),取子進程傳送給exit、_exit 或_Exit參數的低8位WIFSIGNALED (status)若為異常終止子進程返回的狀态,則為真(接到一個不捕捉的信号)。對于這種情況,可執行WTERMSIG(status),取使子進程終止的信号編号。另外,有些實現(非Single UNIX Specification)宏義宏WCOREDUMP(status),若已産生終止進程的core文件,則它返回真WIFSTOPPED (status)若為當前暫停子進程的返回的狀态,則為真,對于這種情況,可執行WSTIOPSIG(status),取使子進程暫停的信号編号WIFCONTINUED (status)若在作業控制暫停後已經繼續的子進程返回了狀态,則為真。(POSIX.1的XSI擴展,僅用于waitpid。)比如說:exit(3) wait (狀态碼); 要通過宏來解析狀态碼

5. 收集退出進程狀态

pid = vfork(); if(pid >0){ while(1){ printf("cnt=%d\n",cnt); printf("this is father print pid is %d\n",getpid()); sleep(1); } }else if(pid == 0){ wait(NULL); // 參數:status 是一個地址 為空 表示不關心退出狀态 while(1){ printf("this is child print pid is =%d\n",getpid()); sleep(1); cnt ; if(cnt == 3 ){ exit(0); } } }

wait(NULL); // 參數:status 是一個地址 為空 表示不關心退出狀态

沒有了11567子進程,這樣就不是僵屍進程了

linux進程使用教程學習(關于Linux進程編程入門)22

收集子進程退出狀态示例代碼:

int main() { pid_t pid; int cnt=0; int status =10; printf("cnt=%d\n",cnt); pid = vfork(); if(pid >0){ wait(&status); // 參數status是一個地址 printf("child out ,chile status =%d\n",WEXITSTATUS(status)); //要解析狀态碼,需要借助WEXITSTATUS while(1){ printf("cnt=%d\n",cnt); printf("this is father print pid is %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ printf("this is child print pid is =%d\n",getpid()); sleep(1); cnt ; if(cnt == 3 ){ exit(5); } } }

int status =10;

wait(&status); // 參數status是一個地址printf("child out ,chile status =%d\n",WEXITSTATUS(status)); //要解析狀态碼,需要借助WEXITSTATUS

linux進程使用教程學習(關于Linux進程編程入門)23

結果顯示:exit(5); 就能看到子進程退出的狀态 status=5

6. 等待函數:waitpid()的使用;

wait和waitpid的區别之一:

wait使父進程(調用者)阻塞,waitpid有一個選項 ,可以使父進程(調用者)不阻塞。

pid_t waitpid(pid_t pid , int *status ,int options);

對于waitpid函數種pid參數的作用解釋如下:

pid == -1等待任一子進程。就這一方面而言,waitpid與wait等效。pid > 0等待其進程ID與pid相等的子進程。pid == 0等待其組ID等于調用進程組ID的任一子進程pid <-1等待其組ID等于pid絕對值的任一子進程。

waitpid 的 options 常量:

WCONTINUED若實現支持作業控制,那麼由pid指定的任一子進程在暫停後已經繼續,但其狀态尚未報告,則返回其狀态(POSIX.1的XSI擴展)WNOHANG若由pid指定的子進程并不是立即可用的,則waitpid不阻塞,此時其返回值為0;WUNTRACED若某實現支持作業控制,而由pid指定的任一子進程已處于暫停狀态。

waitpid 來使得父進程不阻塞代碼:

int main() { pid_t pid; int cnt=0; int status =10; printf("cnt=%d\n",cnt); pid = vfork(); if(pid >0){ waitpid(pid,&status,WNOHANG); // 參數pid 是子進程号,WNOHANG是若由pid指定的子進程并不是立即可用的,則waitpid不阻塞,此時其返回值為0; printf("child out ,chile status =%d\n",WEXITSTATUS(status)); while(1){ printf("cnt=%d\n",cnt); printf("this is father print pid is %d\n",getpid()); sleep(1); } }else if(pid == 0){ while(1){ printf("this is child print pid is =%d\n",getpid()); sleep(1); cnt ; if(cnt == 3 ){ exit(5); } } }

linux進程使用教程學習(關于Linux進程編程入門)24

子進程和父進程同時進行

linux進程使用教程學習(關于Linux進程編程入門)25

但是發現子進程12275 在系統查詢進程中 還是變成了僵屍進程原因是 ==WNOHANG是不等待參數,它隻運行一遍== ,當他運行時候,子進程沒死,等子進程死後,他沒運行,就沒有收到停止狀态,所以成了僵屍進程。

八、孤兒進程1. 孤兒進程的概念:

父進程如果不等待子進程退出,在子進程結束前就了結束了自己的“生命”,此時子進程就叫做孤兒進程。

2.孤兒進程被收留:

Linux避免系統存在過多孤兒進程,init進程收留孤兒進程,變成孤兒進程的父進程【init進程(pid=1)是系統初始化進程】。init 進程會自動清理所有它繼承的僵屍進程。

孤兒進程的代碼:

#include<sys/types.h> #include<unistd.h> #include<stdlib.h> int main() { pid_t pid; int cnt=0; int status =10; pid = fork(); if(pid >0){ printf("this is father print pid is %d\n",getpid()); } else if(pid == 0){ while(1){ printf("this is child print pid is =%d,my father pid is=%d\n",getpid(),getppid()); sleep(1); cnt ; if(cnt == 3 ){ exit(5); } } } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)26

父進程運行結束前,子進程的父進程pid還是13098。父進程運行結束後,子進程的父進程變成了init進程( pid=1)。

linux進程使用教程學習(關于Linux進程編程入門)27

九、exec族函數1. exec族函數的作用:

我們用fork函數創建新進程後,經常會在新進程中調用exec函數去執行另外一個程序。當進程調用exec函數時,該進程被完全替換為新程序因為調用exec函數并不創建新進程,所以前後進程的ID并沒有改變。

2. 為什麼要用exec族函數,有什麼作用?
  1. 一個父進程希望複制自己,使父、子進程同時執行不同的代碼段。這在網絡服務進程中是常見的——父進程等待客戶端的服務請求。當這種請求到達時,父進程調用fork,使子進程處理此請求。父進程則繼續等待下一個服務請求到達。
  2. 一個進程要執行一個不同的程序。這對shell是常見的情況。在這種情況下,子進程從fork返回後立即調用exec。
3. exec族函數定義:

功能:

exec函數族提供了一種在進程中啟動另一個程序執行的方法,它可以根據指定的文件名或目錄名找到可執行文件,并用它來取代原調用進程的數據段、代碼段和堆棧段。在執行完之後,原調用進程的内容除了進程号外,其他全部都被替換了。在調用進程内部執行一個可執行文件,可執行文件既可以是二進制文件,也可以是linux下可執行的腳本文件。【通俗理解就是執行demo1的同時,執行一半去執行demo2。】

函數族:

execl、execlp、execle、execv、execvp、execvpe

函數原型:

#include<unistd.h> extern char **environ; int execl(char *path , char *arg , ...); int execlp(char *file , char *arg , ...); int execle(char *path , char *arg , ... , char *const envp[] ); int execv(char *path , char *const argv[] ); int execvp(char *file , char *const atgv[] ); int execvpe(char *file , char *const argv[] , char *const envp[]);

返回值:

exec函數族的函數執行成功後不會返回,調用失敗時,會設置errno并返回-1,然後從原程序的調用點接着往下執行。

參數說明:

path :可執行文件的路徑名字arg:可執行程序所帶的參數,第一個參數為可執行文件名字,沒有帶路徑且arg必須以NULL結束。file:如果參數file中包含/,則就将其視為路徑名,否則就按PATH環境變量,在它所指定的各目錄中搜尋可執行文件。

exec族函數參數極難記憶和分辨,函數名中的字符會給我們一些幫助:

字符說明l使用參數列表p使用文件名,并從PATH環境尋找可執行文件v應該先構造一個指向各參數的指針數組,然後将該數組的地址作為這些函數的參數。e多了envp[]數組,使用新的環境變量代替調用進程的環境變量

4. exec函數 帶 l 帶p 帶v 來說明參數特點

先寫一個帶參數的程序,輸入參數 輸出參數,在上一篇Linux文件編程裡,main參數我們學過。

./echoarg代碼:

#include<stdio.h> int main(int argc , char *argv[]) { int i =0; for(i =0 ;i <argc;i ){ printf("argv[%d]:%s\n",i ,argv[i]); } return 0; }

在執行a.out 代碼一半的時候,調用上面的代碼echoarg

#include<stdio.h> #include<stdlib.h> #include<unistd.h> int main(void) { printf("brfore execl\n"); //int execl(char *path , char *arg , ...); if(execl("/bin/echoarg","echoarg","abc",NULL)==-1) { printf("execl failed!\n"); } printf("after execl \n"); return 0; }

exec函數族的函數執行成功後不會返回,調用失敗時,會設置errno并返回-1,然後從原程序的調用點接着往下執行。

if(execl("/bin/echoarg","echoarg","abc",NULL)==-1)源代碼:int execl(char *path , char *arg , ...); //最後一個參數是:arg必須以NULL結束。

在執行a.out 代碼一半的時候,調用上面的代碼echoarg: exec函數族的函數執行成功後不會返回,調用失敗時,會設置errno并返回-1,然後從原程序的調用點接着往下執行。

linux進程使用教程學習(關于Linux進程編程入門)28

==perror("why"); //用來在執行錯誤時候,查詢錯誤原因==

若要調用ech 執行一般執行ls ,同理。隻需要改動

if(execl("/bin/ls","ls",NULL,NULL)==-1)

linux進程使用教程學習(關于Linux進程編程入門)29

若要調用ech 執行一般執行ls-l ,同理。

if(execl("/bin/ls","ls","-l",NULL)==-1)

execlp 和execl 的區别

帶p : 可以通過環境變量PATH環境尋找可執行文件

#include<stdio.h> #include<stdlib.h> #include<unistd.h> int main(void) { printf("brfore execl\n"); //int execl(char *path , char *arg , ...); if(execl("ls",";s",NULL,NULL)==-1) { printf("execl failed!\n"); } printf("after execl \n"); return 0; }

在路徑中不用寫具體路徑,就可以自動找到文件

linux進程使用教程學習(關于Linux進程編程入門)30

execvp 和execl 的區别

#include<stdio.h> #include<stdlib.h> #include<unistd.h> int main(void) { printf("brfore execl\n"); char *argv[] = {"ps",NULL,NULL}; if(execvp("ps",argv)==-1) { printf("execl failed!\n"); } printf("after execl \n"); return 0; }

char *argv[] = {"ps",NULL,NULL}; if(execvp("ps",argv)==-1)

結果與上面相同

5. 任何目錄下執行程序

一個程序在目錄下能運行,換一個目錄就無法運行,如果把程序配置到環境變量裡面去。

==pwd顯示當前路徑 echo

PATH: [pwd顯示的當前路徑]==

就可以在任何目錄下執行程序了

6. exec配合fork使用

一個進程要執行一個不同的程序。這對shell是常見的情況。在這種情況下,子進程從fork返回後立即調用exec。

1. 不用exec的方法: 實現功能,當父進程檢查到輸入為1的時候,創建子進程把配置文件的字段值修改掉。

#include<sys/types.h> #include<sys/stat.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<unistd.h> int main() { pid_t pid; int data = 10; while(1){ printf("please input a data\n"); scanf("%d",&data); if(data == 1){ pid = fork(); if(pid>0) { wait(NULL); } if(pid == 0){ int fdSrc; char *readBuf=NULL; fdSrc = open("config.txt",O_RDWR); int size = lseek(fdSrc,0,SEEK_END); lseek (fdSrc,0,SEEK_SET); readBuf =(char *)malloc(sizeof(char)*size 8); int n_read= read(fdSrc,readBuf,size); char *p=strstr(readBuf,"LENG="); //找到(要修改的)位置 //參數1 要找的源文件 2.“要找的字符串” if(p==NULL){ printf("not found\n"); exit(-1); } p=p strlen("LENG="); //移動字符串個字節 *p='0'; //*p 取内容 lseek (fdSrc,0,SEEK_SET); int n_write =write(fdSrc,readBuf,strlen(readBuf)); close(fdSrc); exit(0); } }else { printf("do noting\n"); } } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)31

實現了當父進程檢查到輸入為1的時候,創建子進程把配置文件的字段值修改掉。

linux進程使用教程學習(關于Linux進程編程入門)32

2. 用exec的方法: 實現功能,當父進程檢查到輸入為1的時候,創建子進程把配置文件的字段值修改掉。

int main(){pid_t pid;int data = 10;while(1){printf("please input a data\n");scanf("%d",&data);if(data == 1){pid = fork();if(pid > 0){wait(NULL);}if(pid == 0){execl("./changdata","changdata","config.txt",NULL);exit(0);}}else {printf("do noting\n");}}return 0;}

linux進程使用教程學習(關于Linux進程編程入門)33

linux進程使用教程學習(關于Linux進程編程入門)34

使用execl 和 fork 結合 也能做到上面結果,而且更方便,但是在 ./changdata 可執行文件存在的情況下。

十、system函數1. system函數定義:

函數原型:

#include<stdlib.h> int system(const char * string);

函數說明:

system()會調用fork()産生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令,此命令執行完後随即返回原調用的進程。在調用system()期間SIGCHLD 信号會被暫時擱置,SIGINT和SIGQUIT 信号則會被忽略。

返回值:

system()函數的返回值如下:成功,則返回進程的狀态值;當sh不能執行時,返回127;失敗返回-1;

2. system函數的使用:

用system也可以做到execl的功能用system實現修改配置 數值代碼:

int main() { pid_t pid; int data = 10; while(1){ printf("please input a data\n"); scanf("%d",&data); if(data == 1){ pid = fork(); if(pid > 0){ wait(NULL); } if(pid == 0){ execl("./changdata config.txt"); exit(0); } }else { printf("do noting\n"); } } return 0; }

linux進程使用教程學習(關于Linux進程編程入門)35

在這裡插入圖片描述

3. system和execl不同的是:

sysem運行完調用的可執行文件後還會繼續執行源代碼。

==附加說明:==

在編寫具有SUID/SGID權限的程序時請勿使用system(),system()會繼承環境變量,通過環境變量可能會造成系統安全的問題。

十一、popen函數1. popen函數的定義:

函數原型:

#include<stdio.h> FILE *popen (const char *command ,const char *type); int pclose(FILE *stream);

參數說明:

command: 是一個指向以NULL結束的shell命令字符串的指針。這行命令将被傳到bin/sh并且使用 -c标志 ,shell将執行這個命令。

type: 隻能是讀或者寫中的一種,得到的返回值(标準I/O流)也具有和type相應 的隻讀或隻寫類型。如果type是”r“ 則文件指針連接到command的标準輸出;如果type是”w“則文件指針連接到command的标準輸入。

返回值:

如果調用成功,則返回一個讀或者打開文件的指針,如果失敗,返回NULL,具體錯誤要根據errno判斷

int pclose(FILE *stream) 參數說明:stream:popen返回對丢文件指針 返回值:如果調用失敗,返回-1

作用:

popen()函數用于創建一個管道:其内部實現為調用fork産生一個子進程,執行一個shell以運行命令來開啟一個進程這個進程必須由pclose()函數關閉。

popen比system 在應用中的好處:==可以獲取運行的輸出結果==

linux進程使用教程學習(關于Linux進程編程入門)36

popen函數執行完,執行結果到管道内,數據流出的時候,在管道尾部fread就可以讀出執行數據,就能實現把數據讀到或寫到想要的緩沖區裡。

2. popen函數的使用:#include<stdio.h>#include<stdlib.h>#include<unistd.h>int main(void){char ret[1024]={0};FILE *fp;fp = popen("ps","r");int nread = fread(ret,1,1024,fp);printf("read ret %d byte ,ret =%s\n",nread ,ret);return 0;}

結果發現:popen函數結束後,ps 輸出的内容, 都捕獲到 ret 數組裡面去了。popen可以獲取運行的輸出結果 ,可以讀取也可以寫入文件中。

linux進程使用教程學習(關于Linux進程編程入門)37

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved