Linux:进程替换和知识整合

文章目录

  • 进程程序替换
    • 替换原理
    • 进程替换的理解
  • 环境变量与进程替换
  • 命令行解释器
    • 实现逻辑

进程程序替换

前面已经学习了子进程的创建,但是子进程的创建不管怎么说,都是父进程代码的一部分,那么实际上如果想要子进程执行新的程序呢?

也就是说,执行全新的代码和访问全新的数据,不再和父进程有瓜葛呢?这个时候就引入了关于进程替换的概念

替换原理

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

进程替换的理解

首先演示基本用法:

单进程下的用法

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main()
{printf("execl begin:\n");execl("/usr/bin/ls", "ls", "-a", "-l", "-n", NULL);printf("execl end:\n");return 0;
}

调用结果:

[test@VM-16-11-centos 11-8]$ ./myprocess 
execl begin:
total 28
drwxrwxr-x  2 1003 1003 4096 Nov  9 10:50 .
drwxrwxrwt 16 1003 1003 4096 Nov  8 20:40 ..
-rw-rw-r--  1 1003 1003   74 Nov  8 20:41 Makefile
-rwxrwxr-x  1 1003 1003 8416 Nov  9 10:50 myprocess
-rw-rw-r--  1 1003 1003  175 Nov  9 10:47 myprocess.c

从中看出,它的基本原理就是在进程中进行进程的替换

为什么最后输出的printf不被调用呢?

这是因为,执行到进程替换函数的时候,如果成功,整个进程的代码和数据都会被替换为所需替换的目标代码和数据,这样在后续执行的时候都会使用这份新的代码和数据,因此不会调用后续出现的代码

多进程版本的程序替换

将上述的代码更改为含有子进程的代码,具体如下:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childprintf("pid:%d,begin to exec!\n",getpid());sleep(3);execl("/usr/bin/ls","ls","-a","-l",NULL);printf("pid:%d,end to exec!\n",getpid());}else {// fatherprintf("wait child\n");pid_t rid = waitpid(-1,NULL,0);if(rid > 0){printf("wait success\n");}}return 0;
}

实验结果如下:

[test@VM-16-11-centos 11-8]$ ./myprocess 
wait child
pid:18212,begin to exec!
total 28
drwxrwxr-x  2 test test 4096 Nov  9 11:05 .
drwxrwxrwt 16 test test 4096 Nov  8 20:40 ..
-rw-rw-r--  1 test test   74 Nov  8 20:41 Makefile
-rwxrwxr-x  1 test test 8672 Nov  9 11:05 myprocess
-rw-rw-r--  1 test test  695 Nov  9 11:05 myprocess.c
wait success

从中看出多进程替换中增加了父进程对子进程的等待和回收的部分功能

那在多进程下应该如何理解进程替换呢?用下面图示的过程来演示:

在这里插入图片描述
从这里的进程替换中可以发掘出一些东西,替换的是进程,而不是代码,所以这里可以替换的内容有很多,甚至可以是Java写的程序运行起来的进程等等,看下面的实验

下面实现一个cpp程序

#include <iostream>int main()
{std::cout<<"this is a cpp program"<<std::endl;return 0;
}

对前面的程序进行修改

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// childprintf("pid:%d,begin to exec!\n",getpid());sleep(3);execl("./cpptest","./cpptest",NULL);//execl("/usr/bin/ls","ls","-a","-l",NULL);printf("pid:%d,end to exec!\n",getpid());}else {// fatherprintf("wait child\n");pid_t rid = waitpid(-1,NULL,0);if(rid > 0){printf("wait success\n");}}return 0;
}

对Makefile进行修改

.PHONY:all
all:myprocess cpptestcpptest:cpptest.cc g++ -o $@ $^myprocess:myprocess.cgcc -o $@ $^
.PHONY:clean
clean:rm -rf myprocess cpptest

这里利用的是Makefile自带的自我推演能力,使用Makefile进行自我推演可以推演出,现在需要myprocess和cpptest,而这两个程序又会分别进行执行运行

此时进行运行,此时会做出如下的实验结果:

[test@VM-16-11-centos 11-8]$ ./myprocess 
wait child
pid:23071,begin to exec!
this is a cpp program
wait success

从中不难看出,确实实现了进程的替换,而且替换的还是其余进程

这也就解释了在不同的公司中是可以存在分块进行构建模块功能的,最后都可以通过进程的形式链接起来

从某种意义来说,进程的替换已经可以被看成是一种系统调用了,站在系统的视角看内存中的所谓进程,实际上是一样的,系统高于一切,它可以对进程进行调度和分配

环境变量与进程替换

当进行进程替换的过程中,对于环境变量的角度来讲,是以什么样的情况进行的传递呢?

结论是:子进程对应的环境变量,是可以直接从父进程来的

对这个结论进行验证:

有关进程替换的一些函数
在这里插入图片描述

  1. execl函数,需要找到命令所在的文件目录,使用方法如下:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){// child// 进行进程替换execl("/usr/bin/ls", "ls", "-a", "-l", "-d", NULL);}else {// parent// 对子进程回收pid_t rid = waitpid(-1, NULL, 0);if(rid > 0){printf("wait success\n");}}return 0;
}
  1. execlp函数:会到系统默认的路径下寻找命令
execlp("ls", "ls", "-a", "-l", "-d", NULL);
  1. execle函数:用一个程序调用另外一个程序,但环境变量是自己的环境变量,不是系统的,通过获取环境变量查看

如何在进程中添加一个环境变量?用到的是putenv函数:

void *putenv(char *name)

由此可以写出下面的程序

#include <iostream>int main(int argc, char* argv[], char* env[])
{// 输出命令行参数for(int i = 0; argv[i]; i++){std::cout << i << "->" << argv[i] << std::endl;}std::cout << "##############" << std::endl;// 输出环境变量for(int i = 0; env[i]; i++){std::cout << i << "->" << env[i] << std::endl;}return 0;
}

上面是用于进程替换的函数,在这个基础上,对原程序进行修改

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{// 在程序中新增环境变量char* myenv = "MYVAL1 = 11111111";putenv(myenv);pid_t id = fork();if(id == 0){// child// 进行进程替换execl("./myprocess", "myprocess", NULL);}else {// parent// 对子进程回收pid_t rid = waitpid(-1, NULL, 0);if(rid > 0){printf("wait success\n");}}
}

运行程序如下:

在这里插入图片描述
从中看出,在子进程中是出现了新增的这个环境变量的,由此可以基本验证,在父进程中添加的环境变量会继承到子进程中

那么父进程的父进程是谁呢?答案是bash,那么是不是在bash中添加的环境变量也会继承到子进程中?

再对上面的程序进行修改

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char* argv[], char* env[])
{// 输出环境变量for(int i = 0; env[i]; i++){printf("%d -> %s\n", i, env[i]);}// 在程序中新增环境变量char* myenv = {"MYVAL1 = 11111111","MYVAL2 = 22222222",NULL};putenv(myenv);pid_t id = fork();if(id == 0){// child// 进行进程替换execl("./mytest", "mytest", NULL);}else {// parent// 对子进程回收pid_t rid = waitpid(-1, NULL, 0);if(rid > 0){printf("wait success\n");}}
}

在这里插入图片描述
由此可以得出这样的一条线索化的示意图:

在这里插入图片描述
再次回到这张图

在这里插入图片描述

下面看execle函数

环境变量的传递方式?

前面的例子证明,子进程的环境变量是由父进程传递的,而execle函数就是一个显示传递环境变量的函数,它的第三个参数是envp[],实际上就是环境变量

那如何进行使用?看下面的程序

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main(int argc, char* argv[], char* env[])
{// 在程序中新增环境变量char* const myenv[] = {"MYVAL1 = 11111111","MYVAL2 = 22222222",NULL};pid_t id = fork();if(id == 0){// child// 进行进程替换execle("./mytest", "mytest", NULL, myenv);}else {// parent// 对子进程回收pid_t rid = waitpid(-1, NULL, 0);if(rid > 0){printf("wait success\n");}}return 0;
}

运行结果如下:
在这里插入图片描述
从中看出,通过这个函数可以把环境变量进行显示传递给子进程,并且是一种覆盖式传递

到此,有关进程替换的基本逻辑已经结束,那进程替换可以做什么实际的东西呢?

命令行解释器

在前面的认知中,命令行解释器,也就是bash,可以把用户在命令行中敲的命令转换成命令再输出,而实际上,这是一个逻辑很简单的过程:

bash程序相当于是一个一直在后台运行的程序,而当用户敲了一些命令行后,bash创建子进程,就将这些命令行转换为一个字符串数组,采用进程替换的方式就可以把要找的命令和选项替换到前台,那依据这个原理,其实我们自己也能实现一个命令行解释器:

实现逻辑

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>#define NUM 1024
#define SIZE 64
#define SEP " "char cwd[1024];
char enval[1024];
int lastcode = 0;const char *getUsername()
{const char *name = getenv("USER");if(name) return name;else return "none";
}const char *getHostname()
{const char *hostname = getenv("HOSTNAME");if(hostname) return hostname;else return "none";
}const char *getCwd()
{const char *cwd = getenv("PWD");if(cwd) return cwd;else return "none";
}int getUserCommand(char *command, int num)
{printf("[%s@%s %s]# ", getUsername(), getHostname(), getCwd());char *r = fgets(command, num, stdin);if(r == NULL) return -1;command[strlen(command) - 1] = '\0';return strlen(command);
}void commandSplit(char *in, char *out[])
{int argc = 0;out[argc++] = strtok(in, SEP);while(out[argc++] = strtok(NULL, SEP));
}int execute(char *argv[])
{pid_t id = fork();if(id < 0) {return -1;}else if(id == 0){execvp(argv[0], argv);exit(1);}else{int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);}}return 0;
}void cd(const char *path)
{chdir(path);char tmp[1024];getcwd(tmp, sizeof(tmp));sprintf(cwd, "PWD=%s", tmp);putenv(cwd);
}int doBuildin(char *argv[])
{if(strcmp(argv[0], "cd") == 0){char *path = NULL;if(argv[1] == NULL) path = ".";else path = argv[1];cd(path);return 1;}else if(strcmp(argv[0], "export") == 0){if(argv[1] == NULL) return 1;strcpy(enval, argv[1]);putenv(enval); // ???return 1;}else if(strcmp(argv[0], "echo") == 0){char *val = argv[1] + 1;if(strcmp(val, "?") == 0){printf("%d\n", lastcode);lastcode = 0;}else{printf("%s\n", getenv(val));}return 1;}else if(0){}return 0;
}int main()
{while(1){char usercommand[NUM];char *argv[SIZE];// 1. 打印提示符&&获取用户命令字符串获取成功int n = getUserCommand(usercommand, sizeof(usercommand));if(n <= 0) continue;// 2. 分割字符串// "ls -a -l" -> "ls" "-a" "-l"commandSplit(usercommand, argv);// 3. check build-in commandn = doBuildin(argv);if(n) continue;// 4. 执行对应的命令execute(argv);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/147641.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UiPath Studio 2023.10 Crack

UiPath Studio是一款功能强大且用户友好的集成开发环境 (IDE)&#xff0c;专为机器人流程自动化 (RPA) 设计。它由自动化技术领域的领先公司UiPath开发。 以下是 UiPath Studio 的一些主要功能和组件&#xff1a; 图形用户界面 (GUI)&#xff1a;UiPath Studio 具有直观且用户友…

【机器学习】 逻辑回归算法:原理、精确率、召回率、实例应用(癌症病例预测)

1. 概念理解 逻辑回归&#xff0c;简称LR&#xff0c;它的特点是能够将我们的特征输入集合转化为0和1这两类的概率。一般来说&#xff0c;回归不用在分类问题上&#xff0c;但逻辑回归却能在二分类(即分成两类问题)上表现很好。 逻辑回归本质上是线性回归&#xff0c;只是在特…

数据采集与大数据架构分享

实现场景 要实现亿级数据的长期收集更新&#xff0c;并对采集后的数据进行整理和加工&#xff0c;用于人工智能的训练数据素材集。 数据采集 java支持的爬虫框架还是有很多的&#xff0c;如&#xff1a;webMagic、Spider、Jsoup等添加链接描述 pipeline处理管道 数据并发开发…

2023年【危险化学品经营单位安全管理人员】考试题及危险化学品经营单位安全管理人员模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试题是安全生产模拟考试一点通总题库中生成的一套危险化学品经营单位安全管理人员模拟试题&#xff0c;安全生产模拟考试一点通上危险化学品经营单位安全管理人员作业手机同步练习。…

操作系统(五)| 文件系统上 结构 存取方式 文件目录 检索

文章目录 1 文件系统概述2 文件的结构与存取方式2.1 磁盘2.2 文件的物理结构2.2.1 连续结构2.2.2 链式结构2.2.3 索引结构 2.3 文件的存取方式 3 文件目录3.1 基本概念3.2 目录结构单级目录结构多级目录结构 3.3 文件目录检索3.3.1 目录检索文件寻址 3.4 文件目录的实现 1 文件…

从0开始学习JavaScript--JavaScript 字符串与文本内容使用

JavaScript中的字符串和文本内容处理是前端开发中的核心技能之一。本文将深入研究字符串的创建、操作&#xff0c;以及文本内容的获取、修改等操作&#xff0c;并通过丰富的示例代码&#xff0c;帮助读者更全面地了解和应用这些概念。 JavaScript 字符串基础 字符串是JavaScr…

微服务实战系列之Nacos

导语 欢迎来到 “Nacos” 的世界&#xff01; Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称&#xff0c;一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单…

Ubuntu环境下以编译源码的方式安装Vim

目录 1. Ubuntu环境 2. 下载编译vim 2.1 效果截图 3. 配置环境变量 1. Ubuntu环境 Linux chris-166 6.2.0-36-generic #37~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Mon Oct 9 15:34:04 UTC 2 x86_64 x86_64 x86_64 GNU/Linux 2. 下载编译vim // 源码下载 chris_166chris-16…

文件传输客户端 SecureFX mac中文版支持多种协议

SecureFX mac是一款功能强大的文件传输客户端&#xff0c;可在 Mac 操作系统上使用。它由 VanDyke Software 公司开发&#xff0c;旨在为用户提供安全、可靠、高效的文件传输服务。 SecureFX 支持多种协议&#xff0c;包括 SFTP、SCP、FTP、FTP over SSL/TLS 和 HTTP/S。它使用…

联想系列台式机Win11系统改Win7系统BIOS设置步骤

联想最新一代的台式机默认操作系统Win11&#xff0c;采用UEFIGPT启动模式&#xff0c;并且开启了安全启动功能&#xff0c;一般用户不能直接将Win11改成Win7&#xff0c;如果需要更改操作系统&#xff0c;是需要再BIOS菜单中关闭安全启动功能的&#xff0c;并且把启动模式设置成…

2018年五一杯数学建模C题江苏省本科教育质量综合评价解题全过程文档及程序

2019年五一杯数学建模 C题 江苏省本科教育质量综合评价 原题再现 随着中国的改革开放&#xff0c;国家的综合实力不断增强&#xff0c;中国高等教育发展整体已进入世界中上水平。作为一个教育大省&#xff0c;江苏省的本科教育发展在全国名列前茅&#xff0c;而江苏省13个地级…

Django测试环境搭建及ORM查询(创建外键|跨表查询|双下划线查询 )

文章目录 一、表查询数据准备及测试环境搭建模型层前期准备测试环境搭建代码演示 二、ORM操作相关方法三、ORM常见的查询关键字四、ORM底层SQL语句五、双下划线查询数据查询&#xff08;双下划线&#xff09;双下划线小训练Django ORM __双下划线细解 六、ORM外键字段创建基础表…

Linux常用命令——bye命令

在线Linux命令查询工具 bye 命令用于中断FTP连线并结束程序。。 补充说明 bye命令在ftp模式下&#xff0c;输入bye即可中断目前的连线作业&#xff0c;并结束ftp的执行。 语法 bye实例 bye在线Linux命令查询工具

蓝眼开源云盘部署全过程(手动安装)

环境概述&#xff1a; 系统-Centos7.4 数据库-MySQL8 云盘系统-Tank4.0.1 前提&#xff1a;操作系统已完成安装&#xff0c;有外部网络。 一.安装数据库 cd到合适的目录进行下载安装操作&#xff0c;期间不要切换出去。 wget https://dev.mysql.com/get/mysql80-community-r…

Linux使用ifconifg命令,没有显示ens33

Linux使用ifconifg命令&#xff0c;没有显示ens33 1.问题2.步骤2.1 查看虚拟机的组件是否启动了2.2 修改网络配置文件 ONBOOT修改为yes2.3 重启网络2.4 修改网络服务配置 3.解决 1.问题 打开虚拟机准备使用xshell连接时发现连接失败&#xff0c;在机器上查看ip发现ens33不现实…

C++项目案例圆和点的关系 (涉及知识点:头文件定义类,cpp文件实现类,类和作用域,linux编译运行c++项目)

一.项目描述 点与圆有三种关系&#xff1a; 点在圆外 点在圆上 点在圆内计算点到圆心的距离就能判断点在圆的哪个地方。二.项目结构 三.include文件 3.1 Circle类的声明 Circle.h // 防止头文件重复包含 #pragma once // #include<iostream> #include "Point.h&…

互联网上门预约洗衣洗鞋店小程序;

拽牛科技干洗店洗鞋店软件&#xff0c;方便快捷&#xff0c;让你轻松洗衣。只需在线预约洗衣洗鞋服务&#xff0c;附近的门店立即上门取送&#xff0c;省心省力。轻松了解品牌线下门店&#xff0c;通过列表形式展示周围门店信息&#xff0c;自动选择最近门店为你服务。简单填写…

SpringSecurity5|12.实现RememberMe 及 实现原理分析

security/day08 这个功能大家还熟悉么&#xff1f;我们在登录网站的时候&#xff0c;除了让你输入用户名和密码&#xff0c;还会有个勾选框&#xff1a; 记住我&#xff01;&#xff01;&#xff01;不是让大家记住我哈。 值得一提的是&#xff0c;Spring Security 也提供了这个…

设计模式篇---装饰模式

文章目录 概念结构实例总结 概念 装饰模式&#xff1a;动态的给一个对象增加一些额外的职责。就扩展功能而言&#xff0c;装饰模式提供了 一种比使用子类更加灵活的替代方案。 装饰模式是一种对象结构型模式&#xff0c;它以对客户透明的方式动态地给一个对象附加上更多的责任…

asp.net学生成绩评估系统VS开发sqlserver数据库web结构c#编程计算机网页项目

一、源码特点 asp.net 学生成绩评估系统 是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 系统运行视频连接&#xff1a;https://www.bilibili.com/video/BV1Wz4y1A7CG/ 二、功能介绍 本系统使用Microsof…