Linux C fork exec介紹用法


原文:http://blog.csdn.NET/nvd11/article/details/8856278

假如我們在編寫1個c程序時想調用1個shell腳本或者執行1段 bash shell命令, 應該如何實現呢?

      其實在<stdlib.h> 這個頭文件中包含了1個調用shell命令或者腳本的函數 system();直接把 shell命令作為參數傳入 system函數就可以了, 的確很方便. 關於system 有一段這樣的介紹:   system 執行時內部會自動啟用fork() 新建1個進程,  效率沒有直接使用fork() 和 exec函數高.

       那么這篇文章其實就是介紹一下fork() 和 exec函數的用法, 以及如何使用它們來替代system函數.

1. fork() 函數

1.1 fork() 函數的作用

       一般來講, 我們編寫1個普通的c程序, 運行這個程序直到程序結束, 系統只會分配1個pid給這個程序, 也就就說, 系統里只會有一條關於這個程序的進程.

 

        但是執行了fork() 這個函數就不同了. 

        fork 這個英文單詞在英文里是"分叉"意思,  fork() 這個函數作用也很符合這個意思.  它的作用是復制當前進程(包括進程在內存里的堆棧數據)為1個新的鏡像. 然后這個新的鏡像和舊的進程同時執行下去. 相當於本來1個進程, 遇到fork() 函數后就分叉成兩個進程同時執行了. 而且這兩個進程是互不影響

  fork函數的特點概括起來就是“調用一次,返回兩次”

1.2 區別分主程序和子程序.

        實際應用中, 單純讓程序分叉意義不大, 我們新增一個子程序, 很可能是為了讓子進程單獨執行一段代碼. 實現與主進程不同的功能.

         要實現上面所說的功能, 實際上就是讓子進程和主進程執行不同的代碼啊.

         所以fork() 實際上有返回值, 而且在兩條進程中的返回值是不同的, 在主進程里 fork()函數會返回子進程的pid,   而在子進程里會返回0!   所以我們可以根據fork() 的返回值來判斷進程到底是哪個進程, 就可以利用if 語句來執行不同的代碼了!

int fork_test(){  
    int childpid;  
    int ret;  
  
    if (ret =fork() == -1){    
            printf("fork failed\n");  
        }  
    }else if(ret == 0){  
        //child process  
            printf("This is child process\n");  
    }  else{
        printf("This is parent process\n");
    }
}      

1.3 使用wait() 函數主程序等子程序執行完成(退出)后再執行.   

        由上面例子得知,  主程序和子程序的執行次序是隨機的,  但是實際情況下, 通常我們希望子進程執行后,  才繼續執行主進程. 

        wait()函數就提供了這個功能,    在if 條件內的  主進程呢部分內 加上wait() 函數, 就可以讓主進程執行fork()函數時先hold 住, 等子進程退出后再執行, 通常會配合子進程的exit()函數一同使用.

2. exec 函數組

      需要注意的是exec並不是1個函數, 其實它只是一組函數的統稱, 它包括下面6個函數:

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

可以見到這6個函數名字不同, 而且他們用於接受的參數也不同.

       實際上他們的功能都是差不多的, 因為要用於接受不同的參數所以要用不同的名字區分它們, 畢竟C語言沒有函數重載的功能嘛..  

 

       但是實際上它們的命名是有規律的:

       exec[l or v][p][e]

       exec函數里的參數可以分成3個部分,      執行文件部分,     命令參數部分,   環境變量部分.

        例如我要執行1個命令   ls -l /home/gateman  

        執行文件部分就是  "/usr/bin/ls"

        命令參賽部分就是 "ls","-l","/home/gateman",NULL              見到是以ls開頭 每1個空格都必須分開成2個部分, 而且以NULL結尾的啊.

        環境變量部分, 這是1個數組,最后的元素必須是NULL 例如  char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};

        

        好了說下命名規則:

        e后續,  參數必須帶環境變量部分,   環境變零部分參數會成為執行exec函數期間的環境變量, 比較少用

        l 后續,   命令參數部分必須以"," 相隔, 最后1個命令參數必須是NULL

        v 后續,   命令參數部分必須是1個以NULL結尾的字符串指針數組的頭部指針.         例如char * pstr就是1個字符串的指針, char * pstr[] 就是數組了, 分別指向各個字符串.

        p后續,   執行文件部分可以不帶路徑, exec函數會在$PATH中找

 

          

         還有1個注意的是, exec函數會取代執行它的進程,  也就是說, 一旦exec函數執行成功, 它就不會返回了, 進程結束.   但是如果exec函數執行失敗, 它會返回失敗的信息,  而且進程繼續執行后面的代碼!

 

       通常exec會放在fork() 函數的子進程部分, 來替代子進程執行啦, 執行成功后子程序就會消失,  但是執行失敗的話, 必須用exit()函數來讓子進程退出!

       下面是各個例子:

2.1  execv 函數

int childpid;  
int i;  
  
if (fork() == 0){  
    //child process  
    char * execv_str[] = {"echo", "executed by execv",NULL};  
    if (execv("/usr/bin/echo",execv_str) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execv done\n\n");  
}  

注意字符串指針數組的定義和賦值

2.2  execvp 函數

if (fork() == 0){  
    //child process  
    char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};  
    if (execvp("echo",execvp_str) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execvp done\n\n");  
}  

2.3 execve 函數

if (fork() == 0){  
    //child process  
    char * execve_str[] = {"env",NULL};  
    char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};  
    if (execve("/usr/bin/env",execve_str,env) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execve done\n\n");  
}  

2.4 execl 函數

if (fork() == 0){  
    //child process  
    if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execv done\n\n");  
}  

2.5 execlp 函數

if (fork() == 0){  
    //child process  
    if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execlp done\n\n");  
} 

2.6 execle 函數

 

if (fork() == 0){  
    //child process  
    char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};  
    if (execle("/usr/bin/env","env",NULL,env) <0){  
        perror("error on exec");  
        exit(0);  
    }  
}else{  
    //parent process  
    wait(&childpid);  
    printf("execle done\n\n");  
}  

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM