exec族函數函數的作用:
我們用fork函數創建新進程后,經常會在新進程中調用exec函數去執行另外一個程序。當進程調用exec函數時,該進程被完全替換為新程序(在exec都后面的代碼不會被得到執行)。因為調用exec函數并不創建新進程,所以前后進程的ID并沒有改變。
exec族函數功能:
在調用進程內部執行一個可執行文件。可執行文件既可以是二進制文件,也可以是任何Linux下可執行的腳本文件。
函數族:
exec函數族分別是:execl, execlp, execle, execv, execvp, execvpe
函數原型:
#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 execvpe(const char *file, char *const argv[],char *const envp[]);//使用較少
返回值:
exec函數族的函數執行成功后不會返回,調用失敗時,會設置errno并返回-1,然后從原程序的調用點接著往下執行。(errno這個值可以通過perror打印出來)
參數說明:
- path:可執行文件的路徑名字
- arg:可執行程序所帶的參數,第一個參數為可執行文件名字,沒有帶路徑且arg必須以NULL結束,例如:execl("./bin/echoarg",“echoarg”,“abc”,NULL) echoarg是可執行文件名,abc是第一個參數,最后必須以NULL結尾。
- file:如果參數file中包含/,則就將其視為路徑名,否則就按 PATH環境變量,在它所指定的各目錄中搜尋可執行文件。
exec族函數參數極難記憶和分辨,函數名中的字符會給我們一些幫助:
- l : 使用參數列表
- p:使用文件名,并從PATH環境進行尋找可執行文件
- v:應先構造一個指向各參數的指針數組,然后將該數組的地址作為這些函數的參數。
- e:多了envp[]數組,使用新的環境變量代替調用進程的環境變量
字符串轉整型:
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
示例代碼:
****execl代碼****
#include<stdio.h>
#include <unistd.h>
int main()
{printf("before execl");if(execl("./number","number","12","13",NULL)==-1){printf("execl fail\n");perror("because");}printf("after execl");//因為execl函數調用成功所以它之后的代碼就不再執行。return 0;
}****number可執行文件代碼:****
#include<stdio.h>
#include <stdlib.h>
int main(int agrc,char*argv[3])
{printf("參數和為:%d\n",atoi(argv[1])+atoi(argv[2]));return 0;
}可以通過whereis ls查看ls可執行程序的位置:
上述程序中就可以替換為下列代碼——執行ls即可
execl("ls路徑","ls",NULL);
想用ls -l就可以在ls后面加參數即可:
execl("ls路徑","ls","-l",NULL);獲取系統服務時間指令:date
同樣的道理可以用whereis date查看date可執行程序的路徑
上述程序中就可以替換為下列代碼——執行date即可
execl("date路徑","date",NULL);
execl和execlp的區別在于:
exaclp函數帶p,所以能通過環境變量PATH查找到可執行文件ps,當可執行文件的文件路徑中帶有 / 這種路徑符號,我們將其視為路徑名(就是按照這個路徑去找可執行文件),否則就將其視為環境變量(path變量的作用是可以讓我們在沒有這個應用的路徑下面打開我們需要打開的應用,前提是這個應用得在環境變量里面配置了路徑),在linux系統中可以用 echo $PATH 查看當前的環境變量,那些冒號是分隔符。如果用execlp就可以寫為execlp(“date”,“date”,NULL);直接寫date就好。
環境變量和pwd顯示的路徑不一樣,只有將當前路徑加入到環境變量的時候,環境變量里面才會出現當前路徑,也就意味著在任何路徑下都可以訪問該路徑下的可執行文件。
PATH是什么?
如何修改環境變量?
在linux環境中可以通過以下代碼將路徑加入到環境變量:
方法一:
export PATH=$PATH:當前路徑
$PATH表示當前環境變量
//配置完后可以通過echo $PATH查看配置結果。
生效方法:立即生效
有效期限:臨時改變,只能在當前的終端窗口中有效,當前窗口關閉后就會恢復原有的path配置
用戶局限:僅對當前用戶方法二:
通過修改.bashrc文件:
vim ~/.bashrc
//在最后一行添上:
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:(有以下兩種)
1、關閉當前終端窗口,重新打開一個新終端窗口就能生效
2、輸入“source ~/.bashrc”命令,立即生效
有效期限:永久有效
用戶局限:僅對當前用戶方法三:
通過修改profile文件:
vim /etc/profile
/export PATH //找到設置PATH的行,添加
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:系統重啟
有效期限:永久有效
用戶局限:對所有用戶方法四:
通過修改environment文件:
vim /etc/environment
在PATH=”/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games”中加入“:/usr/local/mongodb/bin”
生效方法:系統重啟
有效期限:永久有效
用戶局限:對所有用戶
帶v不帶l的一類exec函數:
其實就是將execl可執行程序里面的餐數放到指針數組里面,然后用數組的首地址代替可執行程序名字和程序的參數。
示例:
char* canshu[]={"number","12","13","NULL"};
execv("./number",canshu)
exec配合fork使用:
實現功能當父進程檢測到輸入為1的時候后,創建子進程把配置文件的字段修改掉。
***********************
以下是存在父子進程的程序
通過調用現有的可執行程序
修改目標文件的內容,他這個
要修改文件要加上絕對路徑
************************
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
#include <sys/wait.h>
int main()
{pid_t pid;pid_t fpid,returnpid;int status;int input=0;int execlreturn=0;while(1){printf("請輸入數字:\n");scanf("%d",&input);if(input==1){fpid=fork();if(fpid>0){returnpid=waitpid(fpid,&status,0);if(WIFEXITED(status)){printf("子進程正常退出,退出參數是:%d\n",WEXITSTATUS(status));}}if(fpid==0){execlreturn=execl("/home/fhn/linuxfile/changfile","changfile","/home/fhn/linuxfile/test.txt",NULL); if(execlreturn==-1){printf("exec fail\n");perror("execfail");}}}else{printf("do nothing\n");}}return 0;
}
system函數:
#include <stdlib.h>
int system(const char *command);函數說明:
system()會調用fork()產生子進程,由子進程來調用/bin/sh-c string來執行參數string字符串所代表的命令
此命令執行完后隨即返回原調用的進程。在調用system()期間SIGCHLD 信號會被暫時擱置,SIGINT和SIGQUIT 信號則會被忽略。sh -c就相當于./ 就是為了執行后面的指令string返回值:
如果fork()失敗 返回-1:出現錯誤如果exec()失敗,表示不能執行Shell,返回值相當于Shell執行了exit(127)如果執行成功則返回子Shell的終止狀態如果system()在調用/bin/sh時失敗則返回127,其他失敗原因返回-1。若參數string為空指針(NULL),則返回非零值。如果system()調用成功則最后會返回執行shell命令后的返回值,但是此返回值也有可能為 system()調用/bin/sh失敗所返回的127,因此最好能再檢查errno 來確認執行成功。system調用結束后還會返回原程序繼續執行system下面的代碼,而exec族函數不會。我的理解:
在system函數調用成功時返回進程的狀態值
當因shell不能執行時也就是system()在調
用/bin/sh時失敗時返回127,其他失敗情況
返回-1,命令string為空指針(NULL)system
函數的返回值很簡單明了,只有0和1。返回1,
表明系統的命令處理程序,即/bin/sh是可用的。
相反,如果命令處理程序不可用,則返回0。 在
判斷返回值時最好能再檢查errno來確認執行成功使用例子:
system("/home/fhn/linuxfile/changfile /home/fhn/linuxfile/test.txt");
popen函數和system函數區別?
popen函數:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
函數說明:
- popen()會調用fork()產生子進程,然后從子進程中調用/bin/sh -c來執行參數command的指令。
- 參數type可使用“r”代表讀取,“w”代表寫入。依照此type值,popen()會建立管道連到子進程的標準輸出設備(比如說:ps指令,所以可以用“”r“”根據返回的指針讀取子進程的標準輸出內容)或標準輸入設備,然后返回一個文件指針。隨后進程便可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標準輸入設備中。
- 此外,所有使用文件指針(FILE*)操作的函數也都可以使用,除了fclose()以外。
- 如果 type 為 r,那么調用進程讀進 command 的標準輸出。如果 type 為 w,那么調用進程寫到 command 的標準輸入。
- popen比sysytem的好處是可以通過管道獲取運行結果
返回值:
若成功則返回文件指針,否則返回NULL,錯誤原因存于errno中。
注意:
popen()會繼承環境變量,通過環境變量可能會造成系統安全的問題。
為什么要用popen函數:
#include<stdio.h>
#include <stdlib.h>
int main()
{system("ps");return 0;
}這幾行代碼的運行結果如下:
fhn@ubuntu:~/jincheng$ ./popen PID TTY TIME CMD20157 pts/3 00:00:03 bash24755 pts/3 00:00:00 popen24756 pts/3 00:00:00 sh24757 pts/3 00:00:00 ps如果想要把它運行的結果放到文件或者字符串中去
就要用到popen函數將結果流入到文件中去,如下面代碼:
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{char* buf;FILE* fd;int n_read;buf=(char*)malloc(1024);fd=popen("ps","r");n_read=fread(buf,1,1024,fd);pclose(fd);printf("管道輸出:%s",buf);return 0;
}
以下是輸出結果:fhn@ubuntu:~/jincheng$ ./popen
管道輸出: PID TTY TIME CMD20157 pts/3 00:00:03 bash24865 pts/3 00:00:00 popen24866 pts/3 00:00:00 sh24867 pts/3 00:00:00 ps如果有將信息流入到內存而不打印則屏幕上面沒有顯示。
注意popen用的是fread、fwrite而不是read和write,因為read返回的是文件描述符不符合,fread返回的是文件指針