Linux系统编程(多进程编程深入1) 您所在的位置:网站首页 fork函数使用实例 Linux系统编程(多进程编程深入1)

Linux系统编程(多进程编程深入1)

2023-06-28 21:01| 来源: 网络整理| 查看: 265

文章目录 前言一、进程参数和环境变量的意义二、子进程程序结束返回值到哪里去了?三、进程退出函数四、实际使用案例五、僵尸进程总结

前言

本篇文章我们深入的讲解多进程编程。

一、进程参数和环境变量的意义

进程参数和环境变量是两种不同的机制,但它们都在操作系统中扮演着重要的角色。它们用于传递信息给正在运行的进程,以影响它们的行为和配置。

进程参数(Process Arguments): 进程参数是在启动进程时传递给它的命令行参数。它们是在运行进程时指定的,并且可以用于向程序提供特定的输入或配置信息。进程参数通常以空格分隔,并作为命令行命令的一部分传递给可执行文件。

进程参数的意义:

向程序传递输入:可以通过命令行参数向程序传递不同的输入数据,以便程序根据不同的参数执行不同的操作或处理不同的数据。 配置程序行为:可以使用进程参数来配置程序的行为,例如设置日志级别、指定文件路径、设定运行模式等。

示例:

$ ./my_program arg1 arg2 --option=value

环境变量(Environment Variables): 环境变量是在操作系统中设置的全局变量,可以被所有运行的进程访问。它们是键值对的形式,其中键是环境变量的名称,值是环境变量的内容。环境变量提供了一种在进程之间共享配置和信息的机制。

环境变量的意义:

配置系统参数:环境变量可以用于配置整个系统的行为,例如设置默认的语言、指定默认的编译器、定义系统路径等。 程序运行时的配置:程序可以读取环境变量来进行特定的配置,例如读取数据库连接信息、API密钥等。 进程通信:在多进程或多线程的场景中,环境变量可以作为通信媒介,在进程之间传递信息。

示例:

$ export MY_VAR="Hello World"

通过使用进程参数和环境变量,我们可以在不修改程序代码的情况下,通过外部的输入和配置来改变进程的行为和属性。这是一种灵活和可定制的机制,使我们能够根据需要对程序进行动态的调整和配置。

二、子进程程序结束返回值到哪里去了?

当子进程程序结束时,其返回值会被传递给父进程。父进程可以通过一些机制来获取子进程的返回值,这样可以对子进程的执行结果进行处理。

一种常见的获取子进程返回值的方法是使用系统调用wait()或waitpid()。这些调用会使父进程等待子进程的结束,并获取子进程的返回值。父进程可以通过这个返回值来了解子进程的执行状态,通常返回值为一个整数,用于表示子进程的退出状态。

在父进程中,可以使用以下方法来获取子进程的返回值:

1.使用wait()系统调用:

wait()函数用于使父进程等待其子进程结束,并获取子进程的终止状态。当调用wait()函数时,父进程会暂停执行,直到一个子进程结束。

#include #include pid_t wait(int *status);

wait()函数的参数是一个指向整型的指针status,用于获取子进程的退出状态。通过该指针,父进程可以获取子进程的退出状态码,以判断子进程的执行结果。

wait()函数会阻塞父进程的执行,并等待一个子进程终止。当子进程终止后,父进程会继续执行,并返回被终止的子进程的进程ID。同时,子进程的退出状态码会存储在status指针所指向的位置。

#include #include ... int status; pid_t child_pid = wait(&status); if (WIFEXITED(status)) { int exit_status = WEXITSTATUS(status); // 处理子进程的退出状态 }

2.使用waitpid()系统调用:

waitpid()函数可以用于等待指定的子进程结束,并获取其终止状态。该函数也可以用于处理多个子进程的情况。

#include #include pid_t waitpid(pid_t pid, int *status, int options);

waitpid()函数的第一个参数pid指定了要等待的子进程的进程ID。如果pid为负值,那么waitpid()会等待任意子进程终止。如果pid为0,则等待与调用者位于同一进程组的任意子进程。如果pid为正值,则等待具有指定进程ID的子进程。

waitpid()函数的第二个参数status用于获取子进程的退出状态码。第三个参数options指定额外的选项,比如WNOHANG表示不阻塞父进程,而是立即返回。

waitpid()函数会阻塞父进程的执行,直到指定的子进程终止。当子进程终止后,父进程会继续执行,并返回被终止的子进程的进程ID。同时,子进程的退出状态码会存储在status指针所指向的位置。

#include #include ... int status; pid_t child_pid = waitpid(child_pid, &status, 0); if (WIFEXITED(status)) { int exit_status = WEXITSTATUS(status); // 处理子进程的退出状态 }

在这些方法中,status变量用于存储子进程的退出状态,可以使用宏WIFEXITED(status)来判断子进程是否正常退出,WEXITSTATUS(status)则可以获取子进程的退出状态值。

需要注意的是,子进程的返回值范围通常是一个字节,即0-255之间的整数。如果子进程需要返回更多信息,可以使用其他机制,例如通过标准输出或文件来传递额外的数据。

三、进程退出函数

当一个进程完成其任务或者发生了错误时,可以使用进程退出函数来终止进程的执行。两个常用的进程退出函数是exit()和abort()。

1.exit() exit()函数用于正常终止进程的执行。它接受一个整数参数作为进程的退出状态码,并将控制返回给操作系统。这个状态码可以被其他进程或者父进程通过调用wait()或waitpid()来获取。

#include void exit(int status);

exit()函数会执行以下操作:

调用通过atexit()函数注册的退出处理程序(exit handlers)。

关闭已打开的文件描述符。

刷新标准I/O缓冲区。

解除所有由进程分配的内存。

返回到操作系统,并传递退出状态码。

退出状态码可以是任意的整数值,一般情况下非零的状态码表示进程执行失败,而0表示成功。

2.abort() abort()函数用于异常终止进程的执行。当调用abort()函数时,进程会向操作系统发送终止信号(SIGABRT),导致进程异常终止。与exit()函数不同,abort()函数不接受任何参数。

#include void abort(void);

abort()函数会执行以下操作:

强制将进程终止,不执行后续的代码。

调用通过atexit()函数注册的退出处理程序。

使用默认的终止信号(SIGABRT)向操作系统发送终止请求。

abort()函数通常用于处理严重错误或异常情况,例如发生内存错误或者其他无法恢复的错误。它会生成一个包含调试信息的core dump文件,方便开发人员进行调试和错误分析。

总结:exit()函数用于正常终止进程的执行,而abort()函数用于异常终止进程的执行。它们在进程退出时执行不同的操作,但都是用于终止进程的执行。

四、实际使用案例 #include #include #include #include #include int main(int argc, char** argv, char** envp) { pid_t pid = 0; int a = 1; int b = 0; int status = 0; printf("parent :%d\n", getpid()); if((pid = fork()) == 0) exit(0); printf("child = %d\n", pid); if((pid = fork()) == 0) abort(); printf("child = %d\n", pid); if((pid = fork()) == 0) a / b, exit(1); printf("child = %d\n", pid); sleep(20); while((pid = wait(&status)) > 0) { printf("child pid :%d status :%d\n", pid, status); } return 0; }

运行代码后使用ps查看进程情况: 在这里插入图片描述

sleep后再查看进程情况: 在这里插入图片描述 为什么延时后被创建出来的线程就不见了呢,我们下面来讲解。

五、僵尸进程

当一个进程终止时,它的一部分信息需要被保留,以便父进程在需要时查询和处理该子进程的退出状态。此时,子进程变成了一种中间状态,称为"僵尸进程"(Zombie process)或"defunct"进程。

僵尸进程的产生主要是因为父进程没有及时处理子进程的退出状态。在父进程调用wait()或waitpid()等函数之前,子进程的进程描述符和一些资源(如打开的文件描述符)都被维护在系统中,以备父进程查询使用。

僵尸进程的存在不是一个严重的问题,因为内核会回收子进程其他的资源(如内存),但僵尸进程会占用一定的系统资源(主要是进程表项),如果过多的僵尸进程积累,可能会消耗过多的系统资源。

解决僵尸进程的常见方法是父进程及时处理子进程的退出状态,即调用wait()或waitpid()等函数来回收子进程的资源,并从进程表中删除子进程的记录。这样子进程就会完全终止并从系统中清除。

有时,如果父进程已经终止,或者父进程没有合适的处理子进程的代码,僵尸进程可能会长时间存在。在这种情况下,可以通过重新启动父进程、修复父进程的代码或使用一些系统工具来清除僵尸进程。

总结:僵尸进程是指已经终止的子进程,但其父进程尚未及时处理子进程的退出状态。它们处于一种临时的中间状态,不再执行任何代码,但仍然占用一些系统资源。及时处理子进程的退出状态是预防和解决僵尸进程问题的关键。

总结

本篇文章就讲解到这里,下一篇文章我们继续分析多进程编程。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有