首页 > 系统相关 >Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

时间:2024-11-30 23:30:32浏览次数:10  
标签:status exec int char ls Linux 进程 include

上篇文章:Linux操作系统2-进程控制2(进程等待,waitpid系统调用,阻塞与非阻塞等待)-CSDN博客

本篇代码Gitee仓库:Linux操作系统-进程的程序替换学习 · d0f7bb4 · 橘子真甜/linux学习 - Gitee.com

本篇重点:进程替换

目录

一. 什么是进程替换?

二. 进程替换函数常用的函数 

2.1 execl 

a 进程替换覆盖指定位置后面的代码

b 进程替换不会影响父进程 

2.2 execlp 

 2.3 execv/execvp

2.4 execle 

 2.5 execve 系统调用

 三. 进程替换总结

四. 进程替换可以执行任何后端语言!


一. 什么是进程替换?

        我们知道,使用fork函数可以创建子进程。我们创建子进程的目的是什么?

1 让子进程执行父进程的一部分代码(比如执行父进程处于磁盘中的代码)

2 我们希望让子进程执行一个全新的进程,去完成一个不同的功能

        我们让子进程去执行新程序的数据和代码 -> 进程的程序替换

二. 进程替换函数常用的函数 

常见的函数如下:

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

是不是有点眼花缭乱?具体使用如下

2.1 execl 

a 进程替换覆盖指定位置后面的代码

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

//path    用于找到程序(程序的路径,ls的路径是 /usr/bin/ls)
//arg     这个命令是怎么执行的(比如ls命令的执行方式就是单独的 ls)
//...     这个命令需要的参数是什么(比如ls可以带参数 -a -l -i 等)

//返回值,失败返回-1,并且设置错误码

我们使用execl去替换一个 ls命令。代码如下:

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

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("process is running!\n");

    //第一个参数,要执行哪个路径,就填这个路径
    //第二个参数,程序怎么执行,就怎么填
    //后面的参数,执行这个程序需要带的参数,就一个一个地填这些参数
    //最好使用NULL结尾
    int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
    
    //使用系统调用或者涉及底层的C库函数,需要判断错误返回
    perror("execl");


    //由于执行execl之后,代码被覆盖,以下的代码不会被运行!
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    printf("process is running!\n");
    return 0;
}

编译运行结果如下:

        execl之后的代码没有被执行的原因是:进程替换的时候没有创建新进程,而是将指定的程序和代码加载到指定的位置

        这样会覆盖后面的代码,所以后面printf就不会执行了!

b 进程替换不会影响父进程 

        进程替换会覆盖指定位置后面的代码,不过我们在子进程中使用进程替换不会影响父进程的代码和数据。

       这个原因是父子进程之间有写实拷贝,由于父子进程之间的写实拷贝,一旦子进程尝试写入,OS就会给子进程开辟一份空间用于保存子进程的数据和代码。这样一来,子进程进程替换就不会影响父进程。

测试代码:

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

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        int n = execl("/usr/bin/ls", "ls", "-a", "-l", "--color=auto", NULL);
        perror("execl");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果: 

 

2.2 execlp 

int execlp(const char *file, const char *arg, ...);

p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径

测试代码:

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

