【Linux进程控制】进程程序替换

news/2024/9/20 6:05:04 标签: linux

目录

进程程序替换

替换函数

看现象

替换原理

多进程替换

exec*函数使用(部分),并且认识函数参数的含义

1.execl

2.execv

3.execvp

4.execvpe

execlp 和execlpe

替换函数总结


进程程序替换

替换函数

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

EXEC(3)                  Linux Programmer's Manual                 EXEC(3)

NAME
       execl, execlp, execle, execv, execvp, execvpe - execute a file

SYNOPSIS
       #include <unistd.h>

       extern char **environ;

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

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       execvpe(): _GNU_SOURCE

看现象

测试代码:

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

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

  printf("testexec ... end!\n");
  return 0;
}

结果:

[wuxu@Nanyi lesson17]$ ./test
testexec ... begin!
total 56
drwxrwxr-x  2 wuxu wuxu 4096 Aug 25 15:18 .
drwx------ 11 wuxu wuxu 4096 Aug 24 19:49 ..
-rw-rw-r--  1 wuxu wuxu    1 Aug 25 15:14 myprocess
-rw-rw-r--  1 wuxu wuxu  182 Aug 25 15:18 myprocess.c
-rw-rw-r--  1 wuxu wuxu 1809 Aug 24 21:34 task.c
-rwxrwxr-x  1 wuxu wuxu 8416 Aug 25 15:18 test
-rw-rw-r--  1 wuxu wuxu  366 Aug 24 20:02 test1.c
-rw-rw-r--  1 wuxu wuxu  934 Aug 24 20:16 test2.c
-rw-rw-r--  1 wuxu wuxu  501 Aug 24 20:33 wait1.c
-rw-rw-r--  1 wuxu wuxu  583 Aug 24 20:56 wait2.c
-rw-rw-r--  1 wuxu wuxu  469 Aug 24 20:58 wait3.c
-rw-rw-r--  1 wuxu wuxu 1407 Aug 24 21:24 wait4.c

通过观察我们发现:

◉ 第一个printf执行了

◉ ls命令被执行了

◉ 最后一个printf没有被执行

说明程序在execlls替换了,替换也是完完全全的,并不会执行后面的代码

替换原理

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

站在被替换进程的角度:本质就是这个程序被加载到内存了;如何加载?exec*类似于一种Linux上的加载函数

多进程替换

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

创建子进程目的是让子进程完成任务:1️⃣ 让子进程执行父进程代码的一部分 2️⃣ 让子进程执行一个全新的程序

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

int main(){
  printf("testexec ... begin!\n");

  pid_t id = fork();
  if(id == 0){
    printf("child pid: %d\n", getpid());
    sleep(2);
    execl("/usr/bin/ls","ls","-a","-l",NULL);
    exit(1);
  }
    // fahter
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
  printf("testexec ... end!\n");
  return 0;
}

 

[wuxu@Nanyi lesson17]$ gcc -o test myprocess.c -std=c99
[wuxu@Nanyi lesson17]$ ./test
testexec ... begin!
child pid: 8233
total 56
drwxrwxr-x  2 wuxu wuxu 4096 Aug 25 16:29 .
drwx------ 11 wuxu wuxu 4096 Aug 24 19:49 ..
-rw-rw-r--  1 wuxu wuxu    1 Aug 25 15:14 myprocess
-rw-rw-r--  1 wuxu wuxu  540 Aug 25 16:29 myprocess.c
-rw-rw-r--  1 wuxu wuxu 1809 Aug 24 21:34 task.c
-rwxrwxr-x  1 wuxu wuxu 8720 Aug 25 16:29 test
-rw-rw-r--  1 wuxu wuxu  366 Aug 24 20:02 test1.c
-rw-rw-r--  1 wuxu wuxu  934 Aug 24 20:16 test2.c
-rw-rw-r--  1 wuxu wuxu  501 Aug 24 20:33 wait1.c
-rw-rw-r--  1 wuxu wuxu  583 Aug 24 20:56 wait2.c
-rw-rw-r--  1 wuxu wuxu  469 Aug 24 20:58 wait3.c
-rw-rw-r--  1 wuxu wuxu 1407 Aug 24 21:24 wait4.c
father wait success, child exit code: 0
testexec ... end!

原理如图:即便是父子,也要保证独立性

exec*函数使用(部分),并且认识函数参数的含义

1.execl

函数原型,在前面我们已经使用过了,这里不过多介绍。关于exec*函数,我们不考虑它的返回值

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

这里的 l 可以理解为list,path传入绝对路径,arg可变参数,依次传入命令,以及你想执行的指令,最后一个必须为NULL

2.execv

函数原型:

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

这里的 v 可以理解为vector,argv是一个指针数组,我们直接来使用

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

int main()
{
    printf("testexec ... begin!\n");
    pid_t id = fork();
    if(id == 0)
    {
        printf("child pid: %d\n", getpid());
        sleep(2);
        char *const argv[] = 
        {
            (char*)"ls",
            (char*)"-l",
            (char*)"-a",
            (char*)"--color",
            NULL
        };
        execv("/usr/bin/ls", argv);
        exit(1);
    }

    // fahter
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");
    return 0;
}

3.execvp

函数原型:

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

用户可以不传要执行的文件的路径(但是文件名要传),直接告诉exec*,我要执行谁都行

p:查找这个程序,系统会自动在环境变量PATH中进行查找


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

int main()
{
    printf("testexec ... begin!\n");
    pid_t id = fork();
    if(id == 0)
    {
        printf("child pid: %d\n", getpid());
        sleep(2);
        char *const argv[] = 
        {
            (char*)"ls",
            (char*)"-a",    
            (char*)"--color",
            NULL
        };
        execvp("ls",argv);
        exit(1);
    }

    // fahter
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");
    return 0;
}

