李晨晨的嵌入式学习 DAY25

news2024/11/26 10:41:35

今天对昨天的fork函数进行了补充,并且学习了exec函数

一,fork函数补充

1.open在fork之前

                子进程会继承父进程已打开的相关信息,父子进程会影响同一个offset值

2.open在fork之后

                父子进程各自有各自打开的文件信息,不相互影响

3.子进程做的事情和父进程差不多

4.父进程创建子进程后,子进程做的事情与父进程完全不同。

二,exec 族函数

在C语言中,exec族函数是用来执行一个指定的程序,替换当前进程的映像(image)、数据、堆和栈等,但进程ID保持不变。这意味着,当exec函数成功执行后,当前进程的代码和数据将被新程序的代码和数据替换,但进程ID等属性保持不变。exec族函数包括多个变体,这些变体在参数处理和行为上有所不同,但主要目的都是相同的:执行一个新程序。

exec族函数包括但不限于:

execl、execle、execlp、execlpe(使用列表形式传递参数)

这些函数通过参数列表的形式传递给新程序参数,而不是通过字符串数组。其中,execlp和execlpe会根据环境变量PATH来搜索可执行文件,而execl和execle则需要提供可执行文件的完整路径。execle还允许指定新程序的环境变量。

execv、execve、execvp、execvpe(使用字符串数组传递参数)

这些函数通过字符串数组的形式传递参数给新程序。与execl系列类似,execvp和execvpe会搜索PATH环境变量来找到可执行文件,而execv和execve需要可执行文件的完整路径。execve允许直接指定新程序的环境变量。

在这些函数中,execve是最基本的版本,因为它允许直接指定程序路径、参数列表和环境变量。其他exec函数都是基于execve的封装,提供了不同的参数传递方式或自动搜索可执行文件的路径。

值得注意的是,exec族函数执行成功后不会返回,因为当前进程的映像已经被新程序替换。如果exec族函数返回了,那通常意味着发生了错误(例如,指定的文件不是可执行文件,或者没有足够的权限执行该文件),此时会返回-1,并设置errno以指示错误的原因。

由于exec族函数不会返回,因此通常在调用exec族函数之前,会使用fork函数创建一个新的进程,然后在子进程中调用exec族函数来执行新程序。这样,如果exec调用失败,父进程还可以检测到这个错误,并采取相应的措施(如打印错误信息并退出)。

1.execl函数

在C语言中,execl函数是exec族函数之一,用于执行一个指定的程序,替换当前进程的映像(包括代码、数据、堆和栈等),但进程ID保持不变。然而,需要澄清的是,标准的POSIX定义中并没有直接名为execl的函数,但存在类似的execl风格的函数,如execlp和execl的变体(尽管后者在标准中通常不是直接命名的),它们通过参数列表的形式而不是字符串数组来传递参数给新程序。


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

参数:

path:要执行的可执行文件的路径。
arg:传递给新程序的参数列表,以NULL结尾。注意,这里不是使用字符串数组,而是直接在函数调用中列出所有参数,最后以NULL作为结束标志。

返回值:

如果execl函数成功,它不会返回,因为当前进程的映像已经被新程序替换。
如果execl函数失败,则返回-1,并设置全局变量errno以指示错误的原因。

2.execlp函数

execlp函数是C语言中exec函数族的一员,用于执行一个指定的程序,替换当前进程的映像(包括代码、数据、堆和栈等),但进程ID保持不变。execlp函数的特点是从PATH环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,并将后续参数作为该程序的命令行参数。

头文件:#include <unistd.h>
函数原型:int execlp(const char *file, const char *arg, ...);

参数说明


file:要加载的程序的名字(可执行文件名),execlp会在PATH环境变量指定的目录中查找该文件。
arg:传递给新程序的参数列表,第一个参数arg通常被设置为新程序的名称(尽管这不是必须的,但有助于调试和日志记录),后续参数为实际要传递给新程序的参数。参数列表必须以NULL指针作为结束标志。

使用注意事项


参数传递:execlp函数的参数是通过可变参数列表传递的,最后一个参数必须是NULL,用于指示参数列表的结束。
PATH环境变量:execlp会搜索PATH环境变量中指定的目录来查找可执行文件。如果file参数包含斜杠(/),则execlp会将其视为路径名,并直接在该位置查找文件,而不会搜索PATH环境变量。
进程替换:当execlp函数成功执行时,当前进程的映像(包括代码、数据、堆和栈等)会被新程序的映像替换,但进程ID保持不变。这意味着新程序将在当前进程的上下文中运行,但会拥有新程序的代码和数据。
错误处理:如果execlp函数执行失败(例如,找不到指定的文件或没有执行权限),它会返回-1,并将错误原因存储在全局变量errno中。调用者可以通过检查errno的值来确定失败的具体原因。

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

int main() 
{
    // 尝试执行ls命令,列出当前目录下的文件和目录
    execlp("ls", "ls", "-l", NULL);

    // 如果execlp执行成功,则不会执行到这一行
    // 如果执行失败(例如,因为找不到ls命令),则会执行到这里
    perror("execlp failed");
    return 1;
}


在这个示例中,我们尝试使用execlp函数执行ls命令,并列出当前目录下的文件和目录。如果execlp执行成功,则当前进程的映像会被ls命令的映像替换,因此后续的代码(如perror调用)将不会执行。如果execlp执行失败(例如,因为PATH环境变量中没有包含ls命令的路径),则会执行perror调用,打印错误信息。

3.execel函数

执行结果

execel函数与evn结合实现父子函数执行不同功能

执行结果

4.execv函数

execv函数是C语言中exec函数族的一员,用于在当前进程中执行一个新的程序,并替换当前进程的映像(包括代码、数据、堆和栈等),但进程ID保持不变。execv函数的主要特点是它允许用户以字符串数组的形式指定新程序的命令行参数。以下是关于execv函数的详细介绍:
 

1. 函数原型

execv函数的原型定义在<unistd.h>头文件中,其原型如下:

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

pathname:指向要执行的新程序的路径名的指针。这个路径名必须是绝对路径,或者相对于调用execv的进程的当前工作目录的相对路径。
argv:指向一个字符串数组的指针,该数组包含要传递给新程序的参数。数组的第一个元素(argv[0])通常是新程序的名称,但从argv[1]开始是传递给新程序的参数。数组的最后一个元素必须是NULL,以标记参数列表的结束。

2. 函数行为


当execv函数被调用时,它会加载并执行由pathname指定的程序。
如果成功,execv不会返回给调用者,因为当前进程的映像已经被新程序的映像替换。
如果失败,execv会返回-1,并设置全局变量errno以指示错误原因。常见的错误原因包括文件不存在、不是可执行文件等。

3. 示例代码

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

int main() 
{
    const char* path = "/bin/ls"; // 要执行的新程序的路径
    char* argv[] = {"ls", "-l", NULL}; // 传递给新程序的参数数组,以NULL结尾

    // 执行新程序
    if (execv(path, argv) == -1)
    {
        // 如果execv失败,则打印错误信息
        perror("execv failed");
        exit(EXIT_FAILURE);
    }

    // 如果execv成功,则不会执行到这里
    // 因为execv会替换当前进程的映像

    return 0; // 这行代码实际上永远不会执行
}

4. 注意事项
execv函数不会继承调用它的进程的环境变量,除非你显式地通过某种方式(如使用execve函数)传递它们。
在使用execv函数时,当前进程的映像会被新程序的映像替换,因此execv调用之后,除了错误处理外,通常不会有额外的代码执行。
pathname参数必须是绝对路径,或者相对于调用execv的进程的当前工作目录的相对路径。
argv数组必须以NULL结尾,且数组中的每个元素都应该是有效的字符串。

4.execvp函数

execvp函数是C语言中exec函数族的一员,用于在Linux环境下执行指定的文件,并将参数传递给该文件。这个函数的特点在于它会从环境变量PATH所指定的目录中查找符合参数file的文件名,找到后执行该文件,并将第二个参数argv(一个字符串数组)作为参数传递给该文件。以下是关于execvp函数的详细解析:

1. 函数原型

execvp函数的原型定义在<unistd.h>头文件中,其原型如下:

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

file:指向要执行的文件的名称的指针。execvp函数会在环境变量PATH所指定的目录中查找该文件。
argv:指向一个字符串数组的指针,该数组包含要传递给执行文件的参数。数组的第一个元素(argv[0])通常是被执行文件的名称(尽管execvp不直接使用它,但可以作为参数传递给执行文件),从argv[1]开始是传递给执行文件的参数。数组的最后一个元素必须是NULL,以标记参数列表的结束。

2. 函数行为

当execvp函数被调用时,它会按照环境变量PATH中指定的目录顺序搜索名为file的文件。一旦找到该文件,就会加载并执行它,替换当前进程的映像(包括代码、数据、堆和栈等)。
如果成功,execvp函数不会返回给调用者,因为当前进程的映像已经被新程序的映像替换。
如果执行失败,execvp函数会返回-1,并将全局变量errno设置为特定的错误代码,以指示失败的原因。

3. 常见错误代码


EACCES:指定的文件或目录的权限不足,无法执行或访问。
ENOENT:指定的文件不存在。
ENOEXEC:无法执行指定的文件,可能是因为文件格式错误或不是有效的可执行文件。
ENOMEM:系统内存不足,无法执行新程序。
EFAULT:argv参数指向的字符串地址超出了可访问的空间范围。
ENAMETOOLONG:指定的文件名或路径太长。

4. 注意事项


execvp函数不会继承调用它的进程的环境变量,除非通过其他方式(如使用execve函数)显式地传递它们。
在使用execvp函数时,需要确保file参数指定的文件是可执行的,并且具有适当的权限。
由于execvp函数会替换当前进程的映像,因此在其调用之后,除了错误处理代码外,通常不会有额外的代码执行。

5. 示例代码

以下是一个使用execvp函数执行新程序的示例代码:

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

int main()
 {
    char *argv[] = {"ls", "-l", NULL}; // 要传递给ls命令的参数

    // 使用execvp执行ls命令
    if (execvp("ls", argv) == -1) 
    {
        // 如果execvp失败,则打印错误信息
        perror("execvp failed");
        exit(EXIT_FAILURE);
    }

    // 如果execvp成功,则不会执行到这里
    // 因为execvp会替换当前进程的映像

    return 0; // 这行代码实际上永远不会执行
}

在这个示例中,execvp函数会查找并执行名为ls的程序,将-l作为参数传递给它,以列出当前目录下的文件和目录的详细信息。如果execvp调用成功,程序将不会返回,而是直接执行ls命令。如果调用失败,则会打印错误信息并退出程序

5.execvpe函数

execvpe函数是C语言中exec函数族的一个成员,它提供了在Linux环境下执行指定文件的能力,并允许调用者指定自定义的环境变量列表给新程序。这个函数结合了execvp和execve函数的功能,既可以在环境变量PATH中搜索可执行文件,又可以指定新程序的环境变量。


1. 函数原型

execvpe函数的原型定义在<unistd.h>头文件中


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

file:指向要执行的文件的名称的指针。execvpe函数会在环境变量PATH所指定的目录中查找该文件。
argv:指向一个字符串数组的指针,该数组包含要传递给执行文件的参数。数组的第一个元素(argv[0])通常是被执行文件的名称(尽管execvpe不直接使用它,但可以作为参数传递给执行文件),从argv[1]开始是传递给执行文件的参数。数组的最后一个元素必须是NULL,以标记参数列表的结束。
envp:指向一个字符串数组的指针,该数组包含要传递给新程序的环境变量。数组的最后一个元素也必须是NULL,以标记环境变量列表的结束

2. 函数行为


当execvpe函数被调用时,它会按照环境变量PATH中指定的目录顺序搜索名为file的文件。一旦找到该文件,就会加载并执行它,替换当前进程的映像(包括代码、数据、堆和栈等),并使用envp参数指定的环境变量列表作为新程序的环境变量。
如果成功,execvpe函数不会返回给调用者,因为当前进程的映像已经被新程序的映像替换。
如果执行失败,execvpe函数会返回-1,并将全局变量errno设置为特定的错误代码,以指示失败的原因。

3. 注意事项


execvpe函数是GNU的扩展,可能不是所有系统都支持。在编写跨平台代码时,需要注意这一点。
由于execvpe函数会替换当前进程的映像,因此在其调用之后,除了错误处理代码外,通常不会有额外的代码执行。
在使用execvpe函数时,需要确保file参数指定的文件是可执行的,并且具有适当的权限。
自定义的环境变量列表envp需要由调用者提供,并且必须以NULL结尾。如果不需要传递自定义环境变量,可以使用execvp或execv等其他exec函数。

4.示例代码

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

// 假设这个函数实现了类似于 glibc getenv_path 的功能
// 它在 PATH 环境变量中搜索可执行文件
char *find_executable(const char *file) {
    // 这里需要实现实际的搜索逻辑
    // 例如,解析 PATH 环境变量,遍历目录等
    // 注意:这里仅作为伪代码
    return strdup("/path/to/found/executable"); // 假设找到了可执行文件
}

int execvpe(const char *file, char *const argv[], char *const envp[]) {
    char *executable_path = find_executable(file);
    if (!executable_path) {
        errno = ENOENT; // 或其他适当的错误码
        return -1;
    }

    // 使用 execve 执行找到的可执行文件
    // 注意:execve 的第一个参数是文件路径,而不是文件名
    // 因此我们使用 executable_path
    execve(executable_path, argv, envp);

    // 如果 execve 返回,那么一定是有错误发生了
    // 但实际上,execve 只在发生错误时才会返回
    // 这里仅为了代码的完整性而包含
    perror("execvpe failed");
    free(executable_path); // 清理我们分配的内存
    return -1; // 实际中不应该到达这里
}

// 注意:上面的 find_executable 函数需要实现完整的 PATH 搜索逻辑
// 并且 execvpe 函数在调用 execve 后通常不会返回

三,进程的结束

结束方式

1.正常结束

a.从main函数中返回return

b.调用exit

c.调用_exit

exit   ————库函数 ———— 刷新缓冲区

_exit  ————系统调用 ———— 不会刷新缓冲区

调用函数清除缓冲区

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

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

相关文章

Luatos-lua For MacOSX

0x00 缘起 看到Luatos-soc-pc项目能够编译到MacOS平台并且运行&#xff0c;所以尝试编译&#xff1b;可是Apple Clang编译器太过于严格&#xff0c;导致编译不通过。遂换到gcc-11编译通过&#xff0c;虽然其中依旧会报错&#xff08;宏定义LUA_USE_MACOSX不起作用&#xff0c;导…

Linux驱动入门实验班——LED驱动(附百问网视频链接)

目录 一、确定引脚编号 二、编写思路 2.1驱动层 2.2应用层 三、源码 四、实现 课程链接 一、确定引脚编号 首先&#xff0c;可以在开发板上执行如下命令查看已经在使用的GPIO状态&#xff1a; cat /sys/kernel/debug/gpio 可以看到每个gpio都有对应的编号&#xff0c;…

岗位信息采集全攻略:两种方法快速获取招聘信息

摘要 本文将揭秘两大实战策略&#xff0c;助你在激烈的人才市场中迅速捕捉前程无忧上的宝贵岗位信息&#xff0c;无论是手动搜索还是利用现代技术手段&#xff0c;都能事半功倍&#xff0c;抢占先机。 正文 一、手动搜索的艺术&#xff1a;精准定位&#xff0c;深度挖掘 1.…

【网络】传输层TCP协议的报头和传输机制

目录 引言 报头和有效载荷 确认应答机制 超时重传机制 排序和去重 连接管理机制 个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 引言 TCP是传输层协议&#xff0c;全称传输控制协议。TCP报头中有丰富的字段以及协议本身会制定完善的策略来保证网络传输的可靠性。 TCP…

ICM-20948芯片详解(12)

接前一篇文章&#xff1a;ICM-20948芯片详解&#xff08;11&#xff09; 六、寄存器详解 2. USER BANK 0寄存器详述 &#xff08;56&#xff09;FIFO_EN_1 参考代码&#xff1a; 无。 &#xff08;57&#xff09;FIFO_EN_2 ACCEL_FIFO_EN 1 —— 以采样率将ACCEL-XOUT_H、…

haproxy实例

什么是haproxy Haproxy是一款提供高可用性&#xff0c;负载均衡以及基于tcp和http的的应用交付控制器的开源软件。它由法国人威利塔罗使用c语言开发的。它广泛用于管理和路由网络流量&#xff0c;并确保应用程序的高可用性和高性能。 haproxy的功能 提供第4层&#xff08;TCP层…

vulnhub系列:Hackademic.RTB1

vulnhub系列&#xff1a;Hackademic.RTB1 靶机下载 一、信息收集 nmap 扫描存活&#xff0c;根据 mac 地址寻找 IP nmap 192.168.23.0/24nmap 扫描端口&#xff0c;开放端口&#xff1a;22、80 nmap 192.168.23.143 -p- -Pn -sV -O访问80端口&#xff0c;页面发现 target …

DirectX修复工具解决问题:一步步教你排除常见错误

在日常使用电脑的过程中&#xff0c;许多用户可能会遇到与DirectX相关的问题&#xff0c;特别是在运行大型游戏或图形密集型应用程序时。这种情况下&#xff0c;选择一款合适的DirectX修复工具免费版来解决问题至关重要&#xff01; 我们将分享六款好用的DirectX修复工具&…

字节Java后端二面也太难了吧...

粉丝投稿&#xff0c;字节二面直接连环问场景题&#xff0c;难以招架&#xff0c;已经准备好市场上常见的场景题了&#xff0c;希望能帮助你&#xff01; 由于平台篇幅原因&#xff0c;很多内容展示不了&#xff0c;需要这份《java面试宝典》的伙伴们转发文章关注后&#xff…

Linux_Shell变量及运算符-05

一、Shell基础 1.1 什么是shell Shell脚本语言是实现Linux/UNIX系统管理及自W动化运维所必备的重要工具&#xff0c; Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算…

MySQL基础练习题38-每位教师所教授的科目种类的数量

目录 题目 准备数据 分析数据 总结 题目 查询每位老师在大学里教授的科目种类的数量。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Teacher (teacher_id int, subject_id int, dept_id int)## 向表中插入数据 Truncate table…

ALB使用指南|深入探究 lj-lua-stacks:如何生成 Lua 代码级别的火焰图?

前言 ALB&#xff08;Another Load Balancer&#xff09;是一款由灵雀云基于 OpenResty 开发的开源Kubernetes&#xff08;K8s&#xff09; 网关&#xff0c;拥有多年的生产环境使用经验。Openresty框架高性能的基础上&#xff0c;提供了一系列高级特性&#xff0c;包括多租户支…

JsonUtility和JsonConvert.DeserializeObject

文章目录 JsonUtility 和 JsonConvert在字符串转类型时的不同总结 JsonUtility 和 JsonConvert在字符串转类型时的不同 不同&#xff1a; JsonUtility.FromJson要转的字符串内不能有注释的字符串 但JsonConvert.DeserializeObject要转的字符串里可以有 原因&#xff1a;. Jso…

新品|暴雨发布第四代工作站静安系列-TR1176

极致性能引领未来 暴雨信息推出全新一代工作站——静安系列TR1176&#xff0c;搭载了强劲的第四代处理器&#xff0c;为客户带来前所未有的计算速度与效率。无论是处理大规模数据集、复杂算法还是高负载多任务&#xff0c;都能轻松应对&#xff0c;确保客户的工作流程顺畅无阻…

【随笔】Java 连接操作FTP与SFTP 详细指南

引言 在Java开发中&#xff0c;文件传输协议&#xff08;FTP&#xff09;和安全文件传输协议&#xff08;SFTP&#xff09;是处理文件传输的两种常见方式。FTP是标准的网络文件传输协议&#xff0c;而SFTP则在FTP基础上增加了安全层&#xff08;SSH&#xff09;&#xff0c;提…

设计模式 之 —— 组合模式

目录 什么是组合模式&#xff1f; 定义 特点 结构 组合模式&#xff08;java代码示例&#xff09; 首先定义接口 定义叶节点&#xff08;Leaf类&#xff09; 定义容器节点&#xff08;Composite类&#xff09; 测试类&#xff1a; 树形图 运行结果&#xff1a; 组…

CUDA Programming - (1) CUDA简介

1. GPU 简介 处理器一般包含以下几部分&#xff1a;Cache 缓存&#xff0c;ALU 计算单元&#xff0c;Control 控制中心&#xff0c;RAM 内存。 CPU&#xff08;Central Processing Unit&#xff09;&#xff1a;中央处理器。适合进行逻辑&#xff0c;ALU计算核心较少。适合控…

乒乓球桌上的AI新星:谷歌机器人Agent,是竞技的未来,还是科技的幻想?

在巴黎奥运会乒乓球的激烈角逐中&#xff0c;一个不同寻常的选手悄然登场——谷歌的乒乓球机器人Agent。 这是首个在竞技水平上达到人类标准的AI机器人。 01 AI机器人颠覆初级玩家 Agent不仅能够在正反手之间自如转换&#xff0c;连续进攻&#xff0c;甚至在面对诸如长球、高…

【Qt】QWidegt的enable属性

enabled&#xff1a;描述了一个控件是否处于“可用”状态&#xff08;相对的概念“禁用”&#xff09;。 API说明 isEnabled() 获取到控件的可⽤状态 setEnabled 设置控件是否可使⽤. true 表⽰可⽤, false 表⽰禁⽤ 所谓“禁用”指的是该控件不能接收任何用户的输入事件…

localStorage用法

一、localStorage API使用 注意&#xff0c;localStorage 只能存储字符串。 1.1 存储数据 例如&#xff0c;对象存储时&#xff0c;可以使用JSON.stringify 转成字符串&#xff1b; // 假设你有一个对象 const myObject {name: Alice,age: 25, };// 使用 JSON.stringify 将…