Linux:进程替换
什么是进程替换?
我们的可执行程序,在运行起来的时候就上一个进程
一个进程就会有他的内核数据结构+代码和数据
把一个已经成型的进程的代码和数据替换掉,这就叫进程替换
也就是可以通过系统调用把当前进程替换位我们需要的进程
那么替换后,会创建一个新进程吗?
不会,只是在旧进程的壳子执行新进程;替换进程后,之前的代码不会执行,因为已经被替换了
进程怎样替换?
进程替换是需要接口函数的,总不能什么都没有直接替换吧
来看看都有什么:
上图的【int execl(const char *pfin,“const char *arg, ...);】这种后面的省略号,是指参数可变
来举个栗子捏:
test1.c
#include<stdio.h>
#include<unistd.h>int main()
{printf("test1.c ... begin!\n");execl("/usr/bin/ls", "ls", "-l", "-a", NULL);printf("test1.c ... end!\n");return 0;
}
makefile:
test1.out:test1.cgcc -o $@ $^
.PHONY:clean
clean:rm -f test1.out
解释一下:就是把我们本来的代码通过execl来执行execl引用的程序,成为替换的新进程
替换的过程?
而execl的返回值我们并不关心,因为一旦替换成功,就不会向后继续运行旧进程了
而只要继续运行旧进程,那就一定是替换失败了
所以我们的替换接口函数只有失败返回值,没有成功返回值
替换的过程本质上是把这个新程序加载到内存上
一个程序怎样加载到内存上呢?
别忘了exec*!它类似一种Linux上的加载函数,做的是从外设拷贝到内存是操作系统的活
多进程版的进程替换
将代码改成多进程版就是fork创建子进程,让子进程自己替换,父进程只需要等待就好了
也就是说这里被替换的是子进程捏,父进程还在等待
test1.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if(id == 0){printf("exec begin\n");//进程替换execl("/usr/bin/ls", "ls", "-al", NULL);printf("exec end\n");exit(0);}int rid = waitpid(id, NULL, WUNTRACED);if(rid > 0){printf("wait success\n");}return 0;
}
makefile:
test1.out:test1.cgcc -o $@ $^
.PHONY:clean
clean:rm -f test1.out
解释一下:我们在if(rid==0)分支里,先打印了一下,然后进入我们的接口,接口里是我们的ls命令,我们ls命令也是文件,也是C语言写的,我们平时执行命令也是执行该程序,所以可以在接口里替换为ls命令
这样就是替换成功了
我们刚刚说了替换失败会继续执行原进程,来看看吧:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ... begin!\n");pid_t id = fork();if (id == 0){sleep(2);execl("/usr/bin/lsss", "lsss", "-l", "-a", NULL);printf("替换失败捏");exit(1);}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("test1.c ... end!\n");return 0;
}
你看,没执行execl里面的程序
以前创建子进程让它完成任务,是让子进程执行父进程代码的一部分,现如今可以让子进程执行一个全新的程序
进程有对应的数据和代码,创建子进程会有新的地址空间和页表;替换本身就是覆盖,子进程在数据部分进行写时拷贝,当执行新程序的时候新程序也要进行写时拷贝,所以此后父子间几乎完全独立(本来数据和代码是继承父进程的,现在是全新的了)
来仔细介绍一下我们的替换接口函数:
这些函数的前四个字母都是exec,后面跟着的:
p: 表示自动搜索路径;e: 表示自己维护环境变量;l:表示采用列表传参;v:表示采用数组传参;
execl
int execl(const char *path, const char *arg, ...);
l就是list(列表),传参数列表
剩下的...带选项,在命令行中怎样写命令的参数,就怎么写(下图有例子捏)结束时传NULL
路径表明我们想要执行谁,后面的选项表示我们想要怎样执行
execv
v是vector的意思,动态数组
int execv(const char *path, char *const argv[]);
前面的参数是路径,后面的参数是指针数组,execl的是可变参数,execv的传参需要传指针数组指针数组是自己写的
execvp
带p的意思就是用户可以不用传要执行文件的路径(但要传文件名)
char *const argv[] = {"ps", "-ef", NULL};
execvp("ps", argv);
那它是怎么做到的呢?
别忘了p是可以自己搜路径,在环境变量里搜路径
execlp
就是采用列表传参,但是不用输路径
execlp("ps", "ps", "-ef", NULL);
execvpe
execvpe
带e的需要自己组装环境变量
int execvp(const char *file, char *const argv[], char *const envp[]);
以替换 "ls -al" 为例,示例一下剩余接口:
//l代表传参数列表,分成一个个字符串传
execl("/usr/bin/ls", "ls", "-al", NULL);//p表示系统会去PATH环境变量中找
execlp("ls", "ls", "-al", NULL);//v代表传数组,把命令分成字符串,放进字符串数组里,一起传
char* argv[] = {"ls", "-al", NULL};
execv("/usr/bin/ls", argv);
替换自己写的进程
刚刚我们引用了系统的进程,我们也可以引用我们自己写的程序
下面就来替换段C++代码(文件后缀: .cpp , .cc ,.cxx)
来先写一个自己的程序:
#include<iostream>using namespace std;int main()
{cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}
然后再也一个父进程:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){sleep(2);execl("./mypragma", "mypragma", NULL);exit(1);}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("test1.c ... end!\n");return 0;
}
tips:别忘了makefile默认是从上到下形成可执行程序,所以第一个要是主程序,其他的依赖程序依次往下排哦
.PHONY:all
all:testexec mypragmamypragma:mypragma.ccg++ -o $@ $^ -std=c++11testexec:testexec.cgcc -o $@ $^
.PHONY:clean
clean:rm -f testexec mypragma
来看看:
程序替换并未形成新进程,我们来验证一下:
test1.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){ printf("i am a process,pid:%d\n",getpid());sleep(2);execl("./mypragma", "mypragma", NULL);exit(1);}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("test1.c ... end!\n");return 0;
}
mypragma.cpp
#include<iostream>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
using namespace std;int main()
{printf("i am a new process,pid:%d\n",getpid());cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}
看,这两个进程pid是一样的
自己组装环境变量
我们刚刚说环境变量可以自己组装,来引用
看看吧:
test1.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{printf("test1.c ...begin!\n");pid_t id = fork();if (id == 0){sleep(2);char *const argv[] = { (char*)"mypragma",NULL };char* const envp[] = { (char*)"LIKE=521",(char*)"LOVE=1314",NULL };execvpe("./mypragma", argv, envp);exit(1);}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("test1.c ... end!\n"); return 0;
}
mypragma.cpp:
#include<iostream>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++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;cout << "hello C++!" << endl;return 0;
}
哦?
父进程本身自己的环境变量,来自于父进程的父进程:bash
你也可以用全新的环境变量,也可以用修改后的环境变量:使用putenv()
往进程加环境变量: putenv() ,程序替换不会替换环境变量,我们可以通过带e的接口函数(例如execpe())设置新的环境变量,这会覆盖原本的环境变量。
用法:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(){char *p;if((p = getenv("USER")))printf("USER =%s\n", p);putenv("USER=test");printf("USER+5s\n", getenv("USER"));}
这些都是:
#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[]);
tips:只有execve是真正的系统调用,其它五个函数最终都调用 execve(execve在man手册第2节,其它函数在man手册第3节)
相关文章:

