linux系統程式設計之程式(五):exec系列函式(execl,execlp,execle,execv,execvp)使用

mickole發表於2013-07-12

本節目標:

  • exec替換程式映像
  • exec關聯函式組(execl、execlp、execle、execv、execvp)

一,exec替換程式映像

在程式的建立上Unix採用了一個獨特的方法,它將程式建立與載入一個新程式映象分離。這樣的好處是有更多的餘地對兩種操作進行管理。

當我們建立了一個程式之後,通常將子程式替換成新的程式映象,這可以用exec系列的函式來進行。當然,exec系列的函式也可以將當前程式替換掉。

例如:在shell命令列執行ps命令,實際上是shell程式呼叫fork複製一個新的子程式,在利用exec系統呼叫將新產生的子程式完全替換成ps程式。

二,exec系列函式(execl、execlp、execle、execv、execvp)

包含標頭檔案<unistd.h>

功能:

    用exec函式可以把當前程式替換為一個新程式,且新程式與原程式有相同的PID。exec名下是由多個關聯函式組成的一個完整系列,

標頭檔案<unistd.h>

extern char **environ;

原型:

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[]);

引數:

path參數列示你要啟動程式的名稱包括路徑名

arg參數列示啟動程式所帶的引數,一般第一個引數為要執行命令名,不是帶路徑且arg必須以NULL結束

返回值:成功返回0,失敗返回-1

注:上述exec系列函式底層都是通過execve系統呼叫實現:

       #include <unistd.h>

       int execve(const char *filename, char *const argv[],char *const envp[]);

DESCRIPTION:
       execve() executes the program pointed to by filename.  filename must be
       either a binary executable, or a script starting with  a  line  of  the form

以上exec系列函式區別:

1,帶l 的exec函式:execl,execlp,execle,表示後邊的引數以可變引數的形式給出且都以一個空指標結束。

示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    printf("entering main process---\n");
    execl("/bin/ls","ls","-l",NULL);
    printf("exiting main process ----\n");
    return 0;
}

QQ截圖20130712225614

利用execl將當前程式main替換掉,所有最後那條列印語句不會輸出

2,帶 p 的exec函式:execlp,execvp,表示第一個引數path不用輸入完整路徑,只有給出命令名即可,它會在環境變數PATH當中查詢命令

示例:

當不帶p但沒給出完整路徑時:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    printf("entering main process---\n");
    execl("/bin/ls","ls","-l",NULL);
    printf("exiting main process ----\n");
    return 0;
}

結果:

QQ截圖20130712230253

結果顯示找不到,所有替換不成功,main程式繼續執行

現在帶p:

QQ截圖20130712230432

替換成功

3,不帶 l 的exec函式:execv,execvp表示命令所需的引數以char *arg[]形式給出且arg最後一個元素必須

是NULL

示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    printf("entering main process---\n");
    int ret;
    char *argv[] = {"ls","-l",NULL};
    ret = execvp("ls",argv);
    if(ret == -1)
        perror("execl error");
    printf("exiting main process ----\n");
    return 0;
}

結果:

QQ截圖20130712230956

程式替換成功

4,帶 e 的exec函式:execle表示,將環境變數傳遞給需要替換的程式

從上述的函式原型中我們發現:

extern char **environ;

此處的environ是一個指標陣列,它當中的每一個指標指向的char為“XXX=XXX”

environ儲存環境資訊的資料可以env命令檢視:

QQ截圖20130712231740

它由shell程式傳遞給當前程式,再由當前程式傳遞給替換的新程式

示例:execle.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    //char * const envp[] = {"AA=11", "BB=22", NULL};
    printf("Entering main ...\n");
    int ret;
    ret =execl("./hello", "hello", NULL);
    //execle("./hello", "hello", NULL, envp);
    if(ret == -1)
        perror("execl error");
    printf("Exiting main ...\n");
    return 0;
}

hello.c

#include <unistd.h>
#include <stdio.h>
extern char** environ;

int main(void)
{
    printf("hello pid=%d\n", getpid());
    int i;
    for (i=0; environ[i]!=NULL; ++i)
    {
        printf("%s\n", environ[i]);
    }
    return 0;
}

結果:

QQ截圖20130712232918

可知原程式確實將環境變數資訊傳遞給了新程式

那麼現在我們可以利用execle函式自己給的需要傳遞的環境變數資訊:

示例程式:execle.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    char * const envp[] = {"AA=11", "BB=22", NULL};
    printf("Entering main ...\n");
    int ret;
    //ret =execl("./hello", "hello", NULL);
    ret =execle("./hello", "hello", NULL, envp);
    if(ret == -1)
        perror("execl error");
    printf("Exiting main ...\n");
    return 0;
}

hello.c

#include <unistd.h>
#include <stdio.h>
extern char** environ;

int main(void)
{
    printf("hello pid=%d\n", getpid());
    int i;
    for (i=0; environ[i]!=NULL; ++i)
    {
        printf("%s\n", environ[i]);
    }
    return 0;
}

結果:

QQ截圖20130712233335

確實將給定的環境變數傳遞過來了

 

三,fcntl()函式中的FD_CLOEXEC標識在exec系列函式中的作用

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

File descriptor flags
      The following commands manipulate the  flags  associated  with  a  file
      descriptor.   Currently, only one such flag is defined: FD_CLOEXEC, the
      close-on-exec flag.  If the FD_CLOEXEC bit is 0,  the  file  descriptor
      will remain open across an execve(2), otherwise it will be closed.

     //如果FD_CLOEXEC標識位為0,則通過execve呼叫後fd依然是開啟的,否則為關閉的

      F_GETFD (void)
             Read the file descriptor flags; arg is ignored.

      F_SETFD (long)
             Set the file descriptor flags to the value specified by arg.

如:fcntl(fd, F_SETFD, FD_CLOEXEC);

測試示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{

    printf("Entering main ...\n");
    int ret = fcntl(1, F_SETFD, FD_CLOEXEC);
    if (ret == -1)
        perror("fcntl error");
    int val;
    val =execlp("ls", "ls","-l", NULL);
    if(val == -1)
        perror("execl error");
    printf("Exiting main ...\n");
    return 0;
}

結果:

QQ截圖20130712235308

1關閉(標準輸出關閉)ls -l無法將結果顯示在標準輸出

相關文章