<Linux进程控制(2)>——《Linux》

目录

内容介绍:

进程内容回顾:

1.进程程序替换

1.1替换原理

1.2替换函数

1.2.1  int execl(const char *path, const char *arg, ...);函数演示

 1.2.2   int execv(const char *path, char *const argv[]);函数演示​

 1.2.3   int execlp(const char *file, const char *arg, ...);函数演示​

1.2.4  int execvp(const char *file, char *const argv[]);函数演示

 Makefile批量化生成可执行文件演示:

 使用自己写的程序调用其他语言的程序演示:​

1.2.5 int execle(const char *path, const char *arg,..., char * const envp[]);函数演示:

1.3函数解释

1.4命名理解

2.模拟实现shell:

2.1 问题分析:

2.2 模拟实现myshell演示:

2.2.1 Makefile批量化替换指令演示:​

后记:●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                       ——By 作者:新晓·故知


内容介绍:

学习进程替换原理及使用,通过c可执行程序调用cpp、python、shell、java等其他语言可执行程序模拟实现shell等!深入理解Linux操作系统进程替换机制,熟练掌握进程控制等。

进程内容回顾:

1.进程的写时拷贝回顾:

当我们创建一个子进程,子进程执行的是父进程的代码片段。父、子进程代码共享,if、else语句同时执行,通过同一个变量经过虚拟地址转化成物理地址,让父、子进程得到不同的值,从而判定让父、子进程执行不同的代码片段。

2.如何想让创建出来的子进程执行全新的程序呢?

在之前的学习中,我们知道是通过写时拷贝让子进程和父进程在数据上进行解耦,互相保证独立性,代码虽然不会写入,只进行读取。如果让父、子进程彻底分开就需要用到进程程序替换。

3.那么为什么要让子进程执行全新程序?

这是因为我们一般在服务器设计(例如Linux编程)的时候往往需要子进程完成两类事情:

(1)让子进程执行父进程的代码片段(服务器代码)

(2)让子进程执行磁盘中一个全新的程序(例如shell、让客户端执行对应的程序、通过我们自己的程序执行其他人写的不同语言的代码等)

接下来我们开始Linux进程程序替换机制的学习。

1.进程程序替换

1.1替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

进程执行过程图示:

进程写时拷贝图示:

程序替换:

这个过程并没有创建新的进程!且将磁盘中的数据加载到内存(一个硬件—>另一个硬件)是由操作系统完成,通过系统调用,而我们用户只是调用系统接口。

1.2替换函数

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

#include <unistd.h>`
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 execve(const char *path, char *const argv[], char *const envp[]);

如果想执行一个全新的程序(本质上就是磁盘上的文件),需要做哪几件事?
1.先找到程序在哪里?                                                       ---->程序在哪?
2.程序执行(可能会携带选项,也可能会不携带选项)     ---->怎么执行? 

 程序演示进程替换:

1.2.1  int execl(const char *path, const char *arg, ...);函数演示

1.创建文件演示exec的使用:

1.1进程替换执行 ls 指令:

#include<stdio.h>
#include<unistd.h>

int main()
{
  printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  execl("/usr/bin/ls","ls","-l","-a",NULL);
  printf("我执行完毕了,我的pid:%d\n",getpid());
  return 0;
}

 1.2  进程替换执行top指令:

#include<stdio.h>
#include<unistd.h>

int main()
{
  printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  //execl("/usr/bin/ls","ls","-l","-a",NULL);  //带选项
  execl("/usr/bin/top","top",NULL);            //不带选项
  printf("我执行完毕了,我的pid:%d\n",getpid());
  return 0;
}

 1.3 进程替换执行pwd 指令:

#include<stdio.h>
#include<unistd.h>

int main()
{
  printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  //execl("/usr/bin/ls","ls","-l","-a",NULL);    //带选项
  //execl("/usr/bin/top","top",NULL);            //不带选项
  execl("/usr/bin/pwd","pwd",NULL);              //不带选项
  printf("我执行完毕了,我的pid:%d\n",getpid());
  return 0;
}

 观察以上例子,我们发现只执行了替换前的printf语句,而另一个语句未执行!

