在这里插入图片描述

一、进程替换是什么?

fork()之后,父子各自执行父进程代码
的一部分。如果子进程想执行全新程序
就会用到进程的程序替换来完成这个功能

程序替换:通过特定接口,加载磁盘
上的一个权限程序(代码和数据)
加载到调用进程的地址空间中
以达到让子进程执行其他程序的目的

将新的磁盘上的程序加载加载到内存
并和当前进程页表重新建立映射
用操作系统相关接口即可完成
在这里插入图片描述

二、execl系列函数

man execl 查看exec系列函数

在这里插入图片描述

一共有6个函数,这些函数都是

  1. 成功无返回值
  2. 失败返回 -1
  3. 以NULL作为结束标识符

且都属于exec系列函数

我们先演示最简单的execl函数

execl = exec + l
表示它属于exec系列里的execl系列函数
l 表示list,即后面传参时一个个往后跟
像链表的结点一样去传参

execl 是程序替换,该函数调用成功之后
会将当前进程所有代码和数据都进行替换
包括已执行的和没执行的

代码测试

int main()
{
	
    printf("当前进程的开始代码\n");
    execl("/usr/bin/ls", "ls", "-l", NULL);
    
    printf("当前进程的结束代码\n");
    
    return 0;
}

测试结果
在这里插入图片描述
本来要执行后面的
printf(“当前进程的结束代码\n”);
被替换成 ls -l 命令

execlp函数

execlp函数名中p表示PATH
即这个函数会自己在环境变量(PATH)
中进行查找,不用自己传路径

在这里插入图片描述
代码测试

#define NUM 16
 
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        // 子进程
        // ls -a -l
        printf("子进程开始运行,pid: %d\n", getpid());
        sleep(3);                                                                                  
        // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
        execlp("ls", "ls", "-a", "-l", NULL);
        exit(1);
    }
    else
    {
        // 父进程
        printf("父进程开始运行,pid: %d\n", getpid());
        int status = 0;
        pid_t id = waitpid(-1, &status, 0); // 阻塞等待
        if (id > 0) // 等待成功
        {
            // 打印子进程退出码
            printf("wait success exit code: %d\n", WEXITSTATUS(status));
        }
    }

    return 0;
}

测试结果
在这里插入图片描述
execle函数

execle函数比execl函数多个一个参数
这个参数就是环境变量

在这里插入图片描述
在这里插入图片描述

// mycmd.c
int main()                                                                                                            {   
	printf("获取环境变量: my_l: %s\n", getenv("my_l"));
	return 0;
}
// myproc.c
#define NUM 16
const char* myfile = "./mycmd";

int main()
{	
    // 定义一个环境变量,环境变量名my_l后面的=不要空格
    char* const _env[NUM] = { (char*)"my_l=999777888666", NULL }; 
    pid_t id = fork();
    if (id == 0)
    {	
        // 子进程
        printf("子进程开始运行,pid: %d\n", getpid());
        sleep(3);
        char* const argv[NUM] = { (char*)"ls", (char*)"-a", (char*)"-l", NULL };
        execle(myfile, "mycmd", "-a", NULL, _env);
        exit(1);                                                                                                      
    }
    else
    {
        // 父进程
        printf("父进程开始运行,pid: %d\n", getpid());
        int status = 0;
        pid_t id = waitpid(-1, &status, 0); // 阻塞等待
        if (id > 0) // 等待成功
        {
            // 打印子进程退出码
            printf("wait success exit code: %d\n", WEXITSTATUS(status)); 
        }
    }
return 0;
}

通过execle函数
用一个可执行文件(myproc.c)
执行另一个可执行文件(mycmd.c)
并通过另一个可执行文件(mycmd.c)
获取可执行文件(myproc.c)里定义的
环境变量并打印

执行结果
在这里插入图片描述

三、execv系列函数

execv = exec + v
表示它属于exec系列里的execv系列函数
v 表示vector,即后面传参时把你要传的
参数构建成一个指针数组传过去

在这里插入图片描述
execv函数
可以看到execl和execv除了
传参的方式不一样其他都一样

代码测试

#define NUM 16
 
