【Linux系统编程】第二十四弹---从零到一:掌握进程替换的奥秘

个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】

目录

1、进程程序替换

1.1、替换原理

1.2、替换函数

1.3、函数解释

1.4、命名理解

1.5、代码演示 

1.5.1、execl调用举例

1.5.2、execv 和 execvp 调用举例 

1.5.3、execvpe调用举例 


1、进程程序替换

1.1、替换原理


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

1.2、替换函数


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

#include <unistd.h>
// C语言封装的库函数
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 *filename, char *const argv[],char *const envp[]);

1.3、函数解释

  • 这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回-1。
  • 所以exec函数只有出错的返回值而没有成功的返回值。

1.4、命名理解


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

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

1.5、代码演示 

1.5.1、execl调用举例

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

代码演示一

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

int main()
{
  printf("testexec ... begin!\n");
  execl("/usr/bin/ls","ls","-a","-l","--color",NULL);
  printf("testexec ...  end!\n");
  return 0;
}

运行结果

 代码演示二 

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

int main()
{
  printf("testexec ... begin!\n");
  execl("/usr/bin/lsss","ls","-a","-l","--color",NULL);
  printf("testexec ...  end!\n");
  return 0;
}

运行结果 

多进程版本

fork创建子进程,让子进程自己去替换。

创建子进程,让子进程完成任务:

  • 1、让子进程执行父进程代码的一部分
  • 2、让子进程执行一个全新的程序(替换)

代码演示一(替换系统命令)

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execl("/usr/bin/ls","ls","-a","-l",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

上面的程序替换,替换的都是系统命令,能否替换自己写的程序呢?

 答案是可以的。

补充:

C++文件后缀名的三种方式:.cpp  /  .cc  /  .cxx

代码演示二(替换自己程序)

testexec.c

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execl("./mypragma","mypragma",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

mypragma.cc

#include<iostream>
#include<unistd.h>

using namespace std;

int main()
{
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  return 0;
}

makefile 

此处是自己写的程序,也需要先编译生成可执行程序,下面代码可以使用make一次编译多(两)个文件。

.PHONY:all
all:testexec mypragma

testexec:testexec.c 
	gcc -o $@ $^ -std=c99
mypragma:mypragma.cc 
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -rf testexec mypragma 

运行结果 

上面的程序替换的是C++,能否替换其他语言呢?

答案是可以的,我们替换的是可执行程序,只要是程序即可。 

在替换之前,我们先简单使用其他语言写一个程序!!!

python

#!/usr/bin/python3

print("hello python")
print("hello python")
print("hello python")
print("hello python")

运行结果 

能否像以前一样使用./可执行程序运行程序呢?

 答案是可以的,但是需要加执行的权限。

shell 

#!/usr/bin/bash

cnt=0
while [ $cnt -le 10 ]
do
    echo "hello shell, cnt: ${cnt}"
    let cnt++
done

运行结果 

代码演示

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    //execl("./mypragma","mypragma",NULL);
    execl("/usr/bin/python3","phthon3","testpy",NULL);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果  

1.5.2、execv 和 execvp 调用举例 

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

 代码演示

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    char* const argv[]=
    {
     (char*)"ls",
     (char*)"-a",
     (char*)"-l",
     (char*)"--color",
      NULL
    };
    // execv("/usr/bin/ls",argv);
    execvp("ls",argv);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

1.5.3、execvpe调用举例 

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

代码演示:

mypragma.cc

#include<iostream>
#include<unistd.h>

using namespace std;

int main(int argc,char* argv[],char* env[])
{
  int i=0;
  for(;argv[i];i++)
  {
    printf("argv[%d] : %s\n",i,argv[i]);
  }
  printf("-------------------------------\n");
  for(i=0;env[i];i++)
  {
    printf("env[%d] : %s\n",i,env[i]);
  }

  printf("-------------------------------\n");
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  cout<<"hello C++,I am a C++ pragma!:"<<getpid()<<endl;
  return 0;
}

testexec.c(手写环境变量)

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    char* const argv[]=
    {
      (char*)"mypragma",
      (char*)"-a",
      (char*)"-b",
      NULL 
    };
    char* const envp[]=
    {
      (char*)"HAHA=111111",
      (char*)"HEHE=222222",
      NULL 
    };
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    execvpe("./mypragma",argv,envp);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

 testexec.c(系统环境变量+手写)

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

// 多进程版本
int main()
{
  printf("testexec ... begin!\n");
  pid_t id = fork();
  if(id == 0)
  {
    // child
    // 我的父进程本身就有一批环境变量,从bash来,自己想添加可以使用putenv
    putenv("HHHH=333333333");// 头文件 stdlib.h
    char* const argv[]=
    {
      (char*)"mypragma",
      (char*)"-a",
      (char*)"-b",
      NULL 
    };
    printf("I am child process,pid:%d\n",getpid());
    sleep(2);
    extern char** environ;
    execvpe("./mypragma",argv,environ);
  }
  
  // father
  int status = 0;
  pid_t rid = waitpid(id,&status,0);
  if(rid > 0)
  {
    printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
  }
  else 
  {
    printf("father wait failed!\n");
  }
  printf("testexec ... end!\n");
  return 0;
}

运行结果 

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

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

评论 143
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小林熬夜学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值