execl进程替换分析:(下图中有单词打错了,修正:excel—>execl)

替换成功:

替换失败:

 替换演示:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>

int main()
{
  printf("我是父进程,我的pid:%d\n",getpid());
  pid_t id=fork();  //创建子进程
  if(id==0)
  {
    //子进程
    //这里我们想让子进程执行全新的程序,以前是执行父进程代码片段
    printf("我是子进程,我的pid: %d,我的ppid: %d\n",getpid(),getppid());
    execl("/usr/bin/ls","ls","-l","-a",NULL);    //带选项
    exit(1);  //只要执行了exit,意味着excel系列的替换函数失败了!
  }
  //父进程
  int status = 0;
  int ret = waitpid(id,&status,0);
  if(ret==id)
  {
    sleep(2);
    printf("父进程等待成功!\n");
  }



  //printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  //int ret= execl("/usr/bin/lsdf","ls","-l","-a",NULL);    //带选项
  //execl("/usr/bin/top","top",NULL);            //不带选项
  //execl("/usr/bin/pwd","pwd",NULL);              //不带选项
  //printf("我执行完毕了,我的pid:%d,ret: %d\n",getpid(),ret);
  return 0;
}

那么引入进程创建后,子进程执行程序替换,会不会影响父进程呢?

不会! 从以上演示看出:在子进程替换成功后,依旧打印了父进程中的“父进程等待成功!”,因为进程具有独立性!

那们这是如何做到的? 

在之前的父、子进程中我们提到会共享代码,在数据层面发生写时拷贝。而当程序替换的时候,我们可以理解为父、子进程的代码和数据都发生了写时拷贝完成了彻底的分离!

 1.2.2   int execv(const char *path, char *const argv[]);函数演示

execv函数演示:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>

int main()
{
  printf("我是父进程,我的pid:%d\n",getpid());
  pid_t id=fork();  //创建子进程
  if(id==0)
  {
    //子进程
    //这里我们想让子进程执行全新的程序,以前是执行父进程代码片段
   
    printf("我是子进程,我的pid: %d,我的ppid: %d\n",getpid(),getppid());
    char *const argv_[] = {(char*)"ls",(char*)"-a",(char*)"-l",(char*)"-i",NULL};
    execv("/usr/bin/ls",argv_);

    //execl("/usr/bin/ls","ls","-l","-a",NULL);    //带选项
    exit(1);  //只要执行了exit,意味着excel系列的替换函数失败了!
  }
  //父进程
  int status = 0;
  int ret = waitpid(id,&status,0);
  if(ret==id)
  {
    sleep(2);
    printf("父进程等待成功!\n");
  }



  //printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  //int ret= execl("/usr/bin/lsdf","ls","-l","-a",NULL);    //带选项
  //execl("/usr/bin/top","top",NULL);            //不带选项
  //execl("/usr/bin/pwd","pwd",NULL);              //不带选项
  //printf("我执行完毕了,我的pid:%d,ret: %d\n",getpid(),ret);
  return 0;
}

 1.2.3   int execlp(const char *file, const char *arg, ...);函数演示

execlp函数演示:

1.2.4  int execvp(const char *file, char *const argv[]);函数演示

execvp函数演示:

通过以上演示,目前我们演示的执行程序都是系统命令,如果我们要执行自己写的C/C++/python/java等程序呢?

如何能够执行其他语言写的程序?

这里我们先来复习以下makefile的知识:在之前的Makefile中,每次只能生成一个可执行程序,即使写了两个,也是默认生成第一个!那么如何生成两个及两个以上的可执行程序呢?

这里通过举个例子演示:

Makefile批量化生成可执行文件演示:

 

 

.PHONY:all
all:myexec mytest

myexec:myexec.c
		gcc -o $@ $^
mytest:mytest.cpp
		g++ -o $@ $^
.PHONY:clean
clean:
		rm -f myexec mytest 

 使用自己写的程序调用其他语言的程序演示:

C语言调用C++可执行程序:

采用绝对路径

 说明程序替换不挑程序,指令可以替换,自己的程序也可以替换!

 上图采用绝对路径,这里也可以使用相对路径查找目标程序所在位置:

C语言调用Python可执行程序:

#!/usr/bin/python3

print("hello,python!")
print("hello,python!")
print("hello,python!")
print("hello,python!")
print("hello,python!")

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>

int main()
{
  printf("我是父进程,我的pid:%d\n",getpid());
  pid_t id=fork();  //创建子进程
  if(id==0)
  {
    //子进程
    //这里我们想让子进程执行全新的程序,以前是执行父进程代码片段
   
    printf("我是子进程,我的pid: %d,我的ppid: %d\n",getpid(),getppid());

    execl("/usr/bin/python3","python3","test.py",NULL);
    //execl("./mytest","mytest",NULL);   //使用相对路径查找到目标程序所在位置
    //execl("/home/study/lesson/lesson17/mytest","mytest",NULL); //使用绝对路径查找到目标程序所在位置

    //char *const argv_[] = {(char*)"ls",(char*)"-a",(char*)"-l",(char*)"-i",NULL};
    //execvp("ls",argv_);
    //execlp("ls","ls","-a","-l",NULL);  //这里的两个ls,含义不一样!
    //execv("/usr/bin/ls",argv_);
    //execl("/usr/bin/ls","ls","-l","-a",NULL);    //带选项
    exit(1);  //只要执行了exit,意味着excel系列的替换函数失败了!
  }
  //父进程
  int status = 0;
  int ret = waitpid(id,&status,0);
  if(ret==id)
  {
    sleep(2);
    printf("父进程等待成功!\n");
  }



  //printf("我是一个进程,我的pid:%d\n",getpid());
  //ls -a -l
  //int ret= execl("/usr/bin/lsdf","ls","-l","-a",NULL);    //带选项
  //execl("/usr/bin/top","top",NULL);            //不带选项
  //execl("/usr/bin/pwd","pwd",NULL);              //不带选项
  //printf("我执行完毕了,我的pid:%d,ret: %d\n",getpid(),ret);
  return 0;
}

C语言调用shell演示:

#!/usr/bin/bash

cnt=0;

while [ $cnt -le 10 ]
do 
    echo "hello,shell"
    let cnt++
done

 

任何程序都可以用系统级接口,只要能系统调用,就可以用系统级接口调用其他语言!

1.2.5 int execle(const char *path, const char *arg,..., char * const envp[]);函数演示:

 

如果自定义环境变量呢?

#include<iostream>
#include<stdlib.h>
using namespace std;

int main()
{
  cout<<"PATH:"<<getenv("PATH")<<endl;
  cout<<"----------------------------------\n"<<endl;
  cout<<"MYPATH:"<<getenv("MYPATH")<<endl;
  cout<<"----------------------------------\n"<<endl;
  cout<<"hello,cpp!"<<endl;
  cout<<"hello,cpp!"<<endl;
  cout<<"hello,cpp!"<<endl;
  cout<<"hello,cpp!"<<endl;
  cout<<"hello,cpp!"<<endl;
  return 0;
}

 

 

 

 

1.3函数解释

这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。

如果调用出错则返回-1,所以exec函数只有出错的返回值而没有成功的返回值。

1.4命名理解

这些函数原型看起来很容易混,但只要掌握了规律就很好记。

  • l(list) : 表示参数采用列表
  • v(vector) : 参数用数组
  • p(path) : 有p自动搜索环境变量PATH
  • e(env) : 表示自己维护环境变量

#include <unistd.h>
int main()
{
 char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
 execl("/bin/ps", "ps", "-ef", NULL);
 // 带p的,可以使用环境变量PATH,无需写全路径
 execlp("ps", "ps", "-ef", NULL);
 // 带e的,需要自己组装环境变量
 execle("ps", "ps", "-ef", NULL, envp);
 execv("/bin/ps", argv);
 
 // 带p的,可以使用环境变量PATH,无需写全路径
 execvp("ps", argv);
 // 带e的,需要自己组装环境变量
 execve("/bin/ps", argv, envp);
 exit(0);
}

