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

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

前言

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


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

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

进程参数(Process Arguments):

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

进程参数的意义:

向程序传递输入:可以通过命令行参数向程序传递不同的输入数据,以便程序根据不同的参数执行不同的操作或处理不同的数据。

配置程序行为:可以使用进程参数来配置程序的行为,例如设置日志级别、指定文件路径、设定运行模式等。

示例:

   $ ./my_program arg1 arg2 --option=value

环境变量(Environment Variables):

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

环境变量的意义:

配置系统参数:环境变量可以用于配置整个系统的行为,例如设置默认的语言、指定默认的编译器、定义系统路径等。

程序运行时的配置:程序可以读取环境变量来进行特定的配置,例如读取数据库连接信息、API密钥等。

进程通信:在多进程或多线程的场景中,环境变量可以作为通信媒介,在进程之间传递信息。

示例:

   $ export MY_VAR="Hello World"

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


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

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

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

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

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

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

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);

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

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

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

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

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

#include <sys/types.h>
#include <sys/wait.h>
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 <sys/types.h>
#include <sys/wait.h>
...
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 <stdlib.h>
void exit(int status);

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

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

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

刷新标准I/O缓冲区。

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

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

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

2.abort()

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

#include <stdlib.h>
void abort(void);

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

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

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

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

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

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


四、实际使用案例

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
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()等函数来回收子进程的资源,并从进程表中删除子进程的记录。这样子进程就会完全终止并从系统中清除。

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

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


总结

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


相关文章
|
29天前
|
算法 Linux 调度
深入理解Linux操作系统的进程管理
本文旨在探讨Linux操作系统中的进程管理机制,包括进程的创建、执行、调度和终止等环节。通过对Linux内核中相关模块的分析,揭示其高效的进程管理策略,为开发者提供优化程序性能和资源利用率的参考。
66 1
|
18天前
|
存储 缓存 监控
Linux缓存管理:如何安全地清理系统缓存
在Linux系统中,内存管理至关重要。本文详细介绍了如何安全地清理系统缓存,特别是通过使用`/proc/sys/vm/drop_caches`接口。内容包括清理缓存的原因、步骤、注意事项和最佳实践,帮助你在必要时优化系统性能。
151 78
|
22天前
|
Linux Shell 网络安全
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
本指南介绍如何利用 HTA 文件和 Metasploit 框架进行渗透测试。通过创建反向 shell、生成 HTA 文件、设置 HTTP 服务器和发送文件,最终实现对目标系统的控制。适用于教育目的,需合法授权。
54 9
Kali Linux系统Metasploit框架利用 HTA 文件进行渗透测试实验
|
18天前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
83 13
|
25天前
|
SQL 运维 监控
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
南大通用GBase 8a MPP Cluster Linux端SQL进程监控工具
|
1月前
|
运维 监控 Linux
Linux操作系统的守护进程与服务管理深度剖析####
本文作为一篇技术性文章,旨在深入探讨Linux操作系统中守护进程与服务管理的机制、工具及实践策略。不同于传统的摘要概述,本文将以“守护进程的生命周期”为核心线索,串联起Linux服务管理的各个方面,从守护进程的定义与特性出发,逐步深入到Systemd的工作原理、服务单元文件编写、服务状态管理以及故障排查技巧,为读者呈现一幅Linux服务管理的全景图。 ####
|
2月前
|
缓存 算法 Linux
Linux内核的心脏:深入理解进程调度器
本文探讨了Linux操作系统中至关重要的组成部分——进程调度器。通过分析其工作原理、调度算法以及在不同场景下的表现,揭示它是如何高效管理CPU资源,确保系统响应性和公平性的。本文旨在为读者提供一个清晰的视图,了解在多任务环境下,Linux是如何智能地分配处理器时间给各个进程的。
|
18天前
|
Ubuntu Linux C++
Win10系统上直接使用linux子系统教程(仅需五步!超简单,快速上手)
本文介绍了如何在Windows 10上安装并使用Linux子系统。首先,通过应用商店安装Windows Terminal和Linux系统(如Ubuntu)。接着,在控制面板中启用“适用于Linux的Windows子系统”并重启电脑。最后,在Windows Terminal中选择安装的Linux系统即可开始使用。文中还提供了注意事项和进一步配置的链接。
40 0
|
30天前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。
|
6月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能