日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX再学习 -- exec 函数族

發(fā)布時間:2025/3/15 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX再学习 -- exec 函数族 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

我們在講,文件I/O的時候,簡單提到過 exec 函數(shù),講到 vfork 的時候,也有用到。下面我們來詳細(xì)介紹下它。

參看:UNIX再學(xué)習(xí) -- 文件I/O?

參看:UNIX再學(xué)習(xí) -- 函數(shù) fork 和 vfork

一、exec 函數(shù)族概述

參看:維基百科 -- exec函數(shù)族

與 fork 或 vfork 函數(shù)不同,exec 函數(shù)不是創(chuàng)建調(diào)用進(jìn)程的子進(jìn)程,而是創(chuàng)建一個新的進(jìn)程取代調(diào)用進(jìn)程自身。新進(jìn)程會用自己的全部地址空間,覆蓋調(diào)用進(jìn)程的地址空間,但進(jìn)程的 PID 保持不變。exec 只是用磁盤上的一個新程序替換了當(dāng)前進(jìn)程的正文段、數(shù)據(jù)段、堆段和棧段。

exec 不是一個函數(shù)而是一堆函數(shù),一般稱為 exec 函數(shù)族。它們的功能是一樣的,用法也很相近,只是參數(shù)的形式和數(shù)量略有不同。

exec函數(shù)族的作用:根據(jù)指定的文件名找到可執(zhí)行文件,并用它來取代調(diào)用進(jìn)程的內(nèi)容,換句話說,就是在調(diào)用進(jìn)程內(nèi)部執(zhí)行一個可執(zhí)行文件。這里的可執(zhí)行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執(zhí)行的腳本文件

#include <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[]); int execve(const char *path, char *const argv[], char *const envp[]); int fexecve(int fd, char *const argv[], char *const envp[]); 7 個函數(shù)返回值:若出錯,返回 -1;若成功,不返回

這些函數(shù)之間的第一個區(qū)別:

4 個函數(shù)取路徑名作為參數(shù),2 個函數(shù)則取文件名作為參數(shù),最后一個取文件描述符作為參數(shù)。當(dāng)指定 file 作為參數(shù)時:如果 file 中包含 /,則就將其視為路徑名。否則就按 PATH 環(huán)境變量,在它所指定的各目錄中搜索可執(zhí)行文件。

環(huán)境變量,我們之前專門講過,參看:UNIX再學(xué)習(xí) -- 環(huán)境變量

PATH 變量包含了一張目錄表(稱為路徑前綴),目錄之間用冒號(:)分隔。例如,下面 name = value 環(huán)境字符串指定在 4 個目錄中進(jìn)程搜索。

PATH=/bin:/usr/bin:/usr/local/bin:.

最后的路徑前綴 . 表示當(dāng)前目錄。(零長前綴也表示當(dāng)前目錄。在 value 的開始處可用 : 表示,在行中間則要用 :: 表示,在行尾以 : 表示)。

第二個區(qū)別與參數(shù)表的傳遞有關(guān)(l 表示表 (list), v 表示矢量(vector))。

函數(shù) execl、execlp 和 execle 要求將新程序的每個命令行參數(shù)都說明為一個單獨(dú)的參數(shù),這種參數(shù)以空指針結(jié)尾。對于另外 4 個函數(shù) (execv、execvp、execve、fexecve),則應(yīng)先構(gòu)造一個指向各參數(shù)的指針數(shù)組,然后將該數(shù)組地址作為這 4 個函數(shù)的參數(shù)

第三個區(qū)別與向新程序傳遞環(huán)境表相關(guān)

以 e 結(jié)尾的 3 個函數(shù)(execle 和 execve 和 fexecve)可以傳遞一個指向字符串指針數(shù)組的指針。其他四個函數(shù)則使用調(diào)用進(jìn)程中的 environ 變量為新程序復(fù)制現(xiàn)存的環(huán)境。


這些函數(shù),它們的函數(shù)名都是在 exec 后面加上一到兩個字符后綴,不同的字符后綴代表不同的含義。

-l: ?即 list ,新進(jìn)程的命令行參數(shù)以字符指針列表 (const char *arge, ...) 的形式傳入,列表以空指針結(jié)束。

-p: 即 path,若第一個參數(shù)中不包含“/”,則將其視為文件名,并根據(jù) PATH 環(huán)境變量搜索該文件。

-e: ?即 environment,新進(jìn)程的環(huán)境變量以字符指針數(shù)組 (cahr *const envp[]) 的形式傳入,數(shù)組以空指針結(jié)束,不指定環(huán)境變量則從調(diào)用進(jìn)程復(fù)制。?

