网站黄色在线观看视频,男人和女人操逼有免费的视频吗2018高清,91熟女丨老女人丨高潮丰满,丝袜高潮流白浆潮喷在线播放

中國站

中國站

國際版

聯(lián)系我們

400-002-9968

售前咨詢

售后服務

注冊 登錄

博客 > Linux知識:Linux 進程編程入門

Linux知識:Linux 進程編程入門

  • 標簽:
  • Linux
  • Linux進程
  • Linux 進程編程

瀏覽量:1074次評論:0次

作者:銳成網(wǎng)絡整理時間:2024-06-11 18:00:04

在當今IT領域,Linux操作系統(tǒng)具有穩(wěn)定性和靈活性特點,是開發(fā)人員和系統(tǒng)管理員的常用操作系統(tǒng)。理解Linux進程編程是每位程序員必備的技能之一。本文將從基礎概念講起,系統(tǒng)地介紹Linux進程編程的相關知識,包括進程相關概念、創(chuàng)建進程函數(shù)fork、vfork的使用等,以下是相關內容。

Linux知識:Linux 進程編程入門

Linux 進程篇

一、進程相關概念

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

1.什么是程序,什么是進程,兩者之間的區(qū)別?

程序是靜態(tài)的概念,gcc  xxx.c -o pro 磁盤中生成pro文件,叫做程序。

進程是程序的一次運行活動, 通俗點說就是程序跑起來了,系統(tǒng)中就多了一個進程

2.如何查看系統(tǒng)中有哪些進程?

使用ps指令查看:  ps-aux  在ubuntu下查看, 在實際工作中,配合grep來查找程序中是否存在某一個進程。

grep過濾進程:ps -aux | grep init 就只把帶有init的進程過濾出來。

使用top指令查看,類似windows任務管理器

3.什么是進程標識符?

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

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

編程調用getpid函數(shù)獲取自身的進程標識符;

#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內核啟動之后,會創(chuàng)建第一個用戶級進程init, init 進程 (pid=1) 是除了 idle 進程(pid=0,也就是 init_task) 之外另一個比較特殊的進程,它是 Linux 內核開始建立起進程概念時第一個通過 kernel_thread 產生的進程,其開始在內核態(tài)執(zhí)行,然后通過一個系統(tǒng)調用,開始執(zhí)行用戶空間的 / sbin/init 程序。

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

進程A創(chuàng)建了進程B,那么A叫做父進程,B叫做子進程,父進程是相對的概念,理解為人類中的父子關系

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

gcc xxx.c  -o a.out    當執(zhí)行 ./a.out 時候,操作系統(tǒng)會劃分一塊內存空間,如何分配呢?如下圖:

Linux知識:Linux 進程編程入門

二、創(chuàng)建進程函數(shù)fork的使用

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

fork函數(shù)調用成功,返回兩次 返回值為0  ,代表當前進程是子進程 返回值非負數(shù),代表當前進程為父進程 調用失敗 ,返回-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  說明,有了兩個進程!執(zhí)行了兩次打印pid

Linux知識:Linux 進程編程入門

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 進程編程入門

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

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

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

#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 進程編程入門

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 進程編程入門

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

三、進程創(chuàng)建后 發(fā)生了什么事?

Linux知識:Linux 進程編程入門

1 在內存空間中fork后發(fā)生了什么?

Linux知識:Linux 進程編程入門

2. ./demo4 運行的程序父進程是誰?

int main(int argc, const char *argv[])
{
        while(1);
        return 0;
}

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

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

四、創(chuàng)建新進程的實際應用場景

1. fork創(chuàng)建子進程的一般目的:

一個父進程希望復制自己,使父、子進程同時執(zhí)行不同的代碼段。這在網(wǎng)絡服務進程中是常見的——父進程等待客戶端的服務請求。當這種情求達到時,父進程調用fork,使子進程處理此請求。父進程則繼續(xù)等待下一個服務請求到達。

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

2. 模擬socket 創(chuàng)建進程(服務器對接客戶端的應用場景)示例代碼:

#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 進程編程入門

輸入1時候,模擬有客戶端進行交互 ,創(chuàng)建子進程來進行交互,子進程號為:9756

Linux知識:Linux 進程編程入門

模擬多個客戶端進行交互時 ,創(chuàng)建多個子進程來進行交互,子進程號為:9756   / 9758  / 9759

Linux知識:Linux 進程編程入門

查看系統(tǒng)進程:

Linux知識:Linux 進程編程入門

3. fork總結:

一個現(xiàn)有進程可以調用fork函數(shù)創(chuàng)建一個新進程。

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

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

