使用 system
C標準庫中的system 函數(shù)提供了一種調(diào)用其他程序的簡單方法,利用system 函數(shù)調(diào)用程序結(jié)果與從shell中執(zhí)行這個程序基本相似;也就是說system 函數(shù)可以運行一個shell腳本的命令
//案例:
#incude <stdio.h>
int main()
{
int return_value;
return_value = system("ls -l"); // 調(diào)用失敗返回 -1;
return return_value;
}
使用fork和exec族函數(shù)
- fork()函數(shù)
頭文件:#include <unistd.h>
函數(shù)的一般形式:
pid_t fork(void);
返回值:成功返回0,表示子進程;大于零表示父進程;失敗返回 -1,并設(shè)置錯誤代碼。
錯誤代碼:
EAGAIN:當前的進程數(shù)已經(jīng)達到了系統(tǒng)規(guī)定的上限
ENOMEM:內(nèi)存不足
// 案例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
pid_t pid;
pid = fork();
if (0 == pid)
{
printf("This is the child process, its ID is %d.\n ", (int)getpid());
printf("This is the child process, its parent process's ID is %d.\n", (int)getppid());
}
else if (0 < pid)
{
printf("This is the parent process, its ID is %d.\n", (int)getpid());
}
else
{
perror("fork error");
exit(1); // 0表示EXIT_SUCCESS;1表示EXIT_FAILURE
}
return 0;
}
fork() 和 vfork() 的區(qū)別:
使用fork調(diào)用會為子進程復(fù)制父進程所擁有的資源(進程環(huán)境、棧堆等),而vfork設(shè)計時要求子進程立即調(diào)用exec,而不修改任何內(nèi)存,vfork新建的子進程則是和父進程共享所有的資源,在子進程中對數(shù)據(jù)的修改也就是對父進程數(shù)據(jù)的修改,這一點一定要注意。
使用fork系統(tǒng)調(diào)用產(chǎn)生父子進程,在默認情況下無需人為干預(yù),父子進程的執(zhí)行順序是由操作系統(tǒng)調(diào)度的,誰先執(zhí)行并不確定。而對于vfork所生成的父子進程,父進程是在子進程調(diào)用了_exit或者exec后才會繼續(xù)執(zhí)行,不調(diào)用這兩個函數(shù)父進程會等待(父進程由于沒有收到子進程表示已執(zhí)行的相關(guān)信號所以進行等待)。
vfork的出現(xiàn)是為了解決當初fork浪費用戶空間內(nèi)存的問題,因為在fork后,很有可能去執(zhí)行exec函數(shù)重生,vfork設(shè)計思想就是取消fork造成堆棧的復(fù)制,使用vfork可以避免資源的浪費,但是也帶了資源共享所產(chǎn)生的問題。
在[Linux](http://lib.csdn.net/base/linux)中,對fork進行了優(yōu)化,調(diào)用時采用寫時復(fù)制 (COW,copy on write)的方式,在系統(tǒng)調(diào)用fork生成子進程的時候,不馬上為子進程復(fù)制父進程的資源,而是在遇到“寫入”(對資源進行修改)操作時才復(fù)制資源。它使得一個普通的fork調(diào)用非常類似于vfork,但又避免了vfork的缺點,使得vfork變得沒有必要。
- exec() 函數(shù)
fork函數(shù)是用于創(chuàng)建一個子進程,該子進程幾乎是父進程的副本,而有時我們希望子進程去執(zhí)行另外的程序,exec函數(shù)族就提供了一個在進程中啟動另一個程序執(zhí)行的方法。它可以根據(jù)指定的文件名或目錄名找到可執(zhí)行文件,并用它來取代原調(diào)用進程的數(shù)據(jù)段、代碼段和堆棧段,在執(zhí)行完之后,原調(diào)用進程的內(nèi)容除了進程號外,其他全部被新程序的內(nèi)容替換了
包含:execl()、execlp()、execle()、execv()、execvp()、execvpe()六個函數(shù)。
頭文件:#include <unistd.h>
函數(shù)的一般形式:
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 execvpe(const char *file, char *const argv[],
char *const envp[]);
注意:
1.p:表示當前執(zhí)行路徑,無p則表示絕對經(jīng);
2.l:用一個字符串數(shù)組作為調(diào)用程序的參數(shù),這個參數(shù)必須以一個NULL指針作為結(jié)束標志;
3.v:以C語言中的varqs形式接受的參數(shù)列表;參數(shù)傳遞為構(gòu)造指針數(shù)組方式
4.e:表示指明了環(huán)境變量列表的參數(shù);
返回值:成功無返回值,失敗返回 -1;
錯誤代碼:
在使用exec函數(shù)族時,一定要加上錯誤判斷語句。因為exec很容易執(zhí)行失敗,其中最常見的原因
有:
ENOENT:找不到文件或路徑
EFAULT:數(shù)組argv和envp忘記用NULL結(jié)束
EACCES:沒有對應(yīng)可執(zhí)行文件的運行權(quán)限
// 案例:
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);
注意:exec函數(shù)族形參展開時的前兩個參數(shù),第一個參數(shù)是帶路徑的執(zhí)行碼(execlp、execvp函數(shù)第一個參數(shù)是無路徑的,系統(tǒng)會根據(jù)PATH自動查找然后合成帶路徑的執(zhí)行碼),第二個是不帶路徑的執(zhí)行碼,執(zhí)行碼可以是二進制執(zhí)行碼和Shell腳本。
// 案例:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int spawn(char* program, char** arg_list)
{
pid_t pid;
pid = fork();
if (-1 == pid)
{
perror("fork error");
exit(1);
}
else if (0 == pid)
{
execvp(program, arg_list);
fprintf(stderr, "an error occurred in execvp\n");
abort();
}
else
{
return pid;
}
}
int main()
{
char* arg_list[] = { "ls", "-l", "/", NULL}; // 參數(shù)列表必須以NULL指針結(jié)束
spawn("ls", arg_list);
printf("done with main program\n");
return 0;
}