进程基础与系统调用详解

什么是进程?

进程是操作系统进行资源分配和调度的基本单位,它代表了正在运行的程序。与程序不同,进程是动态的,它包括代码、数据以及分配给它的其他系统资源,如文件描述符、网络连接等。例如,我们打开的VMWare、浏览器等软件,都对应操作系统中的一个进程。

使用system函数生成子进程

system函数是标准库中用于执行shell指令的函数。它会启动一个shell子进程来执行传入的命令。例如,我们可以使用system("ping -c 10 www.baidui.com")来向www.baidu.com发送10个ICMP回声请求。在子进程执行过程中,我们可以通过ps -ef命令查看所有进程,定位刚刚启动的进程及其父子关系。

进程处理相关系统调用

fork

fork系统调用用于创建一个子进程,它会复制父进程的资源,包括内存空间。fork的返回值在父进程中是子进程的PID,在子进程中是0,如果出错则返回-1。通过fork,我们可以实现进程的复制和并发执行。

execve

execve系列函数用于在同一个进程中跳转执行另外一个程序。它会替换当前进程的代码、数据和堆栈等资源,但保留进程ID。例如,我们可以通过execve("/home/fish/process_test/erlou", args, envs)来在当前进程中执行另一个程序erlou。

waitpid

waitpid系统调用用于父进程等待子进程的终止并获取子进程的退出状态。它可以帮助父进程回收子进程的资源,避免子进程变成僵尸进程。例如,waitpid(pid, &subprocess_status, 0)会一直挂起父进程的执行,直至子进程pid终止。

进程树

Linux的进程通过父子关系组织起来,构成进程树。每个进程都是其上级节点的子进程,同时又是子结点的父进程。进程树的根节点是操作系统的第一个进程,即PID为1的进程,它负责初始化系统,启动其他所有用户空间的服务和进程。

孤儿进程

孤儿进程是指父进程已结束或终止,而它仍在运行的进程。当父进程结束之前没有等待子进程结束,且父进程先于子进程结束时,子进程就会变成孤儿进程。孤儿进程会被其祖先自动领养,继续运行直到完成任务或被系统回收。

waitpid系统调用

waitpid的定义与作用

waitpid系统调用用于父进程等待子进程的终止,并获取子进程的退出状态。它可以帮助父进程回收子进程的资源,避免子进程变成僵尸进程。waitpid的原型如下:

pid_t waitpid(pid_t pid, int *wstatus, int options);
  • 参数
    • pid:等待的子进程ID。小于-1时,等待进程组ID等于-pid的所有进程终止;等于-1时,等待任何子进程终止;等于0时,等待同一进程组中任何子进程终止;大于0时,仅等待指定进程ID的子进程终止。
    • wstatus:指向整数的指针,用于存储子进程的退出状态。
    • options:选项参数,可以设置为WNOHANG(如果没有子进程终止,也立即返回)、WUNTRACED(收到子进程处于收到信号停止的状态,也返回)等。
  • 返回值:成功时返回子进程的PID;如果没有子进程终止且设置了WNOHANG,返回0;出错时返回-1。
waitpid的使用示例

下面是一个使用waitpid系统调用的示例代码:

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

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        // 创建新进程失败
        perror("fork");
        return 1;
    } else if (pid == 0) {
        // 子进程代码
        printf("子进程PID: %d\n", getpid());
        // 执行一些操作,例如sleep
        sleep(2);
        // 子进程退出
        exit(0);
    } else {
        // 父进程代码
        printf("父进程PID: %d, 等待子进程PID: %d\n", getpid(), pid);
        int wstatus;
        waitpid(pid, &wstatus, 0);
        if (WIFEXITED(wstatus)) {
            printf("子进程正常退出,退出状态码: %d\n", WEXITSTATUS(wstatus));
        } else if (WIFSIGNALED(wstatus)) {
            printf("子进程被信号终止,信号编号: %d\n", WTERMSIG(wstatus));
        }
    }
    return 0;
}

在这个示例中,我们使用fork()创建一个子进程,然后父进程使用waitpid(pid, &wstatus, 0)等待子进程终止。wstatus变量用于存储子进程的退出状态,我们可以通过宏WIFEXITEDWEXITSTATUS来判断子进程是否正常退出以及获取退出状态码。

waitpid的执行过程分析

当调用waitpid系统调用时,它会执行以下步骤:

  1. 等待子进程终止:父进程会挂起执行,等待指定的子进程终止。如果设置了WNOHANG选项,父进程不会挂起,而是立即返回。
  2. 获取子进程退出状态:当子进程终止时,waitpid会获取子进程的退出状态,并将其存储在wstatus指针指向的整数中。
  3. 回收子进程资源:父进程通过waitpid回收子进程的资源,避免子进程变成僵尸进程。
waitpid的注意事项
  • 避免僵尸进程:父进程应及时调用waitpid回收子进程的资源,避免子进程变成僵尸进程,占用系统资源。
  • 错误处理:如果waitpid执行失败,会返回-1,并设置errno变量以指示错误原因。例如,如果指定的子进程ID不存在,errno会被设置为ECHILD
  • 多子进程处理:如果父进程有多个子进程,可以通过循环调用waitpid来等待所有子进程的终止,并分别获取它们的退出状态。

进程树

进程树的概念与结构

进程树是Linux系统中进程的组织结构,它通过父子关系将所有进程连接起来。进程树中的每个节点代表一个进程,每个进程都是其上级节点的子进程,同时又是子结点的父进程。进程树的根节点是操作系统的第一个进程,即PID为1的进程,它负责初始化系统,启动其他所有用户空间的服务和进程。

进程树的查看方法

  • ps命令:使用ps -ef命令可以查看系统中所有进程的信息,包括进程的PID、父进程PID、用户ID、优先级、虚拟内存使用情况、物理内存使用情况、CPU时间等。通过ps -ef命令的输出结果,我们可以追溯进程的父子关系。
  • pstree命令:使用pstree命令可以以树状图的形式展示所有用户进程的依赖关系。pstree命令会从根节点开始,逐级展示进程树的结构,使得进程之间的父子关系一目了然。例如,pstree -p命令会显示进程号,方便我们查看进程的具体信息。

进程树的应用场景

  • 系统监控:通过查看进程树,系统管理员可以了解系统中进程的运行情况,发现潜在的问题。例如,如果某个进程占用了大量的系统资源,可以通过进程树找到其父进程,分析问题的原因。
  • 故障排查:当系统出现故障时,查看进程树可以帮助我们快速定位故障源头。例如,如果某个服务进程崩溃了,可以通过进程树找到与其相关的其他进程,分析它们的状态和日志,找出故障的原因。
  • 性能优化:通过分析进程树,可以发现系统中不必要的进程或资源占用过多的进程,进行相应的优化。例如,如果发现某个进程的子进程过多,可以考虑减少子进程的数量,或者优化子进程的资源使用,提高系统的整体性能。

孤儿进程

孤儿进程的定义与产生原因

孤儿进程是指父进程已结束或终止,而它仍在运行的进程。孤儿进程的产生原因主要有以下几点:

  • 父进程先于子进程结束:如果父进程在子进程之前结束,且没有等待子进程的终止,子进程就会变成孤儿进程。
  • 父进程异常退出:如果父进程由于异常原因(如程序错误、系统崩溃等)退出,而子进程仍在运行,子进程也会变成孤儿进程。

孤儿进程的处理机制

在Linux系统中,孤儿进程会被其祖先自动领养。具体来说,当父进程结束时,孤儿进程会被PID为1的进程(通常是systemdinit进程)领养。领养后的孤儿进程会继续运行,直到完成任务或被系统回收。例如,如果一个父进程创建了一个子进程,然后父进程退出,子进程会被systemd进程领养,继续运行。

孤儿进程的影响与注意事项

  • 资源占用:孤儿进程仍然会占用系统资源,如内存、CPU等。如果孤儿进程过多,可能会导致系统资源紧张,影响系统的性能。
  • 日志记录:孤儿进程的日志记录可能会受到影响,因为它们与原始父进程的联系已经切断。在某些情况下,可能难以追踪孤儿进程的日志信息,给问题排查带来困难。
  • 避免孤儿进程的建议:在编写程序时,要注意避免产生孤儿进程。可以通过以下方法来避免:
    • 等待子进程终止:父进程在结束之前,应该使用waitwaitpid等系统调用来等待子进程的终止,并回收子进程的资源。
    • 设置信号处理:父进程可以设置信号处理函数,在接收到特定信号时,执行子进程的清理工作,确保子进程能够正常退出。
    • 合理设计程序逻辑:在程序设计时,要合理安排父子进程的执行顺序和逻辑,避免父进程提前结束或异常退出,导致子进程变成孤儿进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值