【Linux操作系统】守护进程的创建和逐步实现代码分析

在Unix和Linux系统中,守护进程是一种在后台运行的进程,它独立于终端并且能够持续执行某些任务。创建守护进程是一项常见的任务,它可以用于各种场景,如网络服务、定时任务等。本篇博客将介绍如何在C语言中创建守护进程,并提供了一些示例代码。通过学习本篇博客,你将了解到创建守护进程的一般步骤和注意事项,以及如何编写守护进程的主要逻辑。无论你是初学者还是有一定经验的开发者,本篇博客都将为你提供有用的指导,帮助你创建可靠的守护进程。

在这里插入图片描述


前提知识点

  1. 进程和进程控制:了解进程的基本概念和进程控制相关的系统调用,如forkexecwait等。
  2. 文件描述符:理解文件描述符的概念和作用,以及相关的系统调用,如openclosedup等。
  3. 信号处理:了解信号的概念和信号处理的机制,包括信号的发送、接收和处理等。
  4. 文件权限和文件系统:掌握Linux文件权限的概念和设置方法,以及文件系统的基本操作。

这部分所有的知识都不做解释了,我之前的博客都有涉及到的,可以去翻一下,这个博客主要说守护进程:
 进程的创建共享和管理
 文件描述符
 信号描述
 文件权限和文件系统


创建守护进程的步骤

  1. 创建子进程,并通过fork系统调用复制父进程的文件描述符表和信号处理函数。
  2. 在子进程中调用setsid系统调用创建新的会话,并成为会话的首进程。
  3. 关闭不再需要的文件描述符,如标准输入、标准输出和标准错误输出等。
  4. 修改工作目录为根目录,以防止守护进程占用文件系统。
  5. 重设文件权限掩码,以便守护进程创建文件时具有合适的权限。
  6. 处理信号,如SIGCHLDSIGHUP等,可以选择忽略或自定义处理函数。
  7. 运行守护进程的主要逻辑,如服务监听、数据处理等。
  8. 退出守护进程。

守护进程创建

下面我们将逐步介绍每个步骤的具体实现和相应的代码。

1. 创建子进程并复制文件描述符和信号处理函数

首先,我们需要创建一个子进程,并通过fork系统调用复制父进程的文件描述符表和信号处理函数。

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

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

在上面的代码中,我们通过fork系统调用创建了一个子进程,并在父进程中直接退出,使子进程成为孤儿进程。在子进程中,我们通过signal函数将SIGCHLD信号的处理函数设置为signal_handler函数。


2. 创建新的会话并成为会话的首进程

接下来,我们需要在子进程中调用setsid系统调用创建新的会话,并成为会话的首进程:

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

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

在上面的代码中,我们通过setsid系统调用创建了一个新的会话,并使子进程成为该会话的首进程。这样做可以使守护进程与终端脱离关系,不受终端关闭的影响。


3. 关闭不再需要的文件描述符

在守护进程中,我们需要关闭不再需要的文件描述符,如标准输入、标准输出和标准错误输出等。

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

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

在上面的代码中,我们使用close系统调用关闭了标准输入、标准输出和标准错误输出的文件描述符。


4. 修改工作目录为根目录

为了防止守护进程占用文件系统,我们需要将工作目录修改为根目录。

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

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
        perror("chdir error");
        exit(1);
    }
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

在上面的代码中,我们使用chdir系统调用将工作目录修改为根目录。


5. 重设文件权限掩码

为了使守护进程创建文件时具有合适的权限,我们需要重设文件权限掩码。下面是一个示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

void signal_handler(int signum) {// 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    // ...
    
    return 0;
}

在上面的代码中,我们使用umask系统调用将文件权限掩码重设为0。


6. 执行守护进程的主要逻辑

守护进程的主要逻辑可以根据实际需求进行编写。下面是一个示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    while (1) {
        // 守护进程的具体逻辑
        sleep(1);
    }
    
    return 0;
}

在上面的代码中,我们使用while循环来持续执行守护进程的主要逻辑。在这个示例中,守护进程每隔1秒钟就会执行一次sleep函数,模拟一些需要持续执行的操作。


守护进程创建总代码

下面是将上面创建守护进程的每一部分代码整合成一个完整的代码,并对每一部分进行解释:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>

void signal_handler(int signum) {
    // 信号处理函数的具体实现
}

int main() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork error");
        exit(1);
    } else if (pid > 0) {
        exit(0);  // 父进程退出
    }
    
    // 子进程继续执行
    // 复制信号处理函数
    signal(SIGCHLD, signal_handler);
    
    // 创建新的会话并成为会话的首进程
    if (setsid() < 0) {
        perror("setsid error");
        exit(1);
    }
    
    // 关闭不再需要的文件描述符
    close(STDIN_FILENO);  // 关闭标准输入
    close(STDOUT_FILENO);  // 关闭标准输出
    close(STDERR_FILENO);  // 关闭标准错误输出
    
    // 修改工作目录为根目录
    if (chdir("/") < 0) {
        perror("chdir error");
        exit(1);
    }
    
    // 重设文件权限掩码
    umask(0);
    
    // 子进程的主要逻辑
    while (1) {
        // 守护进程的具体逻辑
        sleep(1);
    }
    
    return 0;
}

代码解释:

  1. fork()函数创建一个子进程,如果返回值小于0,则表示创建子进程失败;如果返回值大于0,则表示当前为父进程,需要退出;如果返回值等于0,则表示当前为子进程,继续执行。
  2. signal()函数复制信号处理函数,将SIGCHLD信号与signal_handler()函数关联起来。
  3. setsid()函数创建新的会话并成为会话的首进程。如果返回值小于0,则表示创建会话失败。
  4. close()函数关闭不再需要的文件描述符,包括标准输入、标准输出和标准错误输出。
  5. chdir()函数修改工作目录为根目录。如果返回值小于0,则表示修改工作目录失败。
  6. umask()函数重设文件权限掩码为0,确保守护进程创建的文件具有合适的权限。
  7. while循环是守护进程的主要逻辑,可以根据实际需求进行编写。在这个示例中,守护进程每隔1秒钟就会执行一次sleep()函数,模拟一些需要持续执行的操作。

总结

本篇博客回答了如何在C语言中创建守护进程的问题。创建守护进程的一般步骤包括:

  1. 使用fork()函数创建子进程,父进程退出,子进程继续执行。
  2. 使用setsid()函数创建新的会话并成为会话的首进程。
  3. 关闭不再需要的文件描述符,包括标准输入、标准输出和标准错误输出。
  4. 修改工作目录为根目录。
  5. 重设文件权限掩码。
  6. 编写守护进程的主要逻辑,并使用while循环持续执行。

需要注意的是,创建守护进程时需要仔细处理信号处理函数、文件描述符、工作目录和文件权限等方面的问题,确保守护进程能够正常运行,并且不会对系统造成不必要的影响。

  • 37
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 37
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入式小白—小黑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值