-v: ?即 vector,新進(jìn)程的命令行參數(shù)以字符指針數(shù)組 (char *const argv[]) 的形式傳入,數(shù)組以空指針結(jié)束。


在很多 UNIX 實(shí)現(xiàn)中,這 7 個函數(shù)中只有 execve 是內(nèi)核的系統(tǒng)調(diào)用另外 6 個只是庫函數(shù),它們最終都要調(diào)用該系統(tǒng)調(diào)用。這 7 個函數(shù)之間的關(guān)系如下圖:
在這種安排中,庫函數(shù) execlp 和 execvp 使用 PATH 環(huán)境變量,查找第一個包含名為 filename 的可執(zhí)行文件的路徑名前綴。fexecve 庫函數(shù)使用 /proc 把文件描述符參數(shù)轉(zhuǎn)換成路徑名,execve 用該路徑名去執(zhí)行程序。

到此,將 exec 函數(shù)族 7 個函數(shù)的區(qū)別和關(guān)系,簡單講了一下。下面我們就一一介紹:

二、execl 函數(shù)

#include <unistd.h> extern char **environ; int execl(const char *path, const char *arg, ...); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execl()其中后綴 "l" 代表 list 也就是參數(shù)列表的意思,第一參數(shù) path 字符指針?biāo)赶蛞獔?zhí)行的文件路徑 接下來的參數(shù)代表執(zhí)行該文件時傳遞的參數(shù)列表:argv[0],argv[1]... 最后一個參數(shù)須用空指針NULL作結(jié)束

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execl ("/bin/ls", "ls", "-l", NULL) == -1) perror ("execl"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 總用量 16 -rwxr-xr-x 1 root root 7380 Apr 20 10:22 a.out -rw-r--r-- 1 root root 383 Apr 20 10:22 test.c -rw-r--r-- 1 root root 151 Apr 20 09:56 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

上例為 vfork 函數(shù)的典型用法。在所創(chuàng)建的子進(jìn)程里直接調(diào)用 exec 函數(shù)啟動另外一個進(jìn)程取代其自身,這比調(diào)用 fork 函數(shù)完成同樣的工作要快得多。 if (execl ("/bin/ls", "ls", "-l", NULL) == -1) ?解釋:
/bin/ls ?為 ls 指令文件路徑;ls -l 為執(zhí)行 ls 指令和選項(xiàng);NULL 最后一個參數(shù);失敗返回 -1. 有時也會看到如下的寫法: if (execl ("ls", "ls", "-l", (char *)0) == -1) ?
將第三個參數(shù)寫為 (char*)0 ,我們之前講過空指針,參看:C語言再學(xué)習(xí) -- NUL和NULL的區(qū)別 如果用常數(shù) 0 來表示一個空指針,則必須將它強(qiáng)制轉(zhuǎn)換為一個字符指針,否則它將解釋為整形參數(shù),如果一個整形數(shù)的長度與 char * 的長度不同,那么 exec 函數(shù)的實(shí)際參數(shù)就將出錯。如果函數(shù)調(diào)用成功,進(jìn)程自己的執(zhí)行代碼就會變成加載程序的代碼,execl()后邊的代碼也就不會執(zhí)行了。舉例說明:#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execl ("/bin/ls", "ls", "-l", 0) == -1) perror ("execlp"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 編譯: 警告: 函數(shù)調(diào)用中缺少哨兵 [-Wformat]

三、execlp 函數(shù)

#include <unistd.h> int execlp(const char *file, const char *arg, ...); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execlp()會從 PATH 環(huán)境變量所指的目錄中查找符合參數(shù) file 的文件名,找到后便執(zhí)行該文件,然后將第二個以后的 參數(shù)當(dāng)做該文件的argv[0]、argv[1]……,最后一個參數(shù)必須用空指針(NULL)作結(jié)束。

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execlp ("ls", "ls", "-l", NULL) == -1) perror ("execlp"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 總用量 16 -rwxr-xr-x 1 root root 7381 Apr 26 15:02 a.out -rw-r--r-- 1 root root 480 Apr 26 15:02 test.c -rw-r--r-- 1 root root 846 Apr 25 17:01 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

開始就講到,當(dāng)指定 file 作為參數(shù)時,如果 file 中包含 /,則就將其視為路徑名。否則就按 PATH 環(huán)境變量,在它所指定的各目錄中搜索可執(zhí)行文件這也就是 execlp 和 execl 的區(qū)別了。

四、execle 函數(shù)

#include <unistd.h> int execle(const char *path, const char *arg, ..., char * const envp[]); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execl是用來執(zhí)行參數(shù) path 字符串所代表的文件路徑,并為新程序復(fù)制最后一個參數(shù)所指示的環(huán)境變量。接下來的參數(shù)代表執(zhí)行該文件時傳遞過去的argv(0)、argv[1]……,最后一個參數(shù)必須用空指針(NULL)作結(jié)束。

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execle ("/bin/ls", "ls", "-l", NULL, NULL) == -1) perror ("execle"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 total 16 -rwxr-xr-x 1 root root 7381 Apr 26 15:32 a.out -rw-r--r-- 1 root root 491 Apr 26 15:32 test.c -rw-r--r-- 1 root root 488 Apr 26 15:13 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

倒數(shù)第二個參數(shù)為,傳遞一個指向字符串指針數(shù)組的指針。

五、execv 函數(shù)

#include <unistd.h> int execv(const char *path, char *const argv[]); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execv() 用來執(zhí)行參數(shù) path 字符串所代表的文件路徑,與 execl() 不同的地方在于 execve() 只需兩個參數(shù),第二個參數(shù)利用數(shù)組指針來傳遞給執(zhí)行文件

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { char *arg[] = {"ls", "-l", NULL};printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execv ("/bin/ls", arg) == -1) perror ("execv"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 總用量 16 -rwxr-xr-x 1 root root 7380 Apr 26 15:48 a.out -rw-r--r-- 1 root root 505 Apr 26 15:48 test.c -rw-r--r-- 1 root root 488 Apr 26 15:13 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

arg 是一個以 NULL 結(jié)尾的字符串?dāng)?shù)組的指針。

