前言:之前也知道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 == - ) {
perror("fork");
exit();
} else if (pid > ) { //parent
//sleep(1); //保证子进程先执行
printf("I'm parent pid = %d, parentID = %d\n", getpid(), getppid());
} else if (pid == ) { //child
printf("child pid = %d, parentID = %d\n", getpid(), getppid());
} return ;
}

  程序很简单不再解释,但要说明几个问题,结果如下:

  

  看到结果知道了父进程也有父进程的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 = , i; //默认创建5个子进程 if (argc == ) {
n = atoi(argv[]);
} for (i = ; i < n; i++) //出口1,父进程专用出口
if (fork() == )
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+, getpid());
} return ;
}

  演示结果:

  

  会出现最开始的问题:输出跑到终端上。接下来解释为什么会出现这个问题?

  原因:shell、父进程和子进程都抢夺CPU,shell当父进程执行return 0,认为父进程执行完了,返回到终端,当子进程还没执行完,就输出到终端了。

  三、exec族函数 

  其实有七种以exec开头的函数,统称exec函数:

  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[]);

  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 == ) {
//execl("/bin/ls", "ls", "-l","-F", "-a", NULL);
//execv("/bin/ls", argvv);
execlp("ls","ls","-l","-F","-a",NULL);
perror("execlp");
exit(); } else if (pid > ) {
sleep();
printf("parent\n");
} return ;
}

  演示结果就不展示了,可以自己在终端手动输入命令,进行对照。

  2.execl函数

  加载一个进程, 通过 路径+程序名 来加载。

  跟execlp的主要区别在于不是通过环境变量获取了,相对路径也是可以的。

  上面程序注释部分有这个程序。

  3.execv函数

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

  注意“v”使用命令行参数。

  上面程序注释部分有这个程序。

  就不一一举例了,有兴趣可以自己试一下。

  总结:有问题,欢迎及时评论、交流与学习。

  

  

  

  

  

  

exec族函数详解及循环创建子进程的更多相关文章

  1. 【Linux 进程】exec族函数详解

    exec族的组成: 在Linux中,并不存在一个exec()的函数形式,exec指的是一组函数,一共有6个,分别是: #include <unistd.h> extern char **e ...

  2. exce族函数详解

    exec函数族 函数族说明 fork() 函数用于创建一个新的子进程,该子进程几乎复制了父进程的全部内容,但是,这个新创建的子进程如何执行呢?exec 函数族就提供了一个在进程中启动另一个程序执行的方 ...

  3. exec系列函数详解

    execve替换进程映像(加载程序):execve系统调用,意味着代码段.数据段.堆栈段和PCB全部被替换.在UNIX中采用一种独特的方法,它将进程创建与加载一个新进程映像分离.这样的好处是有更多的余 ...

  4. js字符串和正则表达式中的match、replace、exec等函数详解

    正则并不是经常使用,而正则和字符串之间的函数关系又错综复杂,谁是谁的函数,又是怎么样的一种结果,往往我们是看一遍忘一遍,对此我是头疼不已,感觉自己是个笨蛋^_^. 为了以后不再查文档,特此把常用的函数 ...

  5. python3 内置函数详解

    内置函数详解 abs(x) 返回数字的绝对值,参数可以是整数或浮点数,如果参数是复数,则返回其大小. # 如果参数是复数,则返回其大小. >>> abs(-25) 25 >&g ...

  6. fork()函数详解

    linux中fork()函数详解(原创!!实例讲解) (转载)    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程 ...

  7. Python内置函数详解

    置顶   内置函数详解 https://docs.python.org/3/library/functions.html?highlight=built#ascii https://docs.pyth ...

  8. 【Linux 进程】fork函数详解

    一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同, ...

  9. (转)fock函数详解

    转自:http://www.cnblogs.com/bastard/archive/2012/08/31/2664896.html linux中fork()函数详解  一.fork入门知识 一个进程, ...

随机推荐

  1. 启动nginx报错问题

    为了解决生产环境的bug,模拟生产环境,我使用了nginx,在安装启动的过程中,出现了很多问题. 1.nginx下载地址 http://nginx.org/en/download.html 这是ngi ...

  2. java自动化-数据驱动junit演示,下篇

    本文旨在帮助读者介绍,如何使用excle实现数据驱动 本文是上文https://www.cnblogs.com/xuezhezlr/p/9096063.html的继续,如果没看上文建议自己看一下,对理 ...

  3. Numpy 基础运算1

    # -*- encoding:utf-8 -*- # Copyright (c) 2015 Shiye Inc. # All rights reserved. # # Author: ldq < ...

  4. WebBrowser加载一个URL被多次调用DocumentCompleted 的问题解决方案<转>

    关于DocumentCompleted事件,MSDN给出的解释是在文档加载完毕后执行,但是在我的程序中DocumentCompleted却被多次调用,查了一下资料,大概出现了以下几种情况. 1.Web ...

  5. spring-cloud-Zuul学习(一)【基础篇】--入门案例【重新定义spring cloud实践】

                                                                                                    -- 2 ...

  6. 4.21Linux(2)

    2019-4-21 22:46:55 今天买了阿里云服务器1年的 116大洋!!! 但是有个服务器感觉很爽!!!!Linux系统还是很有意思的!!!! 直接贴上笔记! 越努力,越幸运!永远不要高估自己 ...

  7. c++编译错误C2971:"std::array":array_size:包含非静态存储不能用作废类型参数;参见“std::array”的声明

    在Qt5中这段代码编写有两种方式:一个编译成功,一个失败 成功版本: static constexpr size_t block_size = 0x2000;//8KB static constexp ...

  8. 搭积木(java)-蓝桥杯

    搭积木小明最近喜欢搭数字积木,一共有10块积木,每个积木上有一个数字,0~9.搭积木规则:每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小.最后搭成4层的金字塔形,必须用完所有的积木.下 ...

  9. python基础之函数式编程

    一.定义: 函数作为参数作用:将核心逻辑传入方法体,使该方法的适用性更广,体现了面向对象的开闭原则: 函数作为返回值作用:逻辑连续,当内部函数被调用时,不脱离当前的逻辑. 二.高阶函数: 1.定义:将 ...

  10. Anveshak: Placing Edge Servers In The Wild

    Anveshak:在野外放置边缘服务器 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 笔者翻译了该论文.由于时间仓促 ...