exec函数族

2019-12-13 00:31栏目:操作系统
TAG:

linux c语言 fork(卡塔尔(قطر‎ 和 exec 函数的简要介绍和用法

 

      假诺我们在编写1个c程序时想调用1个shell脚本可能进行1段 bash shell命令, 应该如何贯彻呢?

      其实在<stdlib.h> 那一个头文件中满含了1个调用shell命令只怕脚本的函数 system(卡塔尔(قطر‎;直接把 shell命令作为参数字传送入 system函数就能够了, 的确很方便. 关于system 有风流罗曼蒂克段那样的牵线:   system 试行时内部会自动启用fork(卡塔尔国 新建1个经过,  功能未有一向利用fork(卡塔尔(英语:State of Qatar) 和 exec函数高.

 

       那么这篇随笔其实便是介绍一下fork(卡塔尔国 和 exec函数的用法, 甚至哪些利用它们来庖代system函数.

      

风流罗曼蒂克、进程的创制fork(卡塔尔(قطر‎函数

fork(卡塔尔(قطر‎函数通过系统调用创制四个与原来进度(父进度卡塔尔国大约完全雷同的历程(子进度是父进度的别本,它将赢得父进度数据空间、堆、栈等能源的别本。注意,子进度具备的是上述存款和储蓄空间的“别本”,那象征老爹和儿子进程间不分享这个囤积空间。linux将复制父进程的地点空间内容给子进度,因而,子进度有了单独之处空间。卡塔尔(英语:State of Qatar),也便是那四个进程做完全雷同的事。

1. fork() 函数

bob体育平台 1

在fork后的子进程中使用exec函数族,能够装入和平运动转此外程序(子进度替换原有进度,和父进度做不一致的事卡塔尔国。

1.1 fork(卡塔尔国 函数的效劳

       日常来说, 我们编辑1个日常的c程序, 运营这么些程序直到程序截止, 系统只会分配1个pid给这些顺序, 也就就说, 系统里只会有一条有关这么些顺序的进度.

 

        可是施行了fork(卡塔尔 那几个函数就区别了. 

        fork 这几个乌Crane语单词在阿拉伯语里是"分叉"意思,  fork(卡塔尔这一个函数成效也很符合这么些意思.  它的意义是复制当前经过(满含过程在内部存储器里的酒馆数据卡塔尔国为1个新的镜像. 然后那些新的镜像和旧的进度同期实施下去. 相当于自然1个经过, 境遇fork(卡塔尔国函数后就分开成三个进程同有的时候候奉行了. 况且那五个经过是互不影响

 

        参照他事他说加以考查下边那些小程序:

 

[cpp] view plain copy

 

  1. int fork_3(){  
  2.     printf("it's the main process step 1!!nn");  
  3.   
  4.     fork();  
  5.   
  6.     printf("step2 after fork() !!bob体育app,nn");  
  7.   
  8.     int i; scanf("%d",&i);   //prevent exiting  
  9.     return 0;  
  10. }  

 

          在这里个函数里, 共有两条printf语句, 可是奉行奉行时则打出了3行新闻. 如下图: 

 由fork创立的新进程被称为子进度(child process卡塔尔。该函数被调用一遍,但回到一回。一次回到的分裂是子进度的重返值是0,而父进度的再次回到值则是 新子进度的进度ID。将子进度ID重回给父进度的说辞是:因为多个进度的子进度能够多于三个,全体未有二个函数使一个进度可以获取其全体子过程的进度ID。fork使子进度获得重返值0的说辞是:贰个历程只会有一个父进程,所以子进程总是能够调用getppid以得到其父进程的历程ID(进度ID  0总是由交流进度使用,所以一个子历程的进度ID不也许为0卡塔尔。

fork创设三个新的经过就生出了三个新的PID,exec运行二个新程序,替换原有的进度,由此这几个新的被 exec 实施的进程的PID不会变动(和调用exec的经过的PID一样卡塔尔(英语:State of Qatar)。

bob体育平台 2

 

            为啥吗, 因为fork(卡塔尔函数将以此顺序分叉了哟,  见上面包车型客车图解:

bob体育平台 3

 

         能够看看程序在fork(卡塔尔国函数实践时都独有1条主进程, 所以 step 1 会被打字与印刷输出1次.

         施行 fork(卡塔尔国函数后,  程序分叉成为了四个经过, 1个是原来的主进度,  另1个是新的子进度, 它们都会实施fork(卡塔尔 函数后边的代码, 所以 step2 会被 两条经过分别打字与印刷输出各壹次, 显示屏上就大器晚成共3条printf 语句了!

 

         能够见到那个函数最前面小编用了 scanf(卡塔尔(英语:State of Qatar)函数来防止程序退出,  此时查看系统的经过, 就能开采多个一样名字的历程:

 

 

#include<unistd.h>

 

如上海教室, pid 8808 那多少个正是主进度了, 而 pid  8809要命便是子进度啊, 因为它的parent pid是 8808啊!

          

          亟需小心的是, 假使未有做特殊管理, 子进程会一直存在, 就算fork_3(卡塔尔(قطر‎函数被调用完结,  子进度会和主程序雷同,重回调用fork_3()函数的上一流函数继续试行, 直到总体程序退出.

 

          能够看来, 固然fork_3(卡塔尔(قطر‎ 被实行2次,  主程序就能分开若干次, 最后成为4个进程, 是否有一些危殆. 所以上边所谓的特殊管理很要紧呀!

 

    子进程和父进度继续实行fork之后的通令。子进程是父进度的仿制品。比如,子进程得到父进度数据空间、堆和栈的仿制品。注意,那是子进程具备的正片。父、子进度并共享那一个囤积部分。假若正文段是只读的,则父、子进度分享正文段。

extern char **environ;

1.2 区别分主程序和子程序.

        实际使用中, 单纯让程序分叉意义非常的小, 大家新扩大三个子前后相继, 很或然是为着让子进度单独实行风度翩翩段代码. 完结与主进程不一样的成效.

         要兑现地点所说的职能, 实际上正是让子进程和主进度实践不风度翩翩的代码啊.

         所以fork(卡塔尔 实际上有再次来到值, 何况在两条经过中的再次回到值是例外的, 在主进度里 fork(卡塔尔国函数会回到主进程的pid,   而在子进程里会再次来到0!   所以大家可以依照fork(卡塔尔 的重临值来剖断进程到底是哪个进程, 就能够利用if 语句来执行分歧的代码了!

 

        如下边这些小程序fork_1():

 

[cpp] view plain copy

 

  1. int fork_1(){  
  2.     int childpid;  
  3.     int i;  
  4.   
  5.     if (fork() == 0){  
  6.         //child process  
  7.         for (i=1; i<=8; i++){  
  8.             printf("This is child processn");  
  9.         }  
  10.     }else{  
  11.         //parent process  
  12.         for(i=1; i<=8; i++){  
  13.             printf("This is parent processn");  
  14.         }  
  15.     }  
  16.   
  17.     printf("step2 after fork() !!nn");  
  18. }  

        小编对fork(卡塔尔 函数的重返值举办了剖断, 如若 再次来到值是0, 小编就让以为它是子进度, 不然是主程序.  那么本身就足以让这两条长河输出不相同的新闻了.

 

       

          输出音讯如下图:

bob体育平台 4

 

          能够见到 子程序和主程序分别出口了8条不相同的音讯,  然则它们并非平整改动输出的, 因为它们两条经过是并行平行影响的, 何人的手快就在显示屏上先输出,  每一次运转的结果皆有望两样哦.

 

        下边是图解:

bob体育平台 5

 

          由图解知两条经过都对fork(卡塔尔再次回到值试行剖断,  在if 推断语句中分头实施各自的代码.  不过if判别实现后,  依旧会回各自实践接下去的代码. 所以 step2 依旧输出了2次.

    

int execl(const char *path,const char *arg, ...);

1.4 使用exit(卡塔尔(قطر‎ 函数令子进度在if 判定内截至.

          参谋上边包车型地铁函数, 尽管使用if 对 fork(卡塔尔国 的重返值进行剖断,  完毕了子进度和 主进程在if决断的界定内施行了差别的代码,  不过犹如下边包车型地铁流程图, 意气风发旦if施行到位, 他们依旧会分别施行前边的代码. 

          平常那不是大家期待的,  大家越多时会希望子进度实行意气风发段非常的代码后就让他甘休,  后边的代码让主程序奉行就能够了.

          那么些达成起来异常粗略, 在子程序的if 条件内最终加上exit(卡塔尔(قطر‎函数就ok了.

 

         将方面包车型客车fork_1(卡塔尔函数校勘一下, 加上exit语句:

 

[cpp] view plain copy

 

  1. int fork_1(){  
  2.     int childpid;  
  3.     int i;  
  4.   
  5.     if (fork() == 0){  
  6.         //child process  
  7.         for (i=1; i<=8; i++){  
  8.             printf("This is child processn");  
  9.         }  
  10.         exit(0);  
  11.     }else{  
  12.         //parent process  
  13.         for(i=1; i<=8; i++){  
  14.             printf("This is parent processn");  
  15.         }  
  16.     }  
  17.   
  18.     printf("step2 after fork() !!nn");  
  19. }  

       再看看输出:

 

bob体育平台 6

 

            能够看来, step2只输出1次了,   那是因为子程序在 if条件内完工了啊, 生龙活虎旦 if 剖断成, 就只剩下1个主进程推行上面的代码了, 这多亏我们想要的!

            注意: exit(卡塔尔(英语:State of Qatar) 函数在 stdlib.h 头文件内

 

流程图:

bob体育平台 7

 

 

 

    以后众多的贯彻并不做一个父进度数据段和堆的一丝一毫拷贝,因为在fork之后常常跟随着exec。作为代表,使用了写时复制(copy-on-write,cow卡塔尔的本领。那几个区域由父、子进度分享,而且基本将他们的存取许可权改动位只读的。若是有经过试图改进那些区域,则内核包非凡,典型的是虚存系统中的“页”,做叁个正片。

int execlp(const char *file,const char *arg, ...);

1.4 使用wait(卡塔尔国 函数主程序等子程序推行实现(退出卡塔尔后再实践.   

 

        由地点例子获悉,  主程序和子程序的试行顺序是轻便的,  但是实际上景况下, 平常我们期望子进度实行后,  才继续施行主进度. 

        比方对于地点的fork_1(卡塔尔函数, 笔者想先输出子进度的8个 "This is child process"  然后再出口 8个 主进度"This is parent process", 改咋办?

        wait(卡塔尔(قطر‎函数就提供了这些效应,    在if 条件内的  主进程呢部分内 加上wait(卡塔尔(英语:State of Qatar) 函数, 就能够让主进度实践fork(卡塔尔(قطر‎函数时先hold 住, 等子进度退出后再实践, 平时会合营子进度的exit(卡塔尔国函数一齐使用.

 

        我将fork_1(卡塔尔(قطر‎函数改正一下, 加多了wait(卡塔尔(英语:State of Qatar)语句:

 

[cpp] view plain copy

 

  1. int fork_1(){  
  2.     int childpid;  
  3.     int i;  
  4.   
  5.     if (fork() == 0){  
  6.         //child process  
  7.         for (i=1; i<=8; i++){  
  8.             printf("This is child processn");  
  9.         }  
  10.         exit(0);  
  11.     }else{  
  12.         //parent process  
  13.         wait();  
  14.         for(i=1; i<=8; i++){  
  15.             printf("This is parent processn");  
  16.         }  
  17.     }  
  18.   
  19.     printf("step2 after fork() !!nn");  
  20. }  

 

输出:

 

bob体育平台 8

      看到那时候的显示器输出就很有规律了!

      其实wait(卡塔尔国 函数还会有1个职能, 正是还不错1个 pid_t(在unistd.h内,其实正是Int啦卡塔尔 指针类型参数,   给这些参数赋上子进度退出前的体系pid值

     流程图:

  bob体育平台 9

 

 

 

 

 

int execle(const char *bob体育平台,path,const char *arg,...,char * const envp[]);

2. exec 函数组

 

      必要专一的是exec实际不是1个函数, 其实它只是风度翩翩组函数的统称, 它归纳下边6个函数:

     

[cpp] view plain copy

 

  1. #include <unistd.h>  
  2.   
  3. int execl(const char *path, const char *arg, ...);  
  4.   
  5. int execlp(const char *file, const char *arg, ...);  
  6.   
  7. int execle(const char *path, const char *arg, ..., char *const envp[]);  
  8.   
  9. int execv(const char *path, char *const argv[]);  
  10.   
  11. int execvp(const char *file, char *const argv[]);  
  12.   
  13. int execve(const char *path, char *const argv[], char *const envp[]);  

 

       能够见到那6个函数名字不一样, 并且他们用于采纳的参数也分歧.

       实际上他们的功能都是基本上的, 因为要用以选取差异的参数所以要用分化的名字分别它们, 终归c语言未有函数重载的效果嘛..  

 

       不过其实它们的命名是有规律的:

       exec[l or v][p][e]

       exec函数里的参数能够分为3个部分,      实践文书部分,     命令参数部分,   情况变量部分.

        举例笔者要推行1个指令   ls -l /home/gateman  

        推行文书部分正是  "/usr/bin/ls"

        命令参Gaby赛部分就是 "ls","-l","/home/gateman",NULL              见到是以ls早先 每1个空格都必须分开成2个部分, 并且以NULL结尾的啊.

        情况变量部分, 那是1个数组,最终的要素必需是NULL 例如  char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};

        

        好了说下命名准绳:

        e后续,  参数必需带情况变量部分,   遇到变零部分参数会成为施行exec函数时期的景况变量, 少之甚少用

        l 后续,   命令参数部分必需以"," 相隔, 最终1个指令参数必需是NULL

        v 后续,   命令参数部分必得是1个以NULL结尾的字符串指针数组的头顶指针.         比如char * pstr正是1个字符串的指针, char * pstr[] 正是数组了, 分别针对各类字符串.

        p后续,   施行文书部分可以不带路线, exec函数会在$PATH中找

 

          

         还应该有1个注意的是, exec函数会代替施行它的进度,  也正是说, 风流倜傥旦exec函数举行成功, 它就不会回来了, 进程截止.   不过若是exec函数推行倒闭, 它会再次回到战败的音信,  况兼经过继续实行前边的代码!

 

       平日exec会放在fork(卡塔尔国 函数的子进度部分, 来替代子进度施行啦, 实行成功后子程序就能流失,  但是施行停业以来, 必须用exit(卡塔尔函数来让子进度退出!

       下边是种种例子:

 

实例1:

int execv(const char *path,char *const argv[]);

2.1  execv 函数

 

[cpp] view plain copy

 

  1. int childpid;  
  2. int i;  
  3.   
  4. if (fork() == 0){  
  5.     //child process  
  6.     char * execv_str[] = {"echo", "executed by execv",NULL};  
  7.     if (execv("/usr/bin/echo",execv_str) <0 ){  
  8.         perror("error on exec");  
  9.         exit(0);  
  10.     }  
  11. }else{  
  12.     //parent process  
  13.     wait(&childpid);  
  14.     printf("execv donenn");  
  15. }  

留意字符串指针数组的定义和赋值

 

 

 

int execvp(const char *file,char *const argv[]);

2.2  execvp 函数

 

 

[cpp] view plain copy

 

  1. if (fork() == 0){  
  2.     //child process  
  3.     char * execvp_str[] = {"echo", "executed by execvp",">>", "~/abc.txt",NULL};  
  4.     if (execvp("echo",execvp_str) <0 ){  
  5.         perror("error on exec");  
  6.         exit(0);  
  7.     }  
  8. }else{  
  9.     //parent process  
  10.     wait(&childpid);  
  11.     printf("execvp donenn");  
  12. }  

 

#include <stdio.h>

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

2.3 execve 函数

 

[cpp] view plain copy

 

  1. if (fork() == 0){  
  2.     //child process  
  3.     char * execve_str[] = {"env",NULL};  
  4.     char * env[] = {"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};  
  5.     if (execve("/usr/bin/env",execve_str,env) <0 ){  
  6.         perror("error on exec");  
  7.         exit(0);  
  8.     }  
  9. }else{  
  10.     //parent process  
  11.     wait(&childpid);  
  12.     printf("execve donenn");  
  13. }  

 

 

#include <stdlib.h>

exec函数族装入并运路程序path/file,并将参数arg0(arg1, arg2, argv[], envp[]卡塔尔传递给子程序,出错再次来到-1.

2.4 execl 函数

 

[cpp] view plain copy

 

  1. if (fork() == 0){  
  2.     //child process  
  3.     if (execl("/usr/bin/echo","echo","executed by execl" ,NULL) <0 ){  
  4.         perror("error on exec");  
  5.         exit(0);  
  6.     }  
  7. }else{  
  8.     //parent process  
  9.     wait(&childpid);  
  10.     printf("execv donenn");  
  11. }  

 

#include <unistd.h>

在exec函数族中,后缀l、v、p、e内定函数将有着某种操作才能:

2.5 execlp 函数

 

[cpp] view plain copy

 

  1. if (fork() == 0){  
  2.     //child process  
  3.     if (execlp("echo","echo","executed by execlp" ,NULL) <0 ){  
  4.         perror("error on exec");  
  5.         exit(0);  
  6.     }  
  7. }else{  
  8.     //parent process  
  9.     wait(&childpid);  
  10.     printf("execlp donenn");  
  11. }  

 

 

后缀操作技能

2.6 execle 函数

 

[cpp] view plain copy

 

  1. if (fork() == 0){  
  2.     //child process  
  3.     char * env[] = {"PATH=/home/gateman", "USER=lei", "STATUS=testing", NULL};  
  4.     if (execle("/usr/bin/env","env",NULL,env) <0){  
  5.         perror("error on exec");  
  6.         exit(0);  
  7.     }  
  8. }else{  
  9.     //parent process  
  10.     wait(&childpid);  
  11.     printf("execle donenn");  
  12. }  

 

 

 输出:

bob体育平台 10

 

 

int glob = 6;

l希望选拔以逗号分隔的参数列表,列表以NULL指针作为完毕标记

3. fork(卡塔尔(英语:State of Qatar) 和exec 函数与system(卡塔尔(英语:State of Qatar)函数比较

     见到上边execvp函数的输出. 你会开采 exec函数只是系统调用, 它是不接济管线管理的

     而system(卡塔尔函数是支撑的.   他的里边会活动fork()1个子进度,不过功用未有fork(卡塔尔(英语:State of Qatar) 和 exec协作使用好.

 

     可是exec 协助实践脚本.  所以无需管线管理的一声令下或然脚本可以行使fork(卡塔尔国 和 exec函数来施行.

 

 

char buf[] = "a write to stdoutn";

v希望接收到二个以NULL结尾的字符串数组的指针

4. 利用 fwrite() ,fork() 和exec 函数 替代system()函数.

 

     上面讲过了, 即便exec函数不帮衬管线, 而且命令参数复杂, 不过它支持实行脚本啊, 所以大家可以使用fwrite将 有管线管理的授命写入1个脚本中, 然后使用exec函数来实践那一个脚本.

     上面会编写1个base_exec(char *卡塔尔 函数, 接受1个字符串参数,   然后实行它.

 

      这里只会大意写出那几个函数的逻辑步骤:

      1. 运用getuid函数拿到当前的pid,  然后使用pid取妥善前唯后生可畏的文书名, 制止因为相像程序同期施行爆发冲突!

      2.  利用fwrite函数在 /tmp/上边  创设1个地点文件名的剧本文件.     因为/tmp/ 任何客户都足以读写啊

     3.  把命令参数写入脚本

     4. 应用fork(卡塔尔(英语:State of Qatar) 和 exec(卡塔尔 实践那一个本子

     5. 有需求的话当exec试行完, 记录日志.

 

     下边便是i代码:

头文件:

base_exec.h

 

[cpp] view plain copy

 

  1. #ifndef __BASE_EXEC_H_  
  2. #define __BASE_EXEC_H_  
  3.   
  4.     int base_exec(char *) ;  
  5.   
  6. #endif /* BASE_EXEC_H_ */  

源文件:

 

base_exec.c

 

[cpp] view plain copy

 

  1. #include "base_exec.h"  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <string.h>  
  5. #include <unistd.h>  
  6. #include <time.h>  
  7.   
  8. #define LOGFILE "/home/gateman/logs/c_exec.log"  
  9.   
  10. int base_exec(char * pcmd){  
  11.     FILE * pf;  
  12.     pid_t pid = getpid();  
  13.     char pfilename[20];  
  14.     sprintf(pfilename, "/tmp/base_exec%d.sh",pid);  
  15.   
  16.     pf=fopen(pfilename,"w"); //w is overwrite, a is add  
  17.     if (NULL == pf){  
  18.         printf("fail to open the file base_exec.sh!!!n");  
  19.         return -1;  
  20.     }  
  21.   
  22.     fwrite("#!/bin/bashn", 12, 1, pf);  
  23.     fwrite(pcmd, strlen(pcmd),1, pf);  
  24.     fwrite("n", 1,1, pf);  
  25.   
  26.     fclose(pf);  
  27.   
  28.     if (fork() ==0 ){  
  29.         //child processj  
  30.         char * execv_str[] = {"bash", pfilename, NULL};  
  31.         if (execv("/bin/bash",execv_str) < 0){  
  32.             perror("fail to execv");  
  33.             exit(-1);  
  34.         }  
  35.     }else{  
  36.         //current process  
  37.         wait();  
  38.         pf=fopen(LOGFILE,"a");  
  39.   
  40.         if (NULL == pf){  
  41.             printf("fail to open the logfile !!!n");  
  42.             return -1;  
  43.         }  
  44.         time_t t;  
  45.         struct tm * ptm;  
  46.         time(&t);  
  47.         ptm  = gmtime(&t);  
  48.         char cstr[24];  
  49.         sprintf (cstr, "time: %4d-%02d-%02d %02d:%02d:%02dn", 1900+ptm->tm_year,ptm->tm_mon,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);  
  50.         fwrite(cstr, strlen(cstr),1, pf);  
  51.   
  52.         int uid = getuid();  
  53.         sprintf(cstr, "uid: %dncommand:n",uid);  
  54.         fwrite(cstr, strlen(cstr),1, pf);  
  55.   
  56.         fwrite(pcmd, strlen(pcmd),1, pf);  
  57.         fwrite("nnn", 3,1, pf);  
  58.         fclose(pf);  
  59.         remove(pfilename);  
  60.         return 0;  
  61.     }  
  62.     return 0;  
  63. }  

 

p是一个以NULL结尾的字符串数组指针,函数能够DOS的PATH变量查找子程序文件

int main()

e函数传递钦点参数envp,允许退换子进程的蒙受,无后缀e时,子进度使用当前前后相继的遭逢

{

int main(int argc, char *argv[])

int var;

{

int pid;

//以NULL结尾的字符串数组的指针,相符满含v的exec函数参数

 

char *arg[] = {"ls", "-a", NULL};

var = 88;

/**

 

* 创立子进度并调用函数execl

if(write(STDOUT_FILENO,buf,sizeof(buf) -1) != sizeof(buf) -1)

* execl 中希望接受以逗号分隔的参数列表,并以NULL指针为结束标识

{

*/

perror("fail to write");

if( fork() == 0 )

return -1;

{

}

// in clild

 

printf( "1------------execl------------n" );

printf("before forkn");

if( execl( "/bin/ls", "ls","-a", NULL ) == -1 )

 

{

if((pid = fork()) < 0)

perror( "execl error " );

{

exit(1);

perror("fail to fork");

}

return -1;

}

}else 

/**

if(pid == 0)

*成立子进度并调用函数execv

{

*execv中希望选择叁个以NULL结尾的字符串数组的指针

glob ++;

*/

var ++;

if( fork() == 0 )

}else{

{

sleep(2);

// in child

}

printf("2------------execv------------n");

 

if( execv( "/bin/ls",arg) < 0)

printf("pid = %d,glob = %d,var = %dn",getpid(),glob,var);

{

exit(0);

perror("execv error ");

}

exit(1);

 

}

运作结果:

}

bob体育平台 11

/**

从地点可以观察,因为子进度和父进度拥有独立的情理内部存款和储蓄器空间,所以当子进度对拷贝来的数码做改过的时候,并从未影响到父进度。

*成立子进度并调用 execlp

 

*execlp中

注意:

*l希望选取以逗号分隔的参数列表,列表以NULL指针作为完结标记

        1.相通的话,fork之后父进度先推行只怕子进程先推行是不显明的。那决定于内核所使用的调节算法。

*p是多少个以NULL结尾的字符串数组指针,函数能够DOS的PATH变量查找子程序文件

        

*/

        2.从地点能够看见两遍的运作结果不风流洒脱致。大家精通write函数是不带缓存的。因为在fork以前调用write,所以其数量写到规范输出叁次。不过,标准I/O库是带缓存的。纵然标准输出连到终端设备,则它是行缓存的,不然它是全缓存的。当以交互作用形式运维该程序时,只拿到printf输出的行一回,其缘由是标准输出缓存由新行符刷新。不过当将规范输出重新定向到八个文件时,却获得printf输骑行若干遍。其原因是,在fork早先调用了printf贰次,当调用fork时,该行数据仍在缓存中,然后在父进度数据空间复制到子进程中时,该缓存数据也被复制到子进度中。于是当场父、子进度各自有了带该行内容的缓存。在exit早前的第三个printf将其数额拉长到现成的缓存中。当各种进程终止时,其缓存中的内容被写到相应文件中。

if( fork() == 0 )

 

{

 

// in clhild

实例 2:

printf("3------------execlp------------n");

 

if( execlp( "ls", "ls", "-a", NULL ) < 0 )

#include <stdio.h>

{

#include <stdlib.h>

perror( "execlp error " );

#include <unistd.h>

exit(1);

 

}

int glob = 6;

}

 

/**

int main()

*成立子里程并调用execvp

{

*v 望接纳到二个以NULL结尾的字符串数组的指针

int var;

*p 是贰个以NULL结尾的字符串数组指针,函数能够DOS的PATH变量查找子程序文件

int pid;

*/

 

if( fork() == 0 )

var = 88;

{

 

printf("4------------execvp------------n");

printf("father:n");

if( execvp( "ls", arg ) < 0 )

printf("&glob = %pn",&glob);

{

printf("&var = %pn",&var);

perror( "execvp error " );

printf("__________________________________n");

exit( 1 );

 

}

if((pid = fork()) < 0)

}

{

/**

perror("fail to fork");

*创立子进度并调用execle

return -1;

*l 希望选用以逗号分隔的参数列表,列表以NULL指针作为完成标识

 

*e 函数字传送递钦赐参数envp,允许更换子进度的条件,无后缀e时,子进度使用当前景序的条件

}else 

*/

if(pid == 0)

if( fork() == 0 )

{

{

printf("child var value not changen:");

printf("5------------execle------------n");

printf("&glob = %pn",&glob);

if( execle("/bin/ls", "ls", "-a", NULL, NULL) == -1 )

printf("&var = %pn",&var);

{

 

perror("execle error ");

glob ++;

exit(1);

var ++;

}

 

}

printf("__________________________________n");

/**

printf("child var value change:n");

*创设子进度并调用execve

printf("&glob = %pn",&glob);

* v 希望采用到一个以NULL结尾的字符串数组的指针

printf("&var = %pn",&var);

* e 函数字传送递钦点参数envp,允许改造子进度的情状,无后缀e时,子进度使用当前前后相继的条件

}

*/

 

if( fork() == 0 )

exit(0);

{

}

printf("6------------execve-----------n");

 

if( execve( "/bin/ls", arg, NULL ) == 0)

运营结果如下:

{

bob体育平台 12

perror("execve error ");

   从地点能够看出,依照copy-on-write的思量,在子进程中,改动父进程的数额时,会先 复制父进度的多少修然后再改,进而到达子进度对数据的改变不影响父进度。不过我们开采,复制的内外,其值的地址没什么区别的。为何吧?子进度拷贝的时候也拷贝了父进度的虚构内部存款和储蓄器"页",这样他们的虚拟地址都如出风华正茂辙,可是对应区别的概况内存空间。

exit(1);

 

}

二、copy-on-write职业原理

}

 

return EXIT_SUCCESS;

    假使进度A创造子进程B,之后进度A和进度B分享A之处空间,同有的时候候该地址空间中的页面全部被标识为写爱慕。那时候B若写address的页面,由于写爱抚的原由会唤起写至极,在这里几个处理中,内核将address所在的丰富写敬服页面复制为新的页面,让B的address页表项指向该新的页面,新页面可写。而A的address页表项依旧指向十二分写爱抚的页面。然后当B在走访address时就能平昔访谈新的页面了,不会在拜望到哪个写体贴的页面。当A试图写address所在的页面时,由于写爱护的原故那个时候也会唤起特别,在十分管理中,内核假如发掘该页面只有八个具备进程,此种处境下也正是A,则向来对该页面裁撤写爱戴,自此当A再拜会address时不会在有写爱惜错误了。即使那个时候A更创办子进程C,则该address所在的页面又棉被服装置为写珍爱,拥有进度A和C,同时别的页面举个例子PAGEX依旧维持写珍重,只是有所进程A、B和C。如果那时A访谈PAGEX,则特别管理会创建一个新页面并将PAGEX中的内容复制到该页面,同偶尔候A相应 的pte指向该新页面。要是那时候C也拜谒PAGEX,也会复制新页面况兼让C对应的pte指向新页面。假使B再拜候PAGEX,则是因为那个时候PAGEX独有一个兼有进度B,故不再复制新页面,而是径直注销该页面包车型客车写敬性格很顽强在荆棘丛生或巨大压力面前不屈,由于B的pte本来正是直接指向该页面,所以不须求要在做其余专业。

}

 

三、exit和_exit

 

(1卡塔尔通常终止:

    (a卡塔尔(英语:State of Qatar)在main函数内实践return语句。那等效于调用exit。

    (b)调用exit函数

    (c)调用_exit系统调用函数

 

(2卡塔尔国极度终止:

    (a卡塔尔(قطر‎调用abort。它发出SIGABRT功率信号,所以是意气风发种十三分终止的大器晚成种特列。

    (b卡塔尔(英语:State of Qatar)当进度采取到有个别确定性信号时。比方,进度越出其地点空间访谈存款和储蓄单元,或者除以0,内核就能够为该进程发生相应的频限信号。

 

介怀:不管进度如何终止,最终都会执行内核中的同大器晚成段代码。这段代码为相应进度关闭全体张开描述符,释放它所运用的存款和储蓄器等。

 

 

exit和_exit的不同

bob体育平台 13

_exit(卡塔尔国函数的效果最为轻便:直接进度结束运作,肃清其选用的内部存款和储蓄器空间,并销毁其在根本中的各个数据构造;

 

exit()函数与_exit(卡塔尔函数最大的区分就在于exit(卡塔尔(英语:State of Qatar)函数在调用exit系统调用此前要检查文件的开荒状态,把文件缓冲区中的内容写回文件,正是"清理I/O"缓冲。

 

探究 1._exit()

 

//_exit(0)   exit(0)  return 0

bob体育平台 14

编译运行结果:

bob体育平台 15

从下面我们看见,test.txt的内容为空.为啥呢?因为标准I/O函数是带缓存的,进行fputs的时候是先向缓存中写的,独有当缓存满的时候才会刷新的缓冲区的。从上述大家开掘,当进度退出时,实行_exit(卡塔尔(قطر‎函数并从未刷新缓冲区的数量,而是直接终止进程的。

 

探究2.exit()

bob体育平台 16

编写翻译运转结果:
bob体育平台 17

从地点大家能够见见,当exit(卡塔尔(英语:State of Qatar)函数结束进度的时候,对缓存进行了拍卖,把缓存的数目写到了磁盘文件中。

 

探究3.return

 

由读者本身成功,其实return语句用在main函数中,和exit是相通的。不过我们知晓,return再次来到的值是给调用者的,它意味着着二个函数的截止。

 

四、exec函数族

 

exec.c  调用exec在那之中的三个函数; gcc exec.c -o exec; ./exec

exec函数族提供了风流浪漫种在进程中运转另一个程序实施的方法。它能够依靠钦定的文书名或目录名找到可试行文件,并用它来代替原调用经过的数据段、代码段、和宾馆段。在推行完之后,原调用经过的始末除了进程号外,别的一切都被交替了。

 

可执行文件既可以够是二进制文件,也足以是任何Linux下可实践的本子文件。

 

什么时候使用?

 

当进度感到自个儿无法再为系统和客户做任何进献了就能够调用exec函数族中的函数,让自个儿实行新的次第。

当前目录: 可实路程序A    B(1,2,3)     

即便有个别进度想同期奉行另一个前后相继,它就足以调用fork函数创设子进度,然后在子进程中调用任何贰个exec函数。那样看起来就象是通过实施应用程序而发出了三个新进程雷同。

 

execl("./B","B","1","2","3",NULL);

char *const envp[] = {"B","1","2","3",NULL}

 

execv("./B",envp);

bob体育平台 18

bob体育平台 19
bob体育平台 20

瞩目:不管file,第一个参数必需是可试行文件的名字

 

可执行文件查找方法

表中的前八个函数的追寻方法都以点名完整的文件目录路劲,而最终七个函数(以p结尾的函数卡塔尔国能够只交付文件名,系统会活动从情形变量"$PATH"所饱含的路子中展开搜索。

 

参数表传递格局

二种办法:一个叁个点数和将持有参数通过指针数组传递

生机勃勃函数名的第5个假名按来差异,字母"l"(list卡塔尔的意味三个三个点数格局;字母"v"(vector卡塔尔(英语:State of Qatar)的表示将具备参数布局成指针数组传递,其语法为char *const argv[]

 

情状变量的应用

exec函数族能够私下认可使用系统的情况变量,也足以流传内定的境况变量。这里,以"e"(Envirment卡塔尔结尾的多个函数execle、execve就足以在envp[]中传送当前经过所运用的景况变量。

 

运用的区分

可施行文件查找方法

参数表传递方式

情形变量的利用

bob体育平台 21

案例一execl

 

#include <stdio.h>

#include <unistd.h>

 

int main(int argc,char *argv[])

{

printf("start to execl.n");

if(execl("/bin/ls","ls",NULL) < 0)

{

perror("Fail to execl");

return -1;

}

printf("end of execl.n");

 

return 0;

}

 

运营结果如下:

bob体育平台 22

案例二、execlp

#include <stdio.h>

#include <unistd.h>

 

int main(int argc,char *argv[])

{

printf("start to execl.n");

if(execlp("ls","ls","-l",NULL) < 0)

{

perror("Fail to execl");

return -1;

}

printf("end of execl.n");

 

return 0;

版权声明:本文由bob体育app发布于操作系统,转载请注明出处:exec函数族