六、execvp 函數(shù)

#include <unistd.h> int execvp(const char *file, char *const argv[]); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execvp()會從 PATH 環(huán)境變量所指的目錄中查找符合參數(shù) file 的文件名,找到后便執(zhí)行該文件,然后將第二個參數(shù)argv 傳給該欲執(zhí)行的文件。

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { char *arg[] = {"ls", "-l", NULL};printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execvp ("ls", arg) == -1) perror ("execvp"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 總用量 16 -rwxr-xr-x 1 root root 7381 Apr 26 15:54 a.out -rw-r--r-- 1 root root 502 Apr 26 15:54 test.c -rw-r--r-- 1 root root 488 Apr 26 15:13 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

當(dāng)指定 file 作為參數(shù)時,如果 file 中包含 /,則就將其視為路徑名。否則就按 PATH 環(huán)境變量,在它所指定的各目錄中搜索可執(zhí)行文件這也就是 execvp 和 execv 的區(qū)別了。 execlp 或 execvp 使用路徑前綴中的一個找到了一個可執(zhí)行文件,但是該文件不是由連接編輯器產(chǎn)生的機(jī)器可執(zhí)行文件,則就認(rèn)為該文件是一個 shell 腳本,于是試著調(diào)用 /bin/sh,并以該 file 作為 shell 的輸入。

七、execve 函數(shù)

#include <unistd.h> int execvpe(const char *file, char *const argv[], char *const envp[]); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

execve() 用來執(zhí)行參數(shù) file 字符串所代表的文件路徑,第二個參數(shù)是利用指針數(shù)組來傳遞給執(zhí)行文件,并且需要以空指針(NULL)結(jié)束,最后一個參數(shù)則為傳遞給執(zhí)行文件的新環(huán)境變量數(shù)組。

2、示例說明

#include <stdio.h> #include <unistd.h> #include <stdlib.h> int main (void) { char *arg[] = {"ls", "-l", NULL};printf ("父進(jìn)程開始執(zhí)行\(zhòng)n"); pid_t pid = vfork (); if (pid == -1) perror ("vfork"), exit (1); if (pid == 0) { printf ("子進(jìn)程開始執(zhí)行\(zhòng)n"); if (execve ("/bin/ls", arg, NULL) == -1) perror ("execve"), _exit (1); } sleep (1); printf ("父進(jìn)程執(zhí)行結(jié)束\n"); return 0; } 輸出結(jié)果: 父進(jìn)程開始執(zhí)行 子進(jìn)程開始執(zhí)行 total 16 -rwxr-xr-x 1 root root 7381 Apr 26 16:12 a.out -rw-r--r-- 1 root root 513 Apr 26 16:12 test.c -rw-r--r-- 1 root root 488 Apr 26 15:13 test.c~ 父進(jìn)程執(zhí)行結(jié)束

3、示例解析

第一個參數(shù)為 file,最后一個參數(shù)為 NULL,這沒什么講的了。 重點(diǎn)是 7 個函數(shù)中只有 execve 是內(nèi)核的系統(tǒng)調(diào)用,另外 6 個只是庫函數(shù),它們最終都要調(diào)用該系統(tǒng)調(diào)用。

八、fexecve 函數(shù)?

#include <unistd.h> int fexecve(int fd, char *const argv[], char *const envp[]); 若出錯,返回 -1, 若成功,不返回

1、函數(shù)解析

fexecve ( )執(zhí)行與 execve?相同的任務(wù),區(qū)別在于通過文件描述符 fd?指定要執(zhí)行的文件,而不是通過路徑名。 文件描述符 fd 必須以只讀方式打開,并且調(diào)用方必須具有執(zhí)行權(quán)限它所指的文件。 fexecve 庫函數(shù)使用 /proc 把文件描述符參數(shù)轉(zhuǎn)換成路徑名,execve 用該路徑名去執(zhí)行程序。 暫時不講,找不到相關(guān)示例。

九、find 和 xargs 命令相關(guān)內(nèi)容?

每個系統(tǒng)對參數(shù)表和環(huán)境表的總長度都有一個限制。這種限制是由 ARG_MAX 給出的。在 POSIX.1 系統(tǒng)中,此值至少是 4096 字節(jié)。而我用的 Ubuntu 12.04 在該系統(tǒng)中默認(rèn)為?2097152,以通過 getconf ARG_MAX?可查看系統(tǒng)當(dāng)前設(shè)置的值。參看:getconf命令及查看Linux32位和64位命令 當(dāng)我們執(zhí)行某些命令有時候會報(bào)“Argument list too long”錯誤,例如,當(dāng)前目錄文件很多時執(zhí)行mv * 或rm *,該錯誤表示執(zhí)行命令的參數(shù)太長,超過系統(tǒng)允許的最大值。 出現(xiàn)這種情況時,通常有兩種解決辦法:
1、更改命令執(zhí)行方式。
例如執(zhí)行rm * 時使用如下命令替換:find . -name "*" | xargs rm {}
2、修改 ARG_MAX 的大小 ?(了解,系統(tǒng)不對實(shí)現(xiàn)不了)
參看:ARG_MAX, maximum length of arguments for a new process 參看:lsattr 命令 (1)使用命令lsattr -El sys0 -a ncargs查看ncargs占有字節(jié),輸出結(jié)果:ncargs 512 ARG/ENV list size in 4K byte blocks True
(2)getconf ARG_MAX 查看ARG_MAX設(shè)置值大小,2097152
(3)調(diào)整ncargs占用字節(jié):chdev -l sys0 -a ncargs=8 表示設(shè)置ncargs占用8字節(jié),增加這個值就可以修改ARG_MAX參數(shù)的設(shè)置了

我們講 find 和 xargs 命令時,也涉及到 exec 選項(xiàng)。 參看:C語言再學(xué)習(xí) -- Linux下find命令用法 參看:C語言再學(xué)習(xí) -- Xargs用法詳解 -exec [commend] ? ? ? ?查找后執(zhí)行命令的時候不詢問用戶,直接執(zhí)行。

查找所有jpg文件,并重命名

root@zslf-virtual-machine:/mnt/test# ls abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg root@zslf-virtual-machine:/mnt/test# find ./ -name "*.jpg" -type f | xargs -i cp {} {}.old root@zslf-virtual-machine:/mnt/test# ls abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old

或使用 -exec

root@zslf-virtual-machine:/mnt/test# ls abc.txt def.txt ef.jpg hh.jpg images.tar.gz sd.jpg root@zslf-virtual-machine:/mnt/test# find ./ -name "*.jpg" -exec cp {} {}.old \; root@zslf-virtual-machine:/mnt/test# ls abc.txt def.txt ef.jpg ef.jpg.old hh.jpg hh.jpg.old images.tar.gz sd.jpg sd.jpg.old

十、最常見的錯誤

大家在平時的編程中,如果用到了exec函數(shù)族,一定記得要加錯誤判斷語句。因?yàn)榕c其他系統(tǒng)調(diào)用比起來,exec很容易受傷,被執(zhí)行文件的位置,權(quán)限等很多因素都能導(dǎo)致該調(diào)用的失敗。最常見的錯誤是:
找不到文件或路徑,此時errno被設(shè)置為ENOENT;
數(shù)組argv和envp忘記用NULL結(jié)束,此時errno被設(shè)置為EFAULT;
沒有對要執(zhí)行文件的運(yùn)行權(quán)限,此時errno被設(shè)置為EACCES。

總結(jié)

以上是生活随笔為你收集整理的UNIX再学习 -- exec 函数族的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。