当前位置: 首页 > article >正文

系统编程1.0-exec函数和exit()的使用

 exec函数

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

int main()
{
        pid_t x = fork();
        if(x > 0)
        {
                sleep(1);
                printf("aaa!\n");
        }
        if(x == 0)
        {
                printf("hello!\n");
                //以下几个是等价
                //execl("/bin/ls","ls","-l",NULL);
                //execlp("ls","ls","-l",NULL);
                //execle("/bin/ls", "ls","-l",NULL,NULL);

                char *arg[3] = {"ls","-l",NULL};

                //execv("/bin/ls",arg);

                execvp("ls",arg);

        //      execvpe("ls",arg,NULL);
                printf("world!\n");
        }
        return 0;
}

详细解释

  • char *arg[3] 定义了一个大小为 3 的字符指针数组。每个元素是一个 char * 类型的指针,指向一个字符串(即一个 char 数组)。
  • {"ls", "-l", NULL} 是初始化这个数组的内容:
    • "ls":这是第一个元素,表示执行的命令或程序名。在这里它表示 ls 命令,用来列出当前目录的内容。
    • "-l":这是第二个元素,表示传递给 ls 命令的一个参数,它告诉 ls 显示文件的详细信息(即长格式的列出目录内容)。
    • NULL:这是数组的最后一个元素,标志着参数列表的结束。exec 系列函数要求参数数组必须以 NULL 结尾来表示参数的结束。
  • char *arg[3] = {"ls", "-l", NULL}; 定义了一个包含两个命令行参数和一个结束符 NULL 的数组,准备作为参数传递给 execv() 或其他 exec 系列函数。这个数组表示执行 ls -l 命令,并列出当前目录的详细内容。

execvpe() 的作用

  • execvpe("ls", arg, NULL) 会执行 /bin/ls(或者在 PATH 中查找 ls),并传递参数 -l。这会列出当前目录的详细文件信息。
  • 一旦 execvpe() 被调用,当前进程(子进程)会被 /bin/ls 程序替换,printf("world!\n"); 语句将不会执行,因为 execvpe() 会完全替换当前进程的映像。

 程序执行流程:

  • 父进程:调用 fork(),得到子进程PID,随后调用 sleep(1) 暂停1秒,然后打印 aaa!
  • 子进程:调用 fork() 后,执行 execvpe("ls", arg, NULL),替换自己成为 /bin/ls,并打印当前目录的文件列表。printf("world!\n") 永远不会执行,因为进程已经被替换。

各种 exec 系列函数:

  • execl():传递每个参数给新程序。示例:execl("/bin/ls", "ls", "-l", NULL);

    • 第一个参数是程序路径,后面的参数是要传递给新程序的命令行参数。最后一个参数必须是 NULL
  • execlp():与 execl() 类似,但会在 PATH 环境变量中查找程序。示例:execlp("ls", "ls", "-l", NULL);

    • 不需要指定程序的完整路径,只要在 PATH 中能够找到即可。
  • execle():与 execl() 类似,但可以传递环境变量。示例:execle("/bin/ls", "ls", "-l", NULL, NULL);

    • 额外的参数传递给新的程序作为环境变量。
  • execv():类似 execl(),但通过数组传递参数。示例:execv("/bin/ls", arg);

    • arg 是一个数组,包含程序的命令行参数。最后一个元素必须是 NULL
  • execvp():与 execv() 类似,但会在 PATH 环境变量中查找程序。示例:execvp("ls", arg);

    • 不需要指定程序的完整路径,直接提供程序名,系统会在 PATH 中查找。
  • execvpe():与 execvp() 类似,但额外提供了环境变量的传递。示例:execvpe("ls", arg, NULL);

    • execvp() 的区别是,可以传递自定义的环境变量。

 

exit函数

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

int main()
{
	printf("hello");
	
	//exit(0);
	_exit(0);
}

代码执行流程:

  1. printf("hello");
    • 程序首先会打印 "hello",但是它并没有立即输出到屏幕上。printf 会将数据写入缓冲区,直到程序结束或缓冲区被刷新,才会显示出来。
  2. _exit(0);exit(0);
    • 程序接着会调用 _exit(0),这是一个系统调用,通常用来退出当前进程。它的作用是终止程序并返回状态码 0(通常表示正常退出)。

为什么使用 _exit()

  • _exit() 更适合在子进程中使用,尤其是在调用 fork() 后,父子进程的执行情况不同,可能需要在子进程中快速退出,而不需要刷新缓冲区或调用清理函数。
  • 例如,fork() 创建子进程时,子进程可能调用 _exit() 以避免父进程的清理函数和缓冲区刷新干扰。

在这段代码中的行为:

  • 如果你使用 exit(0),程序会打印 "hello",然后刷新缓冲区,确保 "hello" 被显示在屏幕上,然后正常退出。
  • 如果你使用 _exit(0),程序会打印 "hello"但由于 _exit() 不会刷新缓冲区,所以 "hello" 可能不会显示出来,然后直接退出程序。

为什么使用退出函数?

程序在执行完成后会自动退出,但是我们使用 exit()_exit() 这样的函数来控制程序退出的方式和时机。虽然程序会在 main() 函数执行完后自动退出,但使用退出函数可以让你精确地控制退出时的行为,特别是如何处理缓冲区、清理工作等。

1. 程序自动退出

  • 当程序执行完 main() 函数,或者执行到 return 或没有进一步的代码时,程序会自动退出。
  • 退出时,操作系统会回收进程占用的资源,并根据程序返回的状态码给出退出信息。

2. 使用退出函数(exit()_exit())的区别

虽然在某些情况下,程序会在 main() 函数结束时自动退出,但通过显式调用退出函数,我们可以控制退出时的一些细节。这包括:

  • 清理工作(如资源释放、内存清理)。
  • 输出缓冲区的处理(如将缓冲区中的数据写入到终端或文件)。
  • 退出状态码的设置(传递给操作系统,父进程或其他程序查看的退出状态)。

3. exit()_exit() 的区别

exit()
  • exit() 是一个标准库函数,通常用来在程序结束时进行清理工作。
  • 主要行为
    1. 刷新缓冲区:它会确保所有通过 printf() 等输出缓冲区中的数据都被写入(即使你在程序中调用了多次 printf(),它们会在退出时一次性显示)。
    2. 调用清理函数exit() 会执行所有通过 atexit() 注册的清理函数,这对于释放资源(如关闭文件、释放内存等)很有用。
    3. 返回退出状态码exit() 会返回一个状态码(通常是 0 表示成功,其他数字表示出错)给操作系统,表示程序是否正常结束。
_exit()
  • _exit() 是一个系统调用,通常在程序异常或需要立即退出时使用。
  • 主要行为
    1. 不刷新缓冲区:调用 _exit() 后,程序不会刷新缓冲区,因此如果有任何数据尚未写入屏幕或文件,它们将丢失。这是它与 exit() 的主要区别。
    2. 不调用清理函数_exit() 不会调用 atexit() 注册的清理函数,因此程序不会执行任何资源释放操作。
    3. 立即退出_exit() 会立即终止进程,不做任何额外操作,快速退出程序。

4. 程序自动退出 vs 显式调用退出函数的对比

自动退出:
  • main() 函数执行完后,程序会自动退出。如果程序没有使用 exit()_exit(),操作系统会回收进程资源,打印退出状态码(默认是0,表示程序正常结束)。
  • 自动退出时的默认行为
    • 程序会先执行标准的清理过程:刷新输出缓冲区,关闭文件描述符等。
    • 最后,返回状态码给操作系统,表示程序的退出状态。
显式调用退出函数:
  • 如果你调用了 exit()_exit(),你就能控制程序退出的方式,确保在程序退出前完成特定的工作。
    • exit():刷新缓冲区、调用清理函数、并返回状态码。
    • _exit():立即退出,不刷新缓冲区,也不调用清理函数。

5. 何时使用 exit()_exit()

  • exit()

    • 当你希望在程序退出前做一些清理工作,如释放资源、关闭文件、清空缓冲区时,使用 exit() 是合适的。
    • 例如,在编写多线程程序时,你可能需要确保在退出时清理资源并关闭所有打开的文件。
  • _exit()

    • 当你希望在程序终止时立刻退出,且不需要做清理工作时,可以使用 _exit()。例如,系统调用、进程失败后的处理,或者子进程通过 fork() 后退出时,通常会用 _exit() 来避免父进程的缓冲区和清理函数影响。

总结:

  • 程序自动退出:如果没有调用 exit()_exit()程序会在 main() 执行结束后自动退出,操作系统会回收资源并返回退出状态。
  • 显式调用退出函数:通过调用 exit()_exit(),你可以精确控制程序退出的时机和行为,特别是如何处理缓冲区、清理资源等。 

http://www.kler.cn/a/469854.html

相关文章:

  • 若依框架简介
  • PixPin—— 高效截图工具的下载与使用攻略
  • 【C++】字符串与字符数|组操作详解:strcpy 和 strcat 的使用与解析
  • 场馆预定平台高并发时间段预定实现V1
  • Synthesia技术浅析(二):虚拟人物视频生成
  • 如何进一步提高Oracle lgwr的写性能?
  • 《OpenCV 5.0.0-alpha:开启计算机视觉新篇章》
  • 在arm平台Euler系统上编译安装ffmpeg
  • [python]验证码识别库-DDDDOCR
  • CAM几何引擎简介
  • 目标检测算法-Picodet
  • 基于python大数据分析的高考志愿填报推荐系统实现
  • 决定系数(R²分数)——评估回归模型性能的一个指标
  • 【办公类-88-02】20250106批量读后感
  • Leetcode-234 回文链表
  • 飞牛fnOS如何通过docker安装宝塔面板
  • 基于Python深度学习【眼疾识别】系统设计与实现+人工智能+机器学习+TensorFlow算法
  • 1929-2024年全球气象站点逐日气象指标数据(气温、降水量、风速等12项)
  • 最新国家商标战略实施DID数据(2007-2023年)
  • 使用Locust对MongoDB进行负载测试
  • 力扣-数组-01两数之和
  • Mysql事务的特性和隔离级别
  • HTML5 + Bootstrap5 网站底部代码分享与解析
  • 【网络安全设备系列】13、网页防篡改
  • Speech Recognition vs. Voice Recognition | 语音识别工作原理 | 模型训练 | 应用
  • Neo4j的部署和操作