子進程和父進程繼續(xù)執(zhí)行fork調用之后的指令。子進程是父進程的副本。例如,子進程獲得父進程數(shù)據(jù)空間、堆和棧的副本。注意,這是子進程所擁有的副本。父、子進程并不共享這些存儲空間部分。父、子進程共享正文段。由于在fork之后經(jīng)常跟隨著exec,所以現(xiàn)在的很多實現(xiàn)并不執(zhí)行一個父進程數(shù)據(jù)段、棧和堆的完全復制。作為替代,使用了寫時復制(Copy-On-Write,COW)技術。這些區(qū)域由父、子進程共享,而且內核將它們的訪問權限改變?yōu)橹蛔x的。如果父、子進程中的任一個試圖修改些區(qū)域,則內核只為修改區(qū)域的那塊內存制作一個副本,通常是虛擬存儲器系統(tǒng)中的一“頁”。Bach和McKusick等對這種特征做了更詳細的說明。

五、vfork創(chuàng)建進程

1. vfork函數(shù) 也可以創(chuàng)建進程,與fork有什么區(qū)別?

關鍵區(qū)別一:vfork直接使用父進程存儲空間,不用拷貝關鍵區(qū)別二:vfork保證子進程先運行,當子進程調用exit退出后,父進程才執(zhí)行

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 進程編程入門

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退出后,父進程才執(zhí)行

Linux知識:Linux 進程編程入門

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 進程編程入門

六、ps 常帶的一些參數(shù)

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

命令參數(shù)

說明

-e

顯示所有進程.

-f

全格式。

-h

不顯示標題。

-l

長格式。

-w

寬輸出。

-a

顯示終端上的所有進程,包括其他用戶的進程。

-r

只顯示正在運行的進程。

-u

以用戶為主的格式來顯示程序狀況。

-x

顯示所有程序,不以終端機來區(qū)分。

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

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

命令參數(shù)

說明

UID:

程序被該 UID 所擁有,指的是用戶ID

PID:

就是這個程序的 ID

PPID :

PID的上級父進程的ID

C :

CPU使用的資源百分比

STIME :

系統(tǒng)啟動時間

TTY:

登入者的終端機位置

TIME :

使用掉的 CPU時間。

CMD:

所下達的指令為何

七、進程退出

1. 子進程退出方式

正常退出:

Mian函數(shù)調用return

進程調用exit(),標準c庫

進程調用_exit()或者——Exit(),屬于系統(tǒng)調用

進程最后一個線程返回

最后一個線程調用pthread_exit

異常退出:

調用abort

當進程收到某些信號時候,如ctrl+C

最后一個線程對取消(cancellation),請求作出響應

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

對上述任意一種終止情形,我們都希望終止進程能夠通知其父進程它是如何終止的。對于三個終止函數(shù)(exit、_exit和_Exit),實現(xiàn)這一點的方法是,將其退出狀態(tài)作為參數(shù)傳送給函數(shù)?!救缟厦媸纠锩鎸懙降腸nt==3情況下,exit(0); 這個0就是子進程退出狀態(tài)?!吭诋惓=K止情況下,內核(不是進程本身)產生一個指示其異常終止原因的終止狀態(tài)。在任何一種情況下,該終止進程的父進程都能用wait或者waitpid取得其終止狀態(tài)。

正常退出的三個函數(shù):

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


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


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

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

2. 等待子進程退出

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

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

所有要等待子進程退出,而且還要收集它退出的狀態(tài) 等待就是調用wait函數(shù) 和 waitpid函數(shù)

3. 僵尸進程

子進程退出狀態(tài)不被收集,會變成僵死進程(僵尸進程)

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

#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 進程編程入門

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

Linux知識:Linux 進程編程入門

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

4. 等待函數(shù):wait(狀態(tài)碼); 的使用:

#include<sys/types.h>
#inlcude<sys/wait.h>

pid_t wait(int *status);    //參數(shù)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位置阻塞,等子進程退出后,父進程開始運行。

 

如果一個子進程已終止,正等待父進程獲取其終止狀態(tài),則會取得該子進程的終止狀態(tài)立即返回。

 

如果它沒有任何子進程,則立即出錯返回。

status參數(shù):是一個整型數(shù)指針 非空:子進程退出狀態(tài)放在它所指向的地址中??眨翰魂P心退出狀態(tài)

檢查wait 和 waitpid 所返回的終止狀態(tài)的宏

說明

WIFEXITED (status)

若為正常終止子進程返回的狀態(tài),則為真。對于這種情況可執(zhí)行WEXITSTATUS(status),取子進程傳送給exit、_exit 或_Exit參數(shù)的低8位