事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在 man手册第3节。这些函数之间的关系如下图所示。

下图exec函数族 一个完整的例子:

2.模拟实现shell:

2.1 问题分析:

我们可以综合前面的知识,做一个简易的shell

考虑下面这个与shell典型的互动:

[root@localhost epoll]# ls
client.cpp readme.md server.cpp utility.h
[root@localhost epoll]# ps
 PID TTY TIME CMD
 3451 pts/0 00:00:00 bash
 3514 pts/0 00:00:00 ps

用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。

然后shell读取新的一行输入,建立一个新的进程,在这个进程中运行程序 并等待这个进程结束。

所以要写一个shell,需要循环以下过程:

1. 获取命令行

2. 解析命令行

3. 建立一个子进程(fork)

4. 替换子进程(execvp)

5. 父进程等待子进程退出(wait)

根据这些思路,和我们前面的学的技术,就可以自己来实现一个shell了。

实现代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#define MAX_CMD 1024
char command[MAX_CMD];
int do_face()
{
   memset(command, 0x00, MAX_CMD);
   printf("minishell$ ");
   fflush(stdout);
   if (scanf("%[^\n]%*c", command) == 0) {
       getchar();
       return -1; 
   }   
   return 0;
}
char **do_parse(char *buff)
{
   int argc = 0;
   static char *argv[32];
   char *ptr = buff;
   while(*ptr != '\0') {
       if (!isspace(*ptr)) {
           argv[argc++] = ptr;
           while((!isspace(*ptr)) && (*ptr) != '\0') {
               ptr++;
           }
           continue;
       }
       *ptr = '\0';
       ptr++;
   }
   argv[argc] = NULL;
   return argv;
}
int do_exec(char *buff)
{
   char **argv = {NULL};
   int pid = fork();
   if (pid == 0) {
       argv = do_parse(buff);
       if (argv[0] == NULL)
 {
  exit(-1);
       }
       execvp(argv[0], argv);
   }else {
       waitpid(pid, NULL, 0);
   }
   return 0;
}
int main(int argc, char *argv[])
{
   while(1) {
       if (do_face() < 0)
           continue;
       do_exec(command);
   }
   return 0;
}

在继续学习新知识前,我们来思考函数和进程之间的相似性

exec/exit就像call/return

一个C程序有很多函数组成。一个函数可以调用另外一个函数,同时传递给它一些参数。被调用的函数执行一定的操作,然后返回一个值。每个函数都有他的局部变量,不同的函数通过call/return系统进行通信。

这种通过参数和返回值在拥有私有数据的函数间通信的模式是结构化程序设计的基础。Linux鼓励将这种应用于程序之内的模式扩展到程序之间。如下图

一个C程序可以fork/exec另一个程序,并传给它一些参数。这个被调用的程序执行一定的操作,然后通过exit(n)来返回值。调用它的进程可以通过wait(&ret)来获取exit的返回值。

2.2 模拟实现myshell演示:

2.2.1 Makefile批量化替换指令演示:

在登录Linux后,我们发现会有一个命令行提示:

 这里的属性是系统调用接口,可以网上搜索“Linux获取用户名的系统调用接口”。

那么接下来我们编写程序模拟实现这个shell功能:

 完整代码:

/*
 *项目名称:模拟实现操作界面(即用户名 主机名 ...)待输入命令的shell脚本
 *项目作者:新晓·故知
 *时间:2022.10.01—2022.10.02
 * 
 * */

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>


#define NUM 1024
#define SIZE 128
#define SEP " "            //设置分隔符

char command_line[NUM];    //保存输入的字符串
char *command_args[SIZE];  //指针数组,将字符串切割成子串,将子串的首地址放到数组

