如何使 Linux 进程与父 shell 分离

LinuxLinuxBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本教程全面概述了 Linux 进程的基础知识,包括如何使进程与父 shell 分离以及如何处理 Linux 进程中的信号。对于系统管理员、开发人员以及任何使用基于 Linux 的系统的人来说,理解这些概念至关重要。通过本教程的学习,你将扎实掌握 Linux 进程的关键方面,并能够将其应用到自己的项目和工作流程中。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL linux(("Linux")) -.-> linux/ProcessManagementandControlGroup(["Process Management and Control"]) linux/ProcessManagementandControlGroup -.-> linux/jobs("Job Managing") linux/ProcessManagementandControlGroup -.-> linux/bg_running("Background Running") linux/ProcessManagementandControlGroup -.-> linux/fg("Job Foregrounding") linux/ProcessManagementandControlGroup -.-> linux/kill("Process Terminating") linux/ProcessManagementandControlGroup -.-> linux/killall("Multi-Process Killing") linux/ProcessManagementandControlGroup -.-> linux/pkill("Pattern-Based Killing") linux/ProcessManagementandControlGroup -.-> linux/wait("Process Waiting") linux/ProcessManagementandControlGroup -.-> linux/bg_process("Background Management") subgraph Lab Skills linux/jobs -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/bg_running -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/fg -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/kill -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/killall -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/pkill -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/wait -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} linux/bg_process -.-> lab-426181{{"如何使 Linux 进程与父 shell 分离"}} end

Linux 进程基础

在 Linux 操作系统中,进程是执行的基本单元。进程是正在运行的程序的一个实例,它代表了一个应用程序从开始到结束的整个生命周期。对于系统管理员、开发人员以及任何使用基于 Linux 系统的人来说,理解 Linux 进程的基础知识至关重要。

进程基础

Linux 中的进程由唯一的进程 ID(PID)标识,PID 是内核分配的整数值。每个进程都有自己的内存空间、文件描述符以及操作系统分配的其他资源。根据其当前活动以及用户或系统执行的操作,进程可以处于不同的状态,例如运行、睡眠、停止或僵尸状态。

进程生命周期

Linux 进程的生命周期通常包括以下阶段:

  1. 创建:使用 fork() 系统调用创建一个新进程,该调用会创建当前进程的一个副本。
  2. 执行:新进程执行自己的代码,这可能涉及系统调用、文件 I/O 以及其他操作。
  3. 终止:进程可以通过调用 exit() 函数自愿终止,或者通过接收来自系统或其他进程的信号非自愿终止。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        printf("子进程 (PID: %d)\n", getpid());
    } else if (pid > 0) {
        // 父进程
        printf("父进程 (PID: %d)\n");
        wait(NULL);
    } else {
        // Fork 失败
        printf("Fork 失败\n");
    }
    return 0;
}

这段代码演示了使用 fork() 系统调用创建子进程的过程。子进程打印自己的进程 ID,父进程使用 wait() 函数等待子进程终止。

进程管理

Linux 提供了各种用于管理进程的工具和系统调用,例如 pstopkillwaitpid()。这些工具和函数允许你查看正在运行的进程的状态、终止或向进程发送信号,以及等待子进程完成。

通过理解 Linux 进程的基础知识,你可以编写更健壮、高效的应用程序,自动化系统任务,并解决与进程管理相关的问题。

使进程与父 shell 分离

在 Linux 中,常常需要使进程与父 shell 分离,以便它能够独立运行,即使父进程终止后也能继续执行。这对于长时间运行的任务、后台服务或守护进程特别有用,这些进程需要在不依赖特定终端会话的情况下运行。

进程分离

将子进程与父 shell 分离的过程称为“守护进程化”。这涉及几个步骤:

  1. 创建子进程:父进程使用 fork() 系统调用创建一个子进程。
  2. 与父进程分离:子进程调用 setsid() 成为新会话的会话组长和新进程组的进程组长。这有效地将子进程与父 shell 分离。
  3. 更改工作目录:子进程将其工作目录更改为根目录(/),以确保它不依赖于特定目录。
  4. 重定向标准流:子进程将其标准输入、输出和错误流重定向到 /dev/null,以避免与终端会话产生任何潜在问题。
  5. 终止父进程:父进程终止,使子进程独立运行。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>

int main() {
    pid_t pid = fork();
    if (pid == 0) {
        // 子进程
        setsid(); // 成为会话组长
        chdir("/"); // 更改工作目录
        freopen("/dev/null", "r", stdin);
        freopen("/dev/null", "w", stdout);
        freopen("/dev/null", "w", stderr);

        // 在此处执行长时间运行的任务
        while (1) {
            // 执行某些操作
        }
    } else if (pid > 0) {
        // 父进程
        printf("父进程 (PID: %d)\n", getpid());
        exit(0);
    } else {
        // Fork 失败
        printf("Fork 失败\n");
        exit(1);
    }
    return 0;
}

这段代码演示了将子进程与父 shell 分离的过程。子进程调用 setsid() 成为会话组长,将其工作目录更改为根目录,并将其标准流重定向到 /dev/null。然后父进程终止,使子进程在后台独立运行。

通过使进程与父 shell 分离,你可以确保即使用户注销或终端会话关闭,关键任务或服务仍能继续运行。这是 Linux 系统守护进程和后台进程开发中常用的技术。

在 Linux 进程中处理信号

在 Linux 操作系统中,进程通过信号相互通信以及与内核进行交互。信号是软件生成的中断,用于通知进程有需要关注的事件或状况。了解如何在 Linux 进程中处理信号对于构建健壮且可靠的应用程序至关重要。

信号基础

Linux 中的信号由一个数值标识,每个信号代表一种特定类型的事件或状况。例如,当用户按下 Ctrl+C 时会发送 SIGINT 信号,而发送 SIGTERM 信号则是请求终止一个进程。

进程可以通过注册信号处理程序来处理信号,信号处理程序是在接收到特定信号时执行的函数。通过实现信号处理程序,进程能够以可控且可预测的方式对信号做出响应。

信号处理示例

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

void signal_handler(int signum) {
    printf("Received signal %d\n", signum);
    if (signum == SIGINT) {
        printf("Terminating process...\n");
        exit(0);
    }
}

int main() {
    // 为 SIGINT(Ctrl+C)注册信号处理程序
    signal(SIGINT, signal_handler);

    // 为 SIGTERM(终止请求)注册信号处理程序
    signal(SIGTERM, signal_handler);

    // 模拟长时间运行的任务
    while (1) {
        printf("Running...\n");
        sleep(1);
    }

    return 0;
}

在此示例中,进程为 SIGINTSIGTERM 信号注册了信号处理程序。当用户按下 Ctrl+C 或进程收到终止请求时,会调用 signal_handler() 函数,该函数会打印一条消息,并在接收到 SIGINT 信号时终止进程。

通过处理信号,进程能够以可控的方式响应外部事件和状况,从而实现更健壮、可靠的应用程序行为。

总结

在本教程中,我们探讨了 Linux 进程的基本概念,包括进程基础、生命周期和管理。我们还学习了如何使进程与父 shell 分离以及如何在 Linux 进程中处理信号。这些技能对于在基于 Linux 的环境中进行有效的系统管理、进程自动化和故障排除至关重要。通过掌握这些技术,你可以简化工作流程,提高应用程序的可靠性,并提升你在使用 Linux 系统方面的整体熟练度。