WIFSIGNALED (status)

若為異常終止子進程返回的狀態(tài),則為真(接到一個不捕捉的信號)。對于這種情況,可執(zhí)行WTERMSIG(status),取使子進程終止的信號編號。另外,有些實現(xiàn)(非Single UNIX Specification)宏義宏WCOREDUMP(status),若已產生終止進程的core文件,則它返回真

WIFSTOPPED (status)

若為當前暫停子進程的返回的狀態(tài),則為真,對于這種情況,可執(zhí)行WSTIOPSIG(status),取使子進程暫停的信號編號

WIFCONTINUED (status)

若在作業(yè)控制暫停后已經(jīng)繼續(xù)的子進程返回了狀態(tài),則為真。(POSIX.1的XSI擴展,僅用于waitpid。)

比如說:exit(3)  wait (狀態(tài)碼); 要通過宏來解析狀態(tài)碼

 

5. 收集退出進程狀態(tài)

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);    // 參數(shù):status  是一個地址  為空 表示不關心退出狀態(tài)
         while(1){
               printf("this is child print pid is =%d\n",getpid());
               sleep(1);
               cnt++;
               if(cnt == 3 ){
                  exit(0);
               }
         }
    }

wait(NULL);    // 參數(shù):status  是一個地址  為空 表示不關心退出狀態(tài)

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

Linux知識:Linux 進程編程入門

收集子進程退出狀態(tài)示例代碼:

int main()
{
    pid_t pid;
    int cnt=0;
    int status =10;

    printf("cnt=%d\n",cnt);

    pid = vfork();

    if(pid >0){

       wait(&status);     // 參數(shù)status是一個地址  
       printf("child out ,chile status =%d\n",WEXITSTATUS(status));  //要解析狀態(tài)碼,需要借助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);     // 參數(shù)status是一個地址
printf("child out ,chile status =%d\n",WEXITSTATUS(status));  //要解析狀態(tài)碼,需要借助WEXITSTATUS

Linux知識:Linux 進程編程入門

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

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

wait和waitpid的區(qū)別之一:

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

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

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

 

 

pid == -1

等待任一子進程。就這一方面而言,waitpid與wait等效。

pid > 0

等待其進程ID與pid相等的子進程。

pid == 0

等待其組ID等于調用進程組ID的任一子進程

pid <-1

等待其組ID等于pid絕對值的任一子進程。

waitpid 的 options 常量:

 

 

WCONTINUED

若實現(xiàn)支持作業(yè)控制,那么由pid指定的任一子進程在暫停后已經(jīng)繼續(xù),但其狀態(tài)尚未報告,則返回其狀態(tài)(POSIX.1的XSI擴展)

WNOHANG

若由pid指定的子進程并不是立即可用的,則waitpid不阻塞,此時其返回值為0;

WUNTRACED

若某實現(xiàn)支持作業(yè)控制,而由pid指定的任一子進程已處于暫停狀態(tài)。

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); // 參數(shù)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 進程編程入門

子進程和父進程同時進行

但是發(fā)現(xiàn)子進程12275  在系統(tǒng)查詢進程中  還是變成了僵尸進程原因是 ==WNOHANG是不等待參數(shù),它只運行一遍== ,當他運行時候,子進程沒死,等子進程死后,他沒運行,就沒有收到停止狀態(tài),所以成了僵尸進程。

八、孤兒進程

1. 孤兒進程的概念:

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

2.孤兒進程被收留:

Linux避免系統(tǒng)存在過多孤兒進程,init進程收留孤兒進程,變成孤兒進程的父進程【init進程(pid=1)是系統(tǒng)初始化進程】。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 進程編程入門

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

Linux知識:Linux 進程編程入門

九、exec族函數(shù)

1. exec族函數(shù)的作用:

我們用fork函數(shù)創(chuàng)建新進程后,經(jīng)常會在新進程中調用exec函數(shù)去執(zhí)行另外一個程序。當進程調用exec函數(shù)時,該進程被完全替換為新程序因為調用exec函數(shù)并不創(chuàng)建新進程,所以前后進程的ID并沒有改變。

2. 為什么要用exec族函數(shù),有什么作用?

一個父進程希望復制自己,使父、子進程同時執(zhí)行不同的代碼段。這在網(wǎng)絡服務進程中是常見的——父進程等待客戶端的服務請求。當這種請求到達時,父進程調用fork,使子進程處理此請求。父進程則繼續(xù)等待下一個服務請求到達。

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

3. exec族函數(shù)定義:

功能:

exec函數(shù)族提供了一種在進程中啟動另一個程序執(zhí)行的方法,它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來取代原調用進程的數(shù)據(jù)段、代碼段和堆棧段。在執(zhí)行完之后,原調用進程的內容除了進程號外,其他全部都被替換了。在調用進程內部執(zhí)行一個可執(zhí)行文件,可執(zhí)行文件既可以是二進制文件,也可以是linux下可執(zhí)行的腳本文件?!就ㄋ桌斫饩褪菆?zhí)行demo1的同時,執(zhí)行一半去執(zhí)行demo2?!?/span>

函數(shù)族:

execl、execlp、execle、execv、execvp、execvpe

函數(shù)原型:

#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函數(shù)族的函數(shù)執(zhí)行成功后不會返回,調用失敗時,會設置errno并返回-1,然后從原程序的調用點接著往下執(zhí)行。

參數(shù)說明:

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

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

字符

說明

l

使用參數(shù)列表

p

使用文件名,并從PATH環(huán)境尋找可執(zhí)行文件

v

應該先構造一個指向各參數(shù)的指針數(shù)組,然后將該數(shù)組的地址作為這些函數(shù)的參數(shù)。

e

多了envp[]數(shù)組,使用新的環(huán)境變量代替調用進程的環(huán)境變量

4. exec函數(shù) 帶 l  帶p  帶v  來說明參數(shù)特點

先寫一個帶參數(shù)的程序,輸入?yún)?shù) 輸出參數(shù),在上一篇Linux文件編程里,main參數(shù)我們學過。

./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;
}

在執(zhí)行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函數(shù)族的函數(shù)執(zhí)行成功后不會返回,調用失敗時,會設置errno并返回-1,然后從原程序的調用點接著往下執(zhí)行。

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

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

Linux知識:Linux 進程編程入門

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

若要調用ech 執(zhí)行一般執(zhí)行l(wèi)s ,同理。只需要改動

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

Linux知識:Linux 進程編程入門

若要調用ech 執(zhí)行一般執(zhí)行l(wèi)s-l ,同理。

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

Linux知識:Linux 進程編程入門

execlp 和execl 的區(qū)別

帶p : 可以通過環(huán)境變量PATH環(huán)境尋找可執(zhí)行文件

#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 進程編程入門

execvp 和execl 的區(qū)別

#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. 任何目錄下執(zhí)行程序

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

==pwd顯示當前路徑 echo

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

就可以在任何目錄下執(zhí)行程序了

6.  exec配合fork使用

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

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

#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="); //找到(要修改的)位置   
  //參數(shù)1 要找的源文件  2.“要找的字符串”
                        if(p==NULL){
                               printf("not found\n");
                               exit(-1);
                         }
                             p=p+strlen("LENG=");  //移動字符串個字節(jié)
                               *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;
}

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

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

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;
}

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

十、system函數(shù)

1. system函數(shù)定義:

函數(shù)原型:

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

函數(shù)說明:

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

返回值:

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

2. system函數(shù)的使用:

用system也可以做到execl的功能用system實現(xiàn)修改配置 數(shù)值代碼:

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;
}

3. system和execl不同的是:

sysem運行完調用的可執(zhí)行文件后還會繼續(xù)執(zhí)行源代碼。

==附加說明:==

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

十一、popen函數(shù)

1.  popen函數(shù)的定義:

函數(shù)原型:

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

參數(shù)說明:

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

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

返回值:

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

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

作用:

popen()函數(shù)用于創(chuàng)建一個管道:其內部實現(xiàn)為調用fork產生一個子進程,執(zhí)行一個shell以運行命令來開啟一個進程這個進程必須由pclose()函數(shù)關閉。

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

popen函數(shù)執(zhí)行完,執(zhí)行結果到管道內,數(shù)據(jù)流出的時候,在管道尾部fread就可以讀出執(zhí)行數(shù)據(jù),就能實現(xiàn)把數(shù)據(jù)讀到或寫到想要的緩沖區(qū)里。

2. popen函數(shù)的使用:

#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;
}

結果發(fā)現(xiàn):popen函數(shù)結束后,ps 輸出的內容, 都捕獲到 ret 數(shù)組里面去了。popen可以獲取運行的輸出結果 ,可以讀取也可以寫入文件中。

Linux知識:Linux 進程編程入門

重要聲明:本文來自一口Linux,經(jīng)授權轉載,版權歸原作者所有,不代表銳成觀點,轉載的目的在于傳遞更多知識和信息。

我的評論

還未登錄?點擊登錄

微信掃碼溝通
微信掃碼溝通

微信掃碼溝通

AI
return head