int main()
{
  //shell 操作界面等待的命令待显示,本质上就是一个死循环(先打印,再等待输入)
  while(1)
  {
    //用户名,主机名这些属性等其实是系统调用接口,这里我们先不关心获取这些属性的接口
    //如果有时间深入研究,可以在网上搜素“Linux获取用户名的系统调用接口”
    
    //1.显示提示符
    printf("[新晓故知@我的主机名 当前目录]# ");
    fflush(stdout); //刷新缓冲区
    
    //2.获取用户输入
    memset(command_line,'\0',sizeof(command_line)*sizeof(char));
    fgets(command_line,NUM,stdin);  //键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
    command_line[strlen(command_line)-1]='\0';  //清空\n (输入指令(字符串等)结束后,敲击回车键,而回车也是字符,但不可显)
   
    //3. "ls -a -l -i" —> "ls","-a","-l","-i"字符串切分
    command_args[0] = strtok(command_line,SEP);   //strtok切分字符串
    int index = 1;
    // = 虽然报错,但是故意这么写的
    // strtok 1.截取成功,返回字符串起始地址 2.截取失败,返回NULL
    //
    while(command_args[index++] = strtok(NULL,SEP));

    // //for debug  仅用于测试是否截取成功
    // for(int i=0;i<index;i++)
    // {
    //   printf("%d : %s\n",i,command_args[i]);
    // }
    
    //4.TODO
    //这里不直接进行替换的原因是:一旦直接替换,那整个shell就会替换,原有的配置被更改
    //交给子进程,子进程能够看到
   
    //5.创建进程,执行
    pid_t id=fork();
    if(id == 0)
    {
      //子进程
      //6.程序替换
      execvp(command_args[0],command_args); //comman_args[0]就是我们要保存的名字
      exit(1);   //如果执行到这里,子进程一定替换失败,因为一旦替换成功,子进程就会执行新的程序去了
    }
    int status = 0;
    pid_t ret=waitpid(id,&status,0);
    if(ret<0)
    {
      printf("等待子进程成功:signal: %d, return code: %d\n",status&0x7F,(status>>8)&0xFF);
    }
  }
  //end while

  return 0;
}

 理解内建命令,在学习环境变量的时候,还有一些命令并没有fork()创建子进程,其实是shell内部的命令! 

创建子进程去执行命令,保证替换时不影响父进程,从而让父进程shell一直进行命令行解析。所以联想到我们在登录Linux时的提示符,其实是系统的shell程序在运行!     

完善shell的功能:

 

如果直接exec*执行cd,最多只是让子进程进行路径切换,子进程是一运行就完毕的进程!那么我们在shell中,更希望是谁的路径发生变化呢?答案是父进程!(shell本身!) 

但是这在我们模拟shell的目前代码下不能完成这个父进程变化!因为我们所有的代码在进行操作的时候本质上都会落实到fork(),然后exec,也就是说不管什么命令都是创建子进程!

那么如果有些行为是必须让父进程shell执行的,不想让子进程执行,那么就绝对不能创建子进程,因此则需要父进程自己实现对应的代码!这部分由shell自己执行的命令,我们称之为内建(或内置bind-in)命令。

这部分内建命令本质上就相当于shell内部的一个内置函数!

这里我们去封装这个函数!

先来认识一个函数chdir:

 

 

/*
 *项目名称:模拟实现操作界面(即用户名 主机名 ...)待输入命令的shell脚本
 *项目作者:新晓?故知
 *时间:2022.10.01?2022.10.02
 * 
 * */

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>


#define NUM 1024
#define SIZE 128
#define SEP " "            //设置分隔符

char command_line[NUM];    //保存输入的字符串
char *command_args[SIZE];  //指针数组,将字符串切割成子串,将子串的首地址放到数组

//对应的shell内建命令
int ChangeDir(const char* new_path)
{
  chdir(new_path);
  return 0;     //调用成功
}