int main()
{
    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //p:path,只要输入替换的程序,会自动去环境变量中进行查找。无需告诉路径
        execlp("ls", "ls", "-a", "-l", "--color=auto", NULL);
        perror("execl");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

 2.3 execv/execvp

//v:vector:可以将所有的执行参数放入数组,进行统一传入,不用可变参数传参 
int execv(const char *path, char *const argv[]);    


// v:vector p:文件名
int execvp(const char *file, char *const argv[]);  

execv 举例:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execv("/usr/bin/ls", argv_);
        
        perror("execv");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

 

 execvp 加上了p:我们只需给出名字,会去环境变量中自动查找

测试代码:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execvp("ls", argv_);
        
        perror("execvp");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果:

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


int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        char * argv_[] = {"ls", "-a", "-l", "--color=auto", NULL}; 
        execvp("ls", argv_);
        
        perror("execvp");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果

2.4 execle 

int execle(const char *path, const char *arg, ...,char *const envp[]);    //e:传入自定义环境变量,

e:传入自定义环境变量

我们定义另一个C程序mybin,让这个程序打印环境变量。然后我们在子进程中替换为mybin

这样我们的子进程就能够打印我们在execle函数中输入的环境变量了

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        // 加入命令行参数env
        execle("./mybin", "./mybin", NULL, env_);

        perror("execle");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

运行结果如下:

 我们换成系统环境变量

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        //注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        // 加入命令行参数env
        extern char** environ;
        execle("./mybin", "./mybin", NULL, environ);

        perror("execle");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

运行结果: 

当然我们也能够在 命令行参数获取环境变量

 2.5 execve 系统调用

上面的exec函数都是对execve系统调用的封装

//2号手册,这个才是真正的系统调用(上面的都是函数封装)
int execve(const char *path, char *const argv[], char *const envp[]);

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        // 注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        char *const argv_[] = {"./mybin"};
        execve("./mybin", argv_, env_);

        perror("execve");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

测试结果: 

 三. 进程替换总结

我们知道程序必须加载到内存中才能执行。这是由冯诺依曼计算机体系结构决定的。

在Linux中就是通过exec*函数来加载程序的!

那么是exec函数先加载还是main函数先执行?

exec函数先加载,然后main函数再执行

我们可以发现exec函数的参数和main函数的参数很像!

所以是exec函数先加载路径,参数,环境变量在执行main函数 

四. 进程替换可以执行任何后端语言!

myCppBin.cc

#include <iostream>
using namespace std;

int main()
{
    cout << "Hello C++!" << endl;
    return 0;
}

mytest.c

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

int main()
{

    // 使用execl去替换ls完成  ls -a -l --color=auto
    printf("1 process is running!\n");
    pid_t id = fork();
    if (id == 0)
    {
        // 注意在最后加上NULL
        char *const env_[] = {"AA=11", "BB=22", "CC=33", "YZC=Hello world!", NULL};
        char *const argv_[] = {"./myCppBin"};

        //执行C程序
        //execve("./mybin", argv_, env_);

        //执行C++
        execve("./myCppBin", argv_, env_);


        perror("execve");
        // 如果替换失败
        exit(-1);
    }

    // 由于写实拷贝,父进程不受子进程进程替换影响
    printf("2 process is running!\n");

    int status = 0;
    int subid = waitpid(id, &status, 0);
    if (subid > 0)
    {
        printf("child exit code:%d child exit signal\n", (status >> 8) & 0xff, status & 0x7f);
    }
    return 0;
}

标签:status,exec,int,char,ls,Linux,进程,include
From: https://blog.csdn.net/yzcllzx/article/details/144039050

相关文章

  • 【Linux】shell编程基础
    一、脚本的创建和执行创建一个1.sh文件vim1.sh在文件中写入以下内容:#!/bin/bash#或者写入#!/bin/sh#或者写入#!/bin/dash无论使用哪种脚本解释器,最终调用的可能是dash。检查默认的sh解释器:ls-l/bin/sh执行脚本文件./1.sh如果权限不足,可以......
  • Linux的150个常用命令汇总,运维大神不一定全部掌握!零基础入门到精通,收藏这一篇就够了
    下面是分类总结的150个命令,看一下你掌握了多少个?1文件和目录操作命令命令作用pwd显示当前所在位置cd切换目录tree以树形结构显示目录下的内容mkdir创建目录touch创建空文件ls显示目录下的内容及相关信息属性cp复制文件或目录mv移动或重命名文件rm删除文件或目录ln创建硬......
  • linux安装intel编译器2018
    加压软件/public/download/parallel_studio_2018.tgz进入目录后用./install.sh开始安装 按回车 这个选择1,然后需要输入lic激活文件路径。 安装完成后添加PATH到环境变量中1、如果是普通用户,在用户的.bashrc里面添加2、如果是root用户,在/etc/profile文件的最......
  • [Ubuntu] linux之Ubuntu18.04的下载及在虚拟机中详细安装过程(附有下载链接)
    前言ubuntu链接:https://pan.quark.cn/s/283509d0d36e提取码:dfT1链接失效(可能被官方和谐)可评论或私信我重发下载压缩包后解压!!安装路径不要有中文下载后解压得到.iso文件,不要放在有中文路径的目录下,我这里是解压放在E盘下打开虚拟机,这里使用的是VMwareWorkstation......
  • 学霸带你游戏化 Linux 防火墙 iptables、firewalld
    网络安全与流量控制的重要性在当今数字化时代,网络安全成为用户和组织关注的核心问题。对于在线游戏而言,稳定的网络连接和安全的环境是保证良好体验的基础。无论是《绝地求生》(PUBG)中的激烈战斗,还是《英雄联盟》(LeagueofLegends)的团队协作,都离不开高效的网络流量管理与安全防......
  • Linux程序员解决程序崩溃的问题
    Linux程序员解决程序崩溃的问题1.引言嘿,各位程序员小伙伴们!你们是否曾经遇到过程序突然“跑路”崩溃的情况?是不是觉得那一刻就像被一只无形的手拍在了脑门上,整个人都懵了?别担心,今天我们就来聊聊如何像侦探一样追查程序崩溃的真相,让你的代码更加坚不可摧!2.程序崩溃的常......
  • 第二章 进程管理
    2.1.1进程与线程的基本概念^03dd71进程的概念与特征进程的概念进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位进程是动态的,是程序的一次执行过程进程映像(进程实体):是静态的由程序段、相关数据段和PCB组成进程的特征动态性(进程最基本的特征)并发......
  • [Linux]动静态库
    动静态库静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库,但是,多个程序使用相同的静态库时,每个程序都会包含一份库的代码,可能会导致可执行文件体积较大。动态库(.so):是在程序运行时被加载的库。当一个程序链接了动态库,在程序启动时,操......
  • 《码农增刊Linus与Linux》读后感
     林纳斯的解释是,有三件事具有生命的意义。它们是你生活当中所有事情的动机。第一是生存,第二是社会秩序,第三是娱乐。生活中所有的事情都是按这个顺序发展的,娱乐之后便一无所有。其实每个人都有自己的理论,一件事做或者不做,都是自己说服自己,每一次进步,要么是推翻自己的理论,要么是......
  • Linux -初识 与基础指令1
    博客主页:【夜泉_ly】本文专栏:【Linux】欢迎点赞......