Linux:进程替换
什么是进程替换? 我们的可执行程序,在运行起来的时候就上一个进程 一个进程就会有他的内核数据结构代码和数据 把一个已经成型的进程的代码和数据替换掉,这就叫进程替换 也就是可以通过系统调用把当前进程替换位我们需要的进程 那么替换…...
带你认识:数据仓库宽表~~~浅显易懂
1. 构建宽表的目的 讲宽表我想从为什么需要宽表入手,而不是一上来就抠概念。因为我觉得一门知识叫什么名字并不是最核心的,关键是搞清楚它的诞生背景以及如何在特定场景用好它。 构建宽表的目的很简单,就是为了"一站式"尽可能多的展示我们需要…...

记录|MessageBox.Show()的使用
目录 前言一、解析1.1 代码1.2 具体图片解析 更新时间 前言 遇到了其他人写的MessageBox.Show()的用法,有点懵,特此记录。 一、解析 1.1 代码 MessageBox.Show("登录失败!", "用户登录", MessageBoxButtons.OK, MessageBoxIcon.E…...

LabVIEW软件定制开发公司的前景如何?
LabVIEW软件定制开发公司的前景在当前的技术发展环境下展现出一定的潜力与挑战。这一领域的市场前景主要受到工业自动化、物联网、智能制造等技术趋势的推动,同时也受到行业竞争、技术更新以及人才市场的制约。 市场需求与增长潜力 随着工业4.0、物联网和智能制…...
vue3列表页搜索条件封装
搜索框组件 封装常用搜索框组件,类型有: input(默认值)selectselectV2 (value/label键值对数组)datePickeryear 集成新增、修改、删除、导入、导出按钮,支持slot自定义其他按钮封装搜索、重置按钮封装按钮权限封装导入弹框 本例仅…...
十三、切片的复制
1、使用函数copy 注意点:复制前必须再声明一个与要复制对象类型相同的切片 var cheeses make([]int, 5)cheeses[0] 1cheeses[1] 2cheeses[2] 3cheeses[3] 4cheeses[4] 5var myCheeses make([]int, 5)copy(myCheeses, cheeses) 使用copy函数将cheeses的数据…...
Java Stream API 的应用:提取并处理多属性集合
Java Stream API 是一个功能强大的工具,可以帮助开发者高效地处理集合数据。本篇博客将专注于一个具体的应用示例,即如何使用 Java Stream API 从一个对象列表中提取多个属性值,并进行过滤和去重。这种技术在处理需要从多个字段中提取数据的情…...

【技术方案】智慧城市大数据平台技术方案(Doc原件)
第1章 总体说明 1.1 建设背景 1.2 建设目标 1.3 项目建设主要内容 1.4 设计原则 第2章 对项目的理解 2.1 现状分析 2.2 业务需求分析 2.3 功能需求分析 第3章 大数据平台建设方案 3.1 大数据平台总体设计 3.2 大数据平台功能设计 3.3 平台应用 第4章 政策标准保障体系 4.1 政策…...

vue项目中引入字体文件样式
需求:关于一些样式需要自定义的,所以需要ui提供字体文件,然后引入项目中,就可实现自定义 首先看一下实现效果图: 第一步:新建一个字体样式文件用于放字体文件和css样式 font.css文件: /* 数字特殊字体 */ font-face {/*给字体命名*/font-family: DINCondBold;/*引入字体文件*…...
Android 11强制App固定user_rotation方向显示
Android11 强制App按照user_rotation方向显示。 diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index b1d349d8e93d..f7fd2983c668 100644 --- a/services/core/java/com/an…...
Harbor仓库push显示
背景: 在做测试时发现harbor仓库端口开放这,却一直登录不上去,重启harbor资源包docker-compose还是不行,修改了docker.service文件不行,json文件也不行,以下是涉及到的命令和报错(好像是这个&am…...
Windows 上设置 MySQL 的主从复制
Windows 上设置 MySQL 的主从复制 一、前言1. 环境准备2. 主服务器配置3. 从服务器配置6. 测试复制7. 注意事项 一、前言 在 Windows 上设置 MySQL 的主从复制涉及几个步骤。下面是一个详细的指南,帮助你实现这一过程。 1. 环境准备 安装 MySQL: 确保你…...

鸿蒙内核源码分析(原子操作篇) | 谁在为原子操作保驾护航
基本概念 在支持多任务的操作系统中,修改一块内存区域的数据需要“读取-修改-写入”三个步骤。然而同一内存区域的数据可能同时被多个任务访问,如果在修改数据的过程中被其他任务打断,就会造成该操作的执行结果无法预知。 使用开关中断的方…...

vue3+ts封装axios以及解决跨域问题
目录 一、前言二、封装axios三、 解决跨域四、调用接口五、运行结果 一、前言 前端请求后端数据时,会用到axios,但是如果不将axios封装好,会导致代码冗余 二次封装的好处如下: 求头能统一处理便于接口的统一管理解决回调地狱配置…...

各厂家BI对比
帆软BI、奥威BI、永洪BI、思迈特BI、亿信华辰BI是国内知名的BI产品,不少企业在选型BI软件时都需要对这些BI软件进行了解,从中选择适合自己的一款。经过过年的发展,这些BI(商业智能)软件各自在多个行业中都有广泛的应用…...
SQL - 触发器
触发器是在插入、更新和删除语句前后自动执行的一堆SQL代码,但是触发器被触发后只会执行一次,通常我们使用触发器增强数据的一致性。创建触发器 -- 创建触发器 drop trigger if exists payments_after_insert; delimiter $$ -- 在 payments表 insert 之后…...

Redis中缓存穿透、缓存击穿、缓存雪崩的详解
如何理解Redis缓存的穿透、击穿、雪崩问题: 缓存穿透 是指缓存中和数据库中都没有数据,而用户不断访问,导致这个不存在的数据每次请求都要到存储层去查询,这样失去了意义。 缓存穿透的解决方案有哪些? 缓存null值布隆过滤增强…...

[Meachines] [Medium] Popcorn SQLI+Upload File+PAM权限提升
信息收集 IP AddressOpening Ports10.10.10.6TCP:22,80 $ nmap -p- 10.10.10.6 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 5.1p1 Debian 6ubuntu2 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: …...
【Linux】python进程管理之supervisor安装使用教程
安装supervisor pip install supervisor生成配置文件 echo_supervisord_conf > /etc/supervisord.conf修改配置文件 vim /etc/supervisord.conf[unix_http_server] file/run/supervisor.sock ; the path to the socket file[supervisord] logfile/var/log/supervisord.log…...

BEM架构
视频 总结: BEM架构:一个命名类的规范而已,说白了就是如何给类起名字使用sass的目的:在<style>中模块化的使用类名,同时减少代码数量 1、 BEM架构 (通义灵码查询结果) BEM (Block Ele…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...