4.execvpe

函数原型:

 int execvpe(const char *file, char *const argv[],char *const envp[]);
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

int main()
{
    printf("testexec ... begin!\n");
    pid_t id = fork();
    if(id == 0)
    {
      printf("child pid: %d\n", getpid());
        sleep(2);
        char *const argv[] = 
        {
            (char*)"ls",
            (char*)"-a",    
            (char*)"--color",
            NULL
        };

        extern char** environ;
        execvpe("ls",argv,environ);
        exit(1);
    }

    // fahter
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        printf("father wait success, child exit code: %d\n", WEXITSTATUS(status));
    }
    printf("testexec ... end!\n");
    return 0;
}

execlp 和execlpe

方法与上面相同,就不再一一介绍了

替换函数总结

函数名参数格式PATH中可执行程序是否需要带绝对路径是否使用当前环境变量
execl列表
execlp列表不是
execle列表不是,需自己组装环境变量
execv数组
execvp数组不是
execvpe数组不是不是,需自己组装环境变量
execve数组不是,需自己组装环境变量

上面各个接口统称为加载器,它们为即将替换进来的可执行程序加载入参数列表、环境变量等信息。下面我们使用execvpe接口给自定义可执行程序传入命令行参数及环境变量,该可执行程序将会把命令行参数及环境变量打印至显示器

myprogma.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 , argv[i]);
  }
    printf("-------------------------\n");

    cout << "hello C++, I am a C++ program! : " << getpid() << endl;
    cout << "hello C++, I am a C++ program! : " << getpid() << endl;
    cout << "hello C++, I am a C++ program! : " << getpid() << endl;
    cout << "hello C++, I am a C++ program! : " << getpid() << endl;

    return 0;
}

 testfin.c:

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

int main(){
  printf("testexec ... begin! \n");
  pid_t id = fork();

  if(id == 0){
    putenv("HHHH=111111111111111111");
       // 我的父进程本身就有一批环境变量!!!, 从bash来
    char *const argv[] = 
    {
      (char*)"mypragma",
      (char*)"-a",
      (char*)"-b",

      NULL
     };

    extern char** environ;
    printf("child pid: %d \n",getpid());
    sleep(2);
   // execvpe("./myprogma",argv,environ);
    execl("/usr/bin/python3","python3","test.py",NULL);
    
    exit(1);

  }


  //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));
  }

  printf("testexec ... end!\n");
  return 0;
}

test.py

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

test.sh:

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

编译运行:

其他几个例子也就不一一测试了

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

 


http://www.niftyadmin.cn/n/5666693.html

相关文章

鸿蒙OpenHarmony【轻量系统芯片移植案例】标准系统方案之瑞芯微RK3566移植案例

标准系统方案之瑞芯微RK3566移植案例 本文章是基于瑞芯微RK3566芯片的khdvk_3566b开发板&#xff0c;进行标准系统相关功能的移植&#xff0c;主要包括产品配置添加&#xff0c;内核启动、升级&#xff0c;音频ADM化&#xff0c;Camera&#xff0c;TP&#xff0c;LCD&#xff…

Netmiko:简化网络设备自动化的强大工具

在当今快速发展的网络环境中,自动化已成为网络工程师的必备技能。Netmiko作为一个强大的Python库,为网络设备自动化提供了简单而有效的解决方案。本文将深入探讨Netmiko的特性、使用方法及其在网络自动化中的应用。 什么是Netmiko? Netmiko是一个基于Python的多厂商网络设备…

POI操作EXCEL插入图片

文章目录 POI操作EXCEL插入图片 POI操作EXCEL插入图片 依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version> </dependency> <dependency><groupId>org.a…

opencv编译报错 libavcodec.a(hevc cabac.o)

1、opencv编译报错&#xff1a;libavcodec.a(hevc cabac.o): relocation R X86 64 PC32 against ff w ff can not be used when makin with -fPlC 需要将ffmpeg在配置的时候./configure --enable-nonfree --enable-pic --enable-shared --disable-x86asm&#xff0c;然后安装ff…

SalescustomerController

目录 1、 SalescustomerController 1.1、 Sales 1.2、 /// 销售客户单号 1.3、 /// 查询客户区域 using QXQPS.Models; using QXQPS.Vo; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; namespac…

前端开发之原型模式

介绍 原型模式本质就是借用一个已有的实例做原型&#xff0c;在这原型基础上快速复制出一个和原型一样的一个对象。 class CloneDemo {name clone democlone(): CloneDemo {return new CloneDemo()} } 原型原型链 函数&#xff08;class&#xff09;都有显示原型 prototyp…

【鸿蒙】HarmonyOS NEXT开发快速入门教程之ArkTS语法装饰器(上)

文章目录 前言一、ArkTS基本介绍1、 ArkTS组成2、组件参数和属性2.1、区分参数和属性的含义2.2、父子组件嵌套 二、装饰器语法1.State2.Prop3.Link4.Watch5.Provide和Consume6.Observed和ObjectLink代码示例&#xff1a;示例1&#xff1a;&#xff08;不使用Observed和ObjectLi…

GSM安全特性

GSM安全特性&#xff0c;具体内容如下&#xff1a; 1. 安全特性&#xff1a; GSM的安全方面在3GPP TS 42.009 [4]和3GPP TS 43.020 [11]的规范参考文献中有描述。本条款提供了SIM卡支持的安全特性信息&#xff0c;以实现以下功能&#xff1a; 对网络进行用户身份认证&#x…