int main()
{
    pid_t id = fork();
    if (id == 0)
    {
        // 子进程
        // ls -a -l
        printf("子进程开始运行,pid: %d\n", getpid());
        sleep(3);
        char* const argv[NUM] = { (char*)"ls", (char*)"-a", (char*)"-l", NULL };
        execv("/usr/bin/ls", argv);                                                                                     
        // execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
        exit(1);
    }
    else
    {
        // 父进程
        printf("父进程开始运行,pid: %d\n", getpid());
        int status = 0;
        pid_t id = waitpid(-1, &status, 0); // 阻塞等待
        if (id > 0) // 等待成功
        {
            // 打印子进程退出码
            printf("wait success exit code: %d\n", WEXITSTATUS(status));
        }
    }

    return 0;
}

测试结果
在这里插入图片描述
execvp函数
我们学习上面几个函数
其他函数就很简单了
用法都是类似的

// 还是上面的代码,函数改成execvp函数
char* const argv[NUM] = { (char*)"ls", (char*)"-a", (char*)"-l", NULL };
execvp("ls", argv);

运行结果: 可以看到是能正常运行的
在这里插入图片描述
这些函数原型看起来很容易混
但只要掌握了规律就很好记

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

在这里插入图片描述
所有的接口底层都是调用
execle系统接口
在这里插入图片描述

四、如何执行其他C、C++二进制程序

形成两个可执行程序
用一个可执行程序
调用另一个可执行程序

代码实现

// 另一个可执行程序地址
const char* myfile = "/home/ff/Gogh/tempfile/2023/mycmd";
// const char* myfile = "./mycmd"; // 绝对路径和相对路径都是可以的

int main()
{

    pid_t id = fork();
    if (id == 0)
    {
        // 子进程
        printf("子进程开始运行,pid: %d\n", getpid());
        sleep(3);
        execl(myfile, "mycmd", "-a", NULL);
        // execlp("./mycmd", "./mycmd", NULL); 用execlp调用另一个可执行程序
        exit(1);
    }
    else
    {
        // 父进程
        printf("父进程开始运行,pid: %d\n", getpid());
        int status = 0;
        pid_t id = waitpid(-1, &status, 0); // 阻塞等待
        if (id > 0) // 等待成功
        {
            // 打印子进程退出码
            printf("wait success exit code: %d\n", WEXITSTATUS(status)); 
        }
    }

    return 0;
}

另一个可执行程序代码

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

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        printf("can not execute\n");
        exit(1);
    }
    if (strcmp(argv[1], "-a") == 0)
    {
        printf("hello a\n");
    }
    else if (strcmp(argv[1], "-b") == 0)
    {
        printf("hello b\n");                                                                                            
    }
    else
    {
        printf("default\n");
    }
    return 0;
}

运行结果
在这里插入图片描述
用一个可执行程序执行
其他语言的可执行程序

// 还是上面的代码
execlp("python", "python", "test.py", NULL);

通过上面的代码实现可以发现
exec*函数:功能就是加载器的底层接口

五、自实现shell命令解析器

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

#define NUM 1024
#define SIZE 32
#define SEP " "

// 保存完整的命令行字符串
char cmd_line[NUM];
// 保存打散之后的命令行字符串
char* g_argv[SIZE];

// shell 运行原理:通过让子进程执行命令,父进程等待 && 解析命令
int main()                                                                                        
{
    // 0.命令行解释器,一定是一个不退出的常驻内存的进程
    while (1)
    {
        // 1.打印提示信息 
        printf("[ff@localhost myshell]#"); // 内容在缓冲区,没有\n无法刷新,有\n光标无法定位当前行
        fflush(stdout); // 用fflush函数刷新
        memset(cmd_line, '\0', sizeof cmd_line); // 初始化
        // 2.获取用户的键盘输入[输入的是各种指令和选项:"ls -a -l -i"]
        if (fgets(cmd_line, sizeof cmd_line, stdin) == NULL) 
        {
            // 读取出错
            continue;
        }
        cmd_line[strlen(cmd_line) - 1] = '\0'; // 清除输入的换行符
        // printf("echo: %s\n", cmd_line);
        // 3.命令行字符串解析:"ls -a -l -i" ->  "ls" "-a" "-l" "-i"
        g_argv[0] = strtok(cmd_line, SEP); // 第一次调用,要传入原始字符串
		int index = 1;
		if (strcmp(g_argv[0], "ls") == 0) // 让目录带颜色
		{
    		g_argv[index++] = "--color=auto";
		}
		if (strcmp(g_argv[0], "ll") == 0)
		{
    		g_argv[0] = "ls";
    		g_argv[index++] = "-l";
    		g_argv[index++] = "--color=auto";
		}
		while (g_argv[index++] = strtok(NULL, SEP)); // 第二次如果还要解析原始字符串,传入NULL
		// for (index = 0; g_argv[index]; index++ )
		//     printf("g_argv[%d]: %s\n", index, g_argv[index]); // g_argv解析完毕最后以NULL结尾
		// 4.TODO,内置命令,让父进程(shell)自己执行的命令叫做内置命令
		// 内置命令本质shell中的一个函数调用
		if (strcmp(g_argv[0], "cd") == 0)
		{
    		if (g_argv[1] != NULL) chdir(g_argv[1]);
    		continue;
		}
		// 5.fork
		pid_t id = fork();
		if (id == 0) // child
		{
    		printf("下面功能让子进程进行的\n");
    		execvp(g_argv[0], g_argv);
    		exit(1);
        }
        // father 阻塞等待
        int status = 0;
        pid_t ret = waitpid(id, &status, 0);
        if (ret > 0) printf("exit code: %d\n", WEXITSTATUS(status));
     }
     return 0;
 }

