在學(xué)習(xí)資料滿天飛的大環(huán)境下,知識變得非常零散,,體系化的知識并不多,,這就導(dǎo)致很多人每天都努力學(xué)習(xí)到感動自己,最終卻收效甚微,,甚至放棄學(xué)習(xí),。我的使命就是過濾掉大量的無效信息,將知識體系化,,以短平快的方式直達(dá)問題本質(zhì),,把大家從大海撈針的痛苦中解脫出來。
在Linux上編寫C代碼經(jīng)常會用到shell指令,。常用的有三種方式,,我們一一道來。
1 system
最簡單的方式就是直接調(diào)用system接口,,該接口返回-1表示調(diào)用shell指令失敗,,返回127表示調(diào)用/bin/sh失敗,,命令執(zhí)行成功返回0,。
1 #include <stdio.h>
2
3 int main(void)
4 {
5 int ret = 0;
6
7 ret = system("touch test");
8 printf("cmd ret = %d.\n", ret);
9
10 return 0;
11 }
事實上事情并沒有這么簡單,在調(diào)用system期間,,SIGINT和SIGQUIT會被忽略,,這就要求盡量不要在死循環(huán)中調(diào)用system,否則就一定要對返回值做相應(yīng)處理,,man手冊中有代碼示例,。
此外,,還需要注意對SIGCHLD的處理。
system的實現(xiàn)主要是三步:
- 調(diào)用fork創(chuàng)建子進(jìn)程,。
- 調(diào)用execl函數(shù)運行shell程序解析指令并執(zhí)行,。
- 調(diào)用waitpid函數(shù)等待給子進(jìn)程收尸。
由于waitpid函數(shù)執(zhí)行成功的前提是對SIGCHLD的處理不能是SIGIGN,,否則會產(chǎn)生ECHILD錯誤,。
所以,最保險的做法如下:
1 #include <stdio.h>//erold_sighandler = signal(SIGCHLD, SIG_DFL);
2 #include <signal.h>
3
4 typedef void (*sighandler_t)(int);
5
6
7 int main(void)
8 {
9 int ret = 0;
10 sighandler_t old_sighandler;
11
12 //old_sighandler = signal(SIGCHLD, SIG_IGN); //err
13 old_sighandler = signal(SIGCHLD, SIG_DFL);
14 if (SIG_ERR == old_sighandler)
15 return -1;
16 ret = system("touch test");
17 if (0 != ret) {
18 printf("cmd ret = %d.\n", ret);
19 perror("");
20 return -2;
21 }
22 signal(SIGCHLD, old_sighandler);
23
24 return 0;
25 }
2 popen
第二種方式就是使用popen,。該函數(shù)內(nèi)部也會調(diào)用fork產(chǎn)生子進(jìn)程,,然后在子進(jìn)程中通過exec運行/bin/sh。比起system來更靈活一些,,可以創(chuàng)建管道,,與子進(jìn)程進(jìn)行通信。但是,,本質(zhì)上和system的實現(xiàn)是一致的,。
1 #include <stdio.h>
2
3 int main(void)
4 {
5 FILE *pfile = NULL;
6 char buff[500] = {0};
7
8 pfile = popen("touch test.txt", "w");
9 pclose(pfile);
10
11 pfile = popen("ls -al", "r");
12 if (NULL == pfile)
13 return -1;
14 fgets(buff, sizeof(buff), pfile);
15
16 printf("%s");
17
18 pclose(pfile);
19
20 return 0;
21 }
3 exec
前兩種方式其實都是本種方式的封裝,用起來更方便而已,。
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <stdlib.h>
4
5 int main(void)
6 {
7 int child_pid = 0;
8 char * argv[] = {"sh", "-c" , "ls -al .", NULL};
9
10 if (0 == vfork()) {//child process
11
12 if (execv("/bin/sh", argv) < 0) {
13 perror("");
14 exit(-1);
15 }
16
17 } else { //parent
18 wait(&child_pid);
19 printf("child pid done.\n");
20 }
21
22 return 0;
23 }
4 該選擇哪種方式
最簡單的方式就是使用system接口,,但是需要注意對返回值的判斷和errno的解析。如果想和子進(jìn)程進(jìn)行通信(比如想拿到指令執(zhí)行后的輸出),,就考慮使用popen方式,,但由于子進(jìn)程直接繼承了父進(jìn)程的環(huán)境變量,據(jù)說是不安全的(對于系統(tǒng)安全這塊還沒有研究,,感興趣的可以深入下),。如果是讀執(zhí)行效率有一定要求(vfork比fork執(zhí)行效率高),或者想改造/擴(kuò)展上述接口功能,,則可以使用exec族函數(shù)自己實現(xiàn),。
恭喜你又堅持看完了一篇博客,又進(jìn)步了一點點,!如果感覺還不錯就點個贊再走吧,,你的點贊和關(guān)注將是我持續(xù)輸出的噠噠噠動力~~
|