int main()
{
  //shell 操作界面等待的命令待显示,本质上就是一个死循环(先打印,再等待输入)
  while(1)
  {
    //用户名,主机名这些属性等其实是系统调用接口,这里我们先不关心获取这些属性的接口
    //如果有时间深入研究,可以在网上搜素“Linux获取用户名的系统调用接口”
    
    //1.显示提示符
    printf("[新晓故知@我的主机名 当前目录]# ");
    fflush(stdout); //刷新缓冲区
    
    //2.获取用户输入
    memset(command_line,'\0',sizeof(command_line)*sizeof(char));
    fgets(command_line,NUM,stdin);  //键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
    command_line[strlen(command_line)-1]='\0';  //清空\n (输入指令(字符串等)结束后,敲击回车键,而回车也是字符,但不可显)
   
    //3. "ls -a -l -i" ?> "ls","-a","-l","-i"字符串切分
    command_args[0] = strtok(command_line,SEP);   //strtok切分字符串
    int index = 1;
    //给ls命令添加颜色
    if(strcmp(command_args[0],"ls") == 0)
      command_args[index++] = (char*)"--color=auto";

    // = 虽然报错,但是故意这么写的
    // strtok 1.截取成功,返回字符串起始地址 2.截取失败,返回NULL
    //
    while(command_args[index++] = strtok(NULL,SEP));

    // //for debug  仅用于测试是否截取成功
    // for(int i=0;i<index;i++)
    // {
    //   printf("%d : %s\n",i,command_args[i]);
    // }
    
    //4.
    //这里不直接进行替换的原因是:一旦直接替换,那整个shell就会替换,原有的配置被更改
    //交给子进程,子进程能够看到
    if(strcmp(command_args[0],"cd")==0 && command_args[1] != NULL)
    {
      ChangeDir(command_args[1]);  //让调用方进行路径切换!(父进程的路径切换)
      continue;
    }


    //5.创建进程,执行
    pid_t id=fork();
    if(id == 0)
    {
      //子进程
      //6.程序替换
      execvp(command_args[0],command_args); //comman_args[0]就是我们要保存的名字
      exit(1);   //如果执行到这里,子进程一定替换失败,因为一旦替换成功,子进程就会执行新的程序去了
    }
    int status = 0;
    pid_t ret=waitpid(id,&status,0);
    if(ret<0)
    {
      printf("等待子进程成功:signal: %d, return code: %d\n",status&0x7F,(status>>8)&0xFF);
    }
  }
  //end while

  return 0;
}

 常见的内建命令:echo、export等

环境变量:

这个我们在之前的学习中有了解过,可以复习之前的三种环境变量获取方式!我们在这里使用一种获取:

这里我们写一个函数获取env:

#include<stdio.h>
int main()
{

  extern char** environ;
  for(int i = 0; environ[i] != NULL; i++)
    printf("[%d]: %s\n",i,environ[i]);
  return 0;
}

 在执行  ./myenv时,我的父进程是bash,所以我的环境变量来自于bash。

 当在执行模拟的myshell时,myshell的父进程是系统的myshell,所以继承环境变量就继承myshell。当内部在which env (或env显示)这样的操作时,实际上也是自己模拟的myshell的子进程!我们采用的是execvp的程序替换,(没有采用带e的程序替换)这样的程序替换会默认将环境变量传递给子进程!(这里要注意exec系列函数,如果不手动传递环境变量,将有默认操作!)这也正契合了环境变量具有全局属性。

我们使用不带e的exec系列函数,那么所有环境变量也是会被继承的。

如果将来传一个环境变量,自己定义一个环境变量的指针数组,传进去我们发现会覆盖我们的环境变量列表,但是我们现在向新增,如何做到?

由于系统不允许我们对环境变量的那个指针数组做任何修改 !那如何自定义导入一个环境变量,让系统执行我们所导入的环境变量呢?

 

 可以通过putenv将当前的环境变量导入到自己的上下文当中!

 环境变量的数据,在进程的上下文中:

(1)环境变量会被子进程继承下去,所以它会有全局属性

(2)当我们进行程序替换的时候,当前进程的环境变量非但不会被替换,而且是继承父进程的!

这个演示不要轻易演示!!!作者云服务器已挂掉!

 

注意:以上的程序不要轻易模拟实现!!!如果服务器挂掉就重启! 

后记:
●由于作者水平有限,文章难免存在谬误之处,敬请读者斧正,俚语成篇,恳望指教!

                                                                       ——By 作者:新晓·故知

  • 19
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 32
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 32
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值