✨✨✨✨✨✨✨✨
本篇博客完,感谢阅读🌹
如有错误之处可评论指出
博主会耐心听取每条意见
✨✨✨✨✨✨✨✨

相关文章

C语言中strstr函数的使用!

这里要进行分析,有一个重要的点就是,成勋会返回abc及其后面的字符,如上图所示p2代表abc,而abc在p1中能够找到,所以返回abc和p1中abc后面的所有字符,这是一个需要注意的地方。//判断p2字符串是不是在p1中,如果在就是子字符串,否则不是。if (ret == NULL) //函数返回值是保存在ret这个字符指针变量中的,为空说明不是子字符串。printf(&quot;子字符串不在\n&quot;);具体直接看下面的这段代码我相信你必明白。

ubuntu20.04安装实时内核补丁PREEMPT_RT

下载实时内核补丁,我下载patch-5.15.148-rt74.patch.sign和patch-5.15.148-rt74.patch.xz。通过以下指令看具体报错并输出日志到make.log:make -j1 deb-pkg 2&gt;&amp;1 | tee ~/make.log。比较幸运没遇到问题,重启进入后,启动页面没有变化,还是进入ubuntu,但是查看内核版本已经自动变到5.15.148。我下载linux-5.15.148.tar.xz和linux-5.15.148.tar.sign。

Linux 目录磁盘满了,怎么查找大文件

如果你不确定某个文件或目录的用途,最好先进行调查或咨询专业人士,而不是直接删除它们。,这是一个基于文本的磁盘使用分析器,非常适合于查找大文件。如果它没有预装,你可以通过你的包管理器安装它(例如,在Ubuntu上使用。会分析根目录的磁盘使用情况,并提供一个交互式界面来浏览最大的文件和目录。请注意,运行这些命令可能需要一些时间,因为它们会检查许多文件。)磁盘满了,你可以使用以下方法来查找占用空间最大的文件和目录。这个命令会搜索根目录下所有的文件,并显示它们的大小。为了找到最大的文件,你可以使用。

如何在 Debian 12 上安装 Microsoft SQL Server?

在安装 Microsoft SQL Server 之前,我们需要确保系统是最新的,并安装一些必要的软件和依赖项。以下是详细的步骤:这将更新软件包列表并升级已安装的软件包。这将安装 curl 用于下载文件,gnupg 用于导入 GPG 密钥,以及 apt-transport-https 用于通过 HTTPS 访问软件包。

Mac 版 Excel 和 Windows 版 Excel的区别

它提供了丰富的功能和工具,包括公式、函数、图表和数据透视表等,帮助用户高效地处理和管理大量数据。例如,VBA 中的扩展 ASCII 字符在 MacOS 中通常有所不同,某些宏键盘快捷键似乎是保留的,VBA 动画仅适用于 Win 版 Excel ,并且右键单击上下文菜单无法使用 Mac 版 Excel 中的 VBA 进行编辑。对于许多高级用户来说,使用数据透视表和数据透视图执行数据分析和可视化数据的能力是 Excel 最有价值的功能之一,因此在确定哪个版本的 Excel 最适合您时需要考虑这一基本功能。

服务器与电脑的区别?

服务器是指一种专门提供计算和存储资源、运行特定软件服务的物理或虚拟计算机。服务器主要用于接受和处理来自客户端(如个人电脑、手机等)的请求,并向客户端提供所需的服务或数据。服务器在网络环境中扮演着中心节点的角色,负责存储和管理数据、提供网络服务、处理计算任务等。

windows下ngnix自启动(借助工具winSw)

在windows下安装nginx后,不想每次都手动启动。本文记录下windows下ngnix自启动(借助工具winSw)的操作流程提示:以下是本篇文章正文内容,下面案例可供参考本文记录下windows下ngnix自启动(借助工具winSw)的操作流程。

linux docker 部署mysql8以上版本时弹出Access denied for user root @ localhost (using password: YES)的解决方案

mysql8登录第一次遇到MYSQL_ROOT_PASSWORD时会自动把该密码尽兴登录,生成一个秘钥放在mysql的数据文件里面,命令里带的MYSQL_ROOT_PASSWORD密码是个参数,除了第一次运行mysql带上会设置密码生成秘钥,其他次启动而不是设置mysql的密码,而是作为参数去验证这个最初的秘钥是否核对正确,于是我进入挂载的data目录,发现我的猜想是对的。通过docker将服务部署完后,navicat连接报错,密码错误,于是我尝试进入mysql容器登录 发现也报错。

Linux 磁盘空间占用率100%的排查

max-size 最大数值 , max-file 最大日志数,但一两个容器还好,但是如果有很多容器需要管理,这样就很不方便了,最好还是可以统一管理(全局修改)结果显示多条如下数据,这里最关键的指标就是使用百分比,这个值较高一般需要处理,或者明确知道自己项目或工作目录是哪个,就只要找对应的位置去处理即可。/var/lib/docker/overlay2 【文件系统】基于容器文件系统保存的数据会写到本机的此目录下,进行限制,以减少日志文件对存储空间的占用,以下配置分别为日志文件最大容量、最大日志文件数。

chatchat部署在ubuntu上的坑

2. 安装后把代理关闭,全局的代理改为手动,重新打开一个新的控制台。1. 安装前要开代理,注意要下载很多东西,流量大。

解决Linux环境下gdal报错:ERROR 4: `/xxx.hdf‘ not recognized as a supported file format.

题外话:我发现linux系统和Windows系统下面,库的版本是有差异的。比如我的本机Windows上装的是gdal3.2.3和numpy1.19.1,linux服务器上装的却是gdal3.0.2和numpy1.21.5。这个是很常见的回复,网上许多回答都说低版本的 gdal 不支持 hdf5,让你重装高版本的gdal。我之前用pip安装了whl,暴力装上了,但用的时候就会有问题。安装了不冲突的gdal之后,就成功打开文件啦~一开始我是抱着试试的心态,用conda,不用pip,重新安装了一下我的gdal。

内网穿透、远程桌面、VPN的理解

针对不同的场景可能咱们可以选择不同的方法,局域网远程桌面这种其实也不是很常用,为什么呢,因为如果就在局域网的话,那么你本人直接过去操作就可以了,不需要远程桌面,而且远程桌面还需要给你的登录账户和密码,这些都是隐私的东西,一般最好不对外泄露。VPN其实是比较适合在家远程办公的场景的,电脑带回家,然后连接VPN,就可以实现办公了,但是也有一些问题,就是公司必须要一个固定的IP,还必须要配一个VPN服务器,固定IP这个是越来越少了,很多宽带都不是固定外网IP了。

Linux 服务器 CPU 详细信息查看、物理 CPU 以及逻辑 CPU

当两个线程都同时需要某一个资源时,其中一个要暂时停止,并让出资源,直到这些资源闲置后才能继续,因此超线程的性能并不等于两颗CPU的性能。超线程技术就是利用特殊的硬件指令,把两个逻辑内核模拟成两个物理芯片,让单个处理器都能使用线程级并行计算,进而兼容多线程操作系统和软件,查看CPU详细得知,服务器共有16个核心,物理CPU个数为 4,证明单个物理CPU上集成了4个核心处理器。日常我们所说的CPU核数指的是物理CPU上存在几个核心处理器或者核心处理单元总和(排除超线程技术)Linux内核支持关闭超线程技术。

浅谈ARM嵌入式中的根文件系统rootfs

这里设置 console 为 ttymxc0,因为 linux启动以后 I.MX6ULL 的串口 1 在 linux 下的设备文件就是/dev/ttymxc0,在 Linux 下,一切皆文件。/dev/mmcblk1、/dev/mmcblk0p1、/dev/mmcblk0p2、/dev/mmcblk1p1 和/dev/mmcblk1p2 这样的文件,其中/dev/mmcblkx(x=0~n)表示 mmc 设备,而/dev/mmcblkxpy(x=0。有这个“根”,其他的文件系统或者软件就别想工作。

linux配置DNS主从服务器

主服务器:OpenElur Linux IP地址为192.168.188.129。从服务器:RedHat Linux IP地址为192.168.188.128。2.配置主服务器的`解析配置文件。3.进行从服务器的基础配置。1.进行主服务器的基础配置。

Centos系统上安装PostgreSQL和常用PostgreSQL功能

PostgreSQL安装成功之后,会默认创建一个名为postgres的Linux用户,初始化数据库后,会有名为postgres的数据库,来存储数据库的基础信息,例如用户信息等等,相当于MySQL中默认的名为mysql数据库。权限代码:SELECT、INSERT、UPDATE、DELETE、TRUNCATE、REFERENCES、TRIGGER、CREATE、CONNECT、TEMPORARY、EXECUTE、USAGE。为了方便我们使用postgres账号进行管理,我们可以修改该账号的密码。

Docker之nacos的安装和使用

在上一期的博客分享中我们分享了有关Nginx的安装和使用,当然我们知道上一期的博客分享的是使用Nginx实现负载均衡。本期的博客文章基于上一期的Docker之Nginx安装的基础上,本期的。

MySQL数据库主从复制集群原理概念以及搭建流程

主从复制是指将主数据库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持同步。MySQL支持一台主库同时向多台从库进行复制, 从库同时也可以作为其他从服务器的主库,实现链状复制。主库出现问题,可以快速切换到从库提供服务。实现读写分离,降低主库的访问压力。可以在从库中执行备份,以避免备份期间影响主库服务。

HarmonyOS4.0系统性深入开发19进程模型概述

HarmonyOS通过CES(Common Event Service,公共事件服务)为应用程序提供订阅、发布、退订公共事件的能力。公共事件从系统角度可分为:系统公共事件和自定义公共事件。系统公共事件:CES内部定义的公共事件,只有系统应用和系统服务才能发布,例如HAP安装,更新,卸载等公共事件。目前支持的系统公共事件详见系统公共事件列表。自定义公共事件:应用自定义一些公共事件用来实现跨进程的事件通信能力。公共事件按发送方式可分为:无序公共事件、有序公共事件和粘性公共事件。

Linux(Ubantu)交叉编译生成windows(32位,64位)可执行程序和库

与 mingw32 相比,mingw-w64 提供了对 64 位 Windows 应用程序的支持,并且通常被认为是更现代和更新的工具。这个选项通常用于 Unix-like 系统的编译器,用以指导链接器在生成可执行文件时保留符号信息,以便支持运行时的符号解析(例如用于动态加载库).该选项对于 Windows 下的编译是无效的,通过。(能够解析windows平台的可执行程序) 则能直接允许直接在linux环境中运行我们生成的win32的可执行程序(包括验证win32平台的动态库).

如何使用可视化管理工具DockerUI远程管理docker容器

DockerUI是一个docker容器镜像的可视化图形化管理工具。DockerUI可以用来轻松构建、管理和维护docker环境。它是完全开源且免费的。基于容器安装方式,部署方便高效,浏览和维护docker单节点或集群节点worker和manager。DockerUI具有易于使用的界面。它不需要记住 docker 指令。只需下载镜像即可立即加入并完成部署。使用DockerUI并结合cpolar内网穿透可以更加轻松的管理docker和swarm,实现后台公网访问并管理,视觉性更加直观,后台开发更加便利。

如何使用Plex在Windows系统搭建个人媒体站点公网可访问

用手机或者平板电脑看视频,已经算是生活中稀松平常的场景了,特别是各种碎片时间(追剧下饭、地铁上刷剧等等),看个喜欢的视频必不可少。但不知道为什么,各大影音平台总能轮流占住热播剧,还限定很多剧只能会员观看,搞得我们总有交不完的会员费。此时,拥有一个私人影音媒体站点就显得很有必要。今天,笔者就为大家介绍,如何使用cpolar+Plex组合,在Windows系统上搭建一个全能的私人媒体影音站点。

如何使用Docker将.Net6项目部署到Linux服务器(三)

尤其在测试环境,多个项目公用一个nginx的情况很多,这个时候,如果多个项目的nginx配置都放在一个conf配置文件中,会特别混乱。按照nginx配置,它应该监听4012端口,然后找到根目录/usr/local/publish_2023/forum_manage_vue,访问我们的vue网站,可是却提醒我们,该网页无法显示。# 注意,还可以写域名,但是需要额外的配置,后面示例会介绍,这个实例是简单的测试服务器的nginx配置、不过多说明。在nginx下的conf中,找到配置文件nginx.conf。
返回
顶部