exec族函数详解及循环创建⼦进程
  前⾔:之前也知道exec族函数,但没有完全掌握,昨天⼜重新学习了⼀遍,基本完全掌握了,还有⼀些⽗⼦进程和循环创建⼦进程的问题,还要介绍⼀下环境变量,今天分享⼀下。
  ⼀、环境变量
  先介绍下环境的概念和特性,再举例⼦吧。
  环境变量,是指在中⽤来指定操作系统运⾏环境的⼀些参数。通常具备以下特征:
  ①字符串(本质) ②有统⼀的格式:名=值[:值] ③值⽤来描述进程环境信息。
  存储形式:与命令⾏参数类似。char *[]数组,数组名environ,内部存储字符串,NULL作为哨兵结尾。
  使⽤形式:与命令⾏参数类似。
  引⼊环境变量表:须声明环境变量。extern char ** environ;
  环境变量跟很多东西有关系,例如接下来的exec族函数,这也是为什么要先介绍下环境变量的原因,对理解exec族函数很有帮助;例如,Linux是什么样的系统?多⽤户多任务开源系统,每个⽤户的登录信息
环境变量都会记录。举例⼀下常⽤的环境变量:
PATH
  可执⾏⽂件的搜索路径。ls命令也是⼀个程序,执⾏它不需要提供完整的路径名/bin/ls,然⽽通常我们执⾏当前⽬录下的程序a.out却需要提供完整的路径名./a.out,这是因为PATH环境变量的值⾥⾯包含了ls命令所在的⽬录/bin,却不包含a.out所在的⽬录。PATH环境变量的值可以包含多个⽬录,⽤:号隔开。在Shell中⽤echo命令可以查看这个环境变量的值:
  $ echo $PATH
SHELL
  当前Shell,它的值通常是/bin/bash。
TERM
  当前终端类型,在图形界⾯终端下它的值通常是xterm,终端类型决定了⼀些程序的输出显⽰⽅式,⽐如图形界⾯终端可以显⽰汉字,⽽字符终端⼀般不⾏。
LANG
  语⾔和locale,决定了字符编码以及时间、货币等信息的显⽰格式。
HOME
  当前⽤户主⽬录的路径,很多程序需要在主⽬录下保存配置⽂件,使得每个⽤户在运⾏该程序时都有⾃⼰的⼀套配置
  介绍跟环境变量相关的函数:
  char *getenv(const char *name);  //获取环境变量
  int setenv(const char *name, const char *value, int overwrite);  //添加或改变环境变量
  int unsetenv(const char *name);  //删除
  ⼆、fork函数及循环创建⼦进程
  先说⼀个问题,学会fork并写程序时,可能都会遇到⼀个问题如下:
  ./a.out的输出跑到终端上了,想过为什么?接下来我会解释这个问题。
  1.fork函数
  原型如下:
  pid_t fork(void);
  很好理解创建⼀个⼦进程,但需要真正理解这个函数需要理解:执⾏⼀次返回两次,就是有两个返回值,如下:
  (1)返回⼦进程的pid
  (2)返回0
  2.getpid、getppid函数
  两个函数原型,如下:
  pid_t getpid(void);  //获取当前进程ID
  pid_t getppid(void);  //获取⽗进程ID
  3.fork创建⼦进程
  主要创建⼀个⼦进程,并打印当前进程和⽗进程ID,并且打下了当前⽗进程的⽗进程ID,想⼀下⽗进程的⽗进程ID会是多少呢?程序如下:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
pid_t pid;
pid = fork();
if (pid == -1 ) {
perror("fork");
exit(1);
} else if (pid > 0) {    //parent
//sleep(1); //保证⼦进程先执⾏
printf("I'm parent pid = %d, parentID = %d\n", getpid(), getppid());
} else if (pid == 0) {    //child
printf("child  pid = %d, parentID = %d\n", getpid(), getppid());
}
return0;
}
View Code
  程序很简单不再解释,但要说明⼏个问题,结果如下:
  看到结果知道了⽗进程也有⽗进程的ID,并查⼀下它,是bash其实就是shell,shell通过某种⽅式创⼦进程(就是我们程序中的⽗进程),然后⼦进程再创建⼦进程。
对了,还有⼀个问题,有⼀个sleep函数,注释是:确保⼦进程先执⾏,⽗⼦进程的执⾏顺序是由CPU的调度算法决定,但为啥我注释了sleep,还是⽗进程先执⾏。说点题外话吧,APUE(unix环境⾼级编程)的作者做过实验,98%的概率的都是⽗进程先执⾏。
  4.循环创建⼦进程
  接下来怎么创建多个⼦进程,直接给正确的程序吧,先演⽰⼀下有些问题的代码,如下:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int n = 5, i;                //默认创建5个⼦进程
if (argc == 2) {
n = atoi(argv[1]);
}
for (i = 0; i < n; i++)    //出⼝1,⽗进程专⽤出⼝
if (fork() == 0)
break;            //出⼝2,⼦进程出⼝,i不⾃增
if (n == i) {
//sleep(n);
字符串函数怎么获取printf("I am parent, pid = %d\n", getpid());
} else {
//sleep(i);
printf("I'm %dth child, pid = %d\n",
i+1, getpid());
}
return0;
}
View Code
  演⽰结果:
  会出现最开始的问题:输出跑到终端上。接下来解释为什么会出现这个问题?
  原因:shell、⽗进程和⼦进程都抢夺CPU,shell当⽗进程执⾏return 0,认为⽗进程执⾏完了,返回到终端,当⼦进程还没执⾏完,就输出到终端了。
  三、exec族函数 
  其实有七种以exec开头的函数,统称exec函数:
  int exec l(const char *path, const char *arg, ...);
  int exec lp(const char *file, const char *arg, ...);
  int exec le(const char *path, const char *arg,..., char * const envp[]);
  int exec v(const char *path, char *const argv[]);
  int exec vp(const char *file, char *const argv[]);
  int exec vpe(const char *file, char *const argv[],char *const envp[]);
  int execve(const char *path, char *const argv[], char *const envp[]);   //真的系统调⽤
  主要函数红⾊部分的说明:
  l (list)                          命令⾏参数列表
  p (path)                      搜素file时使⽤path变量
  v (vector)                    使⽤命令⾏参数数组
  e (environment)      使⽤环境变量数组,不使⽤进程原有的环境变量,设置新加载程序运⾏的环境变量
  1.execlp函数
  加载⼀个进程,借助PATH环境变量
  参数说明:
  file:⽂件名,通过PATH环境变量查
  arg:命令⾏参数,要强掉⼀下,第⼀个arg相当于arg[0],并要以NULL结尾
  ...:可变参数
  通过调⽤ls来举例:
  execlp("ls","ls","-l",NULL);
  其实,可以试⼀下第⼆个标红的参数,随便写也不会有错误的,说明内核并不使⽤第⼆个参数。
  程序⽰例如下:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("========================\n");
char *argvv[] = {"ls", "-l", "-F", "R", "-a", NULL};
pid_t pid = fork();
if (pid == 0) {
//execl("/bin/ls", "ls", "-l","-F", "-a", NULL);
//execv("/bin/ls", argvv);
execlp("ls","ls","-l","-F","-a",NULL);
perror("execlp");
exit(1);
} else if (pid > 0) {
sleep(1);
printf("parent\n");
}
return0;
}
View Code
  演⽰结果就不展⽰了,可以⾃⼰在终端⼿动输⼊命令,进⾏对照。
  2.execl函数
  加载⼀个进程,通过路径+程序名来加载。
  跟execlp的主要区别在于不是通过环境变量获取了,相对路径也是可以的。  上⾯程序注释部分有这个程序。
  3.execv函数
  int execv(const char *path, char *const argv[]);
  注意“v”使⽤命令⾏参数。
  上⾯程序注释部分有这个程序。
  就不⼀⼀举例了,有兴趣可以⾃⼰试⼀下。
  总结:有问题,欢迎及时评论、交流与学习。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。