Linux系统编程之进程控制

概述

在Linux系统中,创建一个新的进程后,如何对该进程进行有效的控制,是一项非常重要的操作。控制进程状态的操作主要包括:进程的执行、进程的等待、进程的终止等。下面,我们将逐个进行介绍。

进程的执行

创建进程后,通常需要让进程执行特定的任务。这可以通过exec系列函数来实现,这些函数会用新的程序替换当前进程的内存映像,即改变当前进程的行为。常见的exec函数有:execl、execlp、execle、execv和execvp等。

1、execl函数使用可变参数列表来传递参数,最后一个参数必须是NULL。它要求提供完整的路径名来执行程序,其函数原型如下。

cpp 复制代码
int execl(const char *path, const char *arg, ...);

path:新程序的完整路径名。

arg:新程序的第一个参数,通常是程序名。

...:其他参数,最后一个参数必须是NULL。

使用execl函数的示例代码如下。

cpp 复制代码
#include <unistd.h>
#include <stdio.h>

int main()
{
    execl("/bin/ls", "ls", "-l", "/home", NULL);
    return 0;
}

2、execlp函数类似于execl,但它会在环境变量PATH中搜索指定的命令,因此不需要提供完整的路径名。其函数原型如下。

cpp 复制代码
int execlp(const char *file, const char *arg, ...);

file:要执行的程序名。

arg:新程序的第一个参数,通常是程序名。

...:其他参数,最后一个参数必须是NULL。

使用execlp函数的示例代码如下。

cpp 复制代码
#include <unistd.h>
#include <stdio.h>

int main()
{
    execlp("ls", "ls", "-l", "/home", NULL);
    return 0;
}

3、execle函数类似于execl,但它允许传递一个环境变量列表,这对于需要在新环境中执行程序的情况非常有用。其函数原型如下。

cpp 复制代码
int execle(const char *path, const char *arg, ..., char *const envp[]);

path:新程序的完整路径名。

arg:新程序的第一个参数,通常是程序名。

...:其他参数,最后一个参数必须是NULL。

envp:环境变量列表,最后一个元素必须是NULL。

使用execle函数的示例代码如下。

cpp 复制代码
#include <unistd.h>
#include <stdio.h>

int main()
{
    char *envp[] = {"PATH=/bin:/usr/bin", NULL};
    execle("/bin/ls", "ls", "-l", "/home", NULL, envp);
    return 0;
}

4、execv函数使用一个数组来传递参数列表,第一个参数是程序的完整路径名。其函数原型如下。

cpp 复制代码
int execv(const char *path, char *const argv[]);

path:新程序的完整路径名。

argv:参数列表,最后一个元素必须是NULL。

使用execv函数的示例代码如下。

cpp 复制代码
#include <unistd.h>
#include <stdio.h>

int main()
{
    char *argv[] = {"ls", "-l", "/home", NULL};
    execv("/bin/ls", argv);
    return 0;
}

5、execvp函数类似于execv,但它会在环境变量PATH中搜索指定的命令,因此不需要提供完整的路径名。其函数原型如下。

cpp 复制代码
int execvp(const char *file, char *const argv[]);

file: 要执行的程序名。

argv: 参数列表,最后一个元素必须是NULL。

使用execvp函数的示例代码如下。

cpp 复制代码
#include <unistd.h>
#include <stdio.h>

int main()
{
    char *argv[] = {"ls", "-l", "/home", NULL};
    execvp("ls", argv);
    return 0;
}

为了方便查看这几个函数的区别,可以参考下面的思维导图。

进程的等待

wait和waitpid函数用于等待子进程的结束,并获取子进程的状态信息。这两个函数在父进程中非常有用,可以防止子进程成为僵尸进程。

1、wait函数等待任意一个子进程结束,并返回该子进程的PID。其函数原型如下。

cpp 复制代码
pid_t wait(int *status);

status:指向一个整型变量的指针,用于存储子进程的退出状态。如果不需要获取状态信息,可以传递NULL。

返回值:成功时,返回已终止子进程的PID。如果没有子进程存在,则返回-1,并设置errno为ECHILD。

使用wait函数的示例代码如下。

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        // 子进程
        execlp("ls", "ls", "-l", "/home", NULL);
        perror("execlp failed");
        exit(1);
    }
    else if (pid > 0)
    {
        // 父进程
        int status;
        pid_t cpid = wait(&status);
        if (WIFEXITED(status))
        {
            printf("Child process %d exited with status %d\n", cpid , WEXITSTATUS(status));
        }
        else if (WIFSIGNALED(status))
        {
            printf("Child process %d terminated by signal %d\n", cpid , WTERMSIG(status));
        }
    }
    else
    {
        perror("fork failed");
        return 1;
    }

    return 0;
}

2、waitpid函数等待指定的子进程结束,并返回该子进程的PID。它比wait函数提供了更多的灵活性,比如:可以选择非阻塞等待。其函数原型如下。

cpp 复制代码
pid_t waitpid(pid_t pid, int *status, int options);

pid:指定要等待的子进程的ID。如果pid > 0,则等待进程ID等于pid的子进程。如果pid == 0,则等待任何其组ID等于调用进程的组ID的子进程。如果 pid < -1,则等待任何其组ID等于-pid的子进程。如果pid == -1,则等待任何子进程,这是最常用的情况。

status:指向一个整型变量的指针,用于存储子进程的退出状态。如果不需要获取状态信息,可以传递NULL。

options:可以用来改变waitpid的行为,常用的选项如下。

(1)WNOHANG:如果没有子进程已经退出,则立即返回,不挂起调用进程。

(2)WUNTRACED:如果有子进程已经停止运行,但未被跟踪,则返回该子进程的信息。

(3)WCONTINUED:如果有子进程已经从暂停状态恢复运行,则返回该子进程的信息,仅在某些系统上可用。

返回值:如果成功等待到一个子进程,将返回该子进程的PID。如果设置了WNOHANG选项,并且没有已退出的子进程可等待,将返回0。如果没有匹配的子进程,或调用失败,将返回-1,可通过errno来确定错误的原因。

进程的终止

kill函数用于向一个或多个进程发送信号,这些信号可以用来控制进程的行为,比如:终止进程、暂停进程等。最常用的是:发送SIGTERM信号来请求进程正常终止,或者发送SIGKILL信号强制终止进程。其函数原型如下。

cpp 复制代码
int kill(pid_t pid, int sig);

pid:目标进程的进程ID。如果pid > 0,则信号将发送给进程ID为 pid 的进程。如果pid == 0,则信号将发送给所有与调用进程属于同一个进程组的进程。如果pid == -1,则信号将发送给所有除了发起该调用的进程以外的所有进程。如果pid < -1,则信号将发送给所有进程组ID为-pid的进程。

sig:要发送的信号编号,常见的信号如下。

(1)SIGTERM:请求进程优雅地终止。

(2)SIGKILL:强制终止进程。

(3)SIGINT:中断信号,通常是通过按下Ctrl + C发送的。

返回值:如果成功发送信号,将返回0。如果发生错误,将返回-1,并设置errno来指示错误类型。

相关推荐
Soneswy11 小时前
Linux系统篇--重定向与文件操作命令
linux
有毒的教程11 小时前
Linux底层存储路由配
linux·运维·网络
Linux系统运维工程师_郭主任12 小时前
运维人还在被拒offer?现在能考的,只有 RHCE 10.0。
linux
自养Agent12 小时前
MCP Server Boundary Audit Report
linux·服务器·人工智能·windows·大模型·llm
Coisinier13 小时前
docker命令管理镜像及容器
linux·运维·nginx·docker·容器·操作系统
A心有千千结14 小时前
通过 SSH 反向代理让服务器借用本机网络访问 GitHub 的最佳实践
linux·服务器·网络·git·ssh·github
书生执笔画浮沉14 小时前
Linux 内核模块为什么不能自己解决兼容性问题?
linux·c·kernel·update·灵感
15Moonlight14 小时前
Linux网络(03):Socket 编程 TCP
linux·网络·tcp/ip
广州灵眸科技有限公司15 小时前
单芯片同时跑 Linux 与 RT-Thread:RV1126B的AMP异构方案完整拆解
linux·运维·服务器
小雪崩15 小时前
嵌入式学习:Linux驱动开发总结
linux·驱动开发·学习