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

﹏ヽ暗。殇╰゛Y 2024-03-24 18:58 52阅读 0赞

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

在这里插入图片描述

文章目录

    • 前提知识点
    • 创建守护进程的步骤
    • 守护进程创建
        1. 创建子进程并复制文件描述符和信号处理函数
        1. 创建新的会话并成为会话的首进程
        1. 关闭不再需要的文件描述符
        1. 修改工作目录为根目录
        1. 重设文件权限掩码
        1. 执行守护进程的主要逻辑
    • 守护进程创建总代码
    • 总结

前提知识点

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

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


创建守护进程的步骤

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

守护进程创建

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

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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. void signal_handler(int signum) {
  6. // 信号处理函数的具体实现
  7. }
  8. int main() {
  9. pid_t pid = fork();
  10. if (pid < 0) {
  11. perror("fork error");
  12. exit(1);
  13. } else if (pid > 0) {
  14. exit(0); // 父进程退出
  15. }
  16. // 子进程继续执行
  17. // 复制信号处理函数
  18. signal(SIGCHLD, signal_handler);
  19. // 子进程的主要逻辑
  20. // ...
  21. return 0;
  22. }

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


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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. void signal_handler(int signum) {
  6. // 信号处理函数的具体实现
  7. }
  8. int main() {
  9. pid_t pid = fork();
  10. if (pid < 0) {
  11. perror("fork error");
  12. exit(1);
  13. } else if (pid > 0) {
  14. exit(0); // 父进程退出
  15. }
  16. // 子进程继续执行
  17. // 复制信号处理函数
  18. signal(SIGCHLD, signal_handler);
  19. // 创建新的会话并成为会话的首进程
  20. if (setsid() < 0) {
  21. perror("setsid error");
  22. exit(1);
  23. }
  24. // 子进程的主要逻辑
  25. // ...
  26. return 0;
  27. }

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


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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. void signal_handler(int signum) {
  6. // 信号处理函数的具体实现
  7. }
  8. int main() {
  9. pid_t pid = fork();
  10. if (pid < 0) {
  11. perror("fork error");
  12. exit(1);
  13. } else if (pid > 0) {
  14. exit(0); // 父进程退出
  15. }
  16. // 子进程继续执行
  17. // 复制信号处理函数
  18. signal(SIGCHLD, signal_handler);
  19. // 创建新的会话并成为会话的首进程
  20. if (setsid() < 0) {
  21. perror("setsid error");
  22. exit(1);
  23. }
  24. // 关闭不再需要的文件描述符
  25. close(STDIN_FILENO); // 关闭标准输入
  26. close(STDOUT_FILENO); // 关闭标准输出
  27. close(STDERR_FILENO); // 关闭标准错误输出
  28. // 子进程的主要逻辑
  29. // ...
  30. return 0;
  31. }

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


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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. void signal_handler(int signum) {
  6. // 信号处理函数的具体实现
  7. }
  8. int main() {
  9. pid_t pid = fork();
  10. if (pid < 0) {
  11. perror("fork error");
  12. exit(1);
  13. } else if (pid > 0) {
  14. exit(0); // 父进程退出
  15. }
  16. // 子进程继续执行
  17. // 复制信号处理函数
  18. signal(SIGCHLD, signal_handler);
  19. // 创建新的会话并成为会话的首进程
  20. if (setsid() < 0) {
  21. perror("setsid error");
  22. exit(1);
  23. }
  24. // 关闭不再需要的文件描述符
  25. close(STDIN_FILENO); // 关闭标准输入
  26. close(STDOUT_FILENO); // 关闭标准输出
  27. close(STDERR_FILENO); // 关闭标准错误输出
  28. // 修改工作目录为根目录
  29. if (chdir("/") < 0) {
  30. perror("chdir error");
  31. exit(1);
  32. }
  33. // 子进程的主要逻辑
  34. // ...
  35. return 0;
  36. }

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


5. 重设文件权限掩码

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. void signal_handler(int signum) {
  8. // 信号处理函数的具体实现
  9. }
  10. int main() {
  11. pid_t pid = fork();
  12. if (pid < 0) {
  13. perror("fork error");
  14. exit(1);
  15. } else if (pid > 0) {
  16. exit(0); // 父进程退出
  17. }
  18. // 子进程继续执行
  19. // 复制信号处理函数
  20. signal(SIGCHLD, signal_handler);
  21. // 创建新的会话并成为会话的首进程
  22. if (setsid() < 0) {
  23. perror("setsid error");
  24. exit(1);
  25. }
  26. // 关闭不再需要的文件描述符
  27. close(STDIN_FILENO); // 关闭标准输入
  28. close(STDOUT_FILENO); // 关闭标准输出
  29. close(STDERR_FILENO); // 关闭标准错误输出
  30. // 修改工作目录为根目录
  31. if (chdir("/") < 0) {
  32. perror("chdir error");
  33. exit(1);
  34. }
  35. // 重设文件权限掩码
  36. umask(0);
  37. // 子进程的主要逻辑
  38. // ...
  39. return 0;
  40. }

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


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

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. void signal_handler(int signum) {
  8. // 信号处理函数的具体实现
  9. }
  10. int main() {
  11. pid_t pid = fork();
  12. if (pid < 0) {
  13. perror("fork error");
  14. exit(1);
  15. } else if (pid > 0) {
  16. exit(0); // 父进程退出
  17. }
  18. // 子进程继续执行
  19. // 复制信号处理函数
  20. signal(SIGCHLD, signal_handler);
  21. // 创建新的会话并成为会话的首进程
  22. if (setsid() < 0) {
  23. perror("setsid error");
  24. exit(1);
  25. }
  26. // 关闭不再需要的文件描述符
  27. close(STDIN_FILENO); // 关闭标准输入
  28. close(STDOUT_FILENO); // 关闭标准输出
  29. close(STDERR_FILENO); // 关闭标准错误输出
  30. // 修改工作目录为根目录
  31. if (chdir("/") < 0) {
  32. perror("chdir error");
  33. exit(1);
  34. }
  35. // 重设文件权限掩码
  36. umask(0);
  37. // 子进程的主要逻辑
  38. while (1) {
  39. // 守护进程的具体逻辑
  40. sleep(1);
  41. }
  42. return 0;
  43. }

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


守护进程创建总代码

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

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <signal.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. void signal_handler(int signum) {
  8. // 信号处理函数的具体实现
  9. }
  10. int main() {
  11. pid_t pid = fork();
  12. if (pid < 0) {
  13. perror("fork error");
  14. exit(1);
  15. } else if (pid > 0) {
  16. exit(0); // 父进程退出
  17. }
  18. // 子进程继续执行
  19. // 复制信号处理函数
  20. signal(SIGCHLD, signal_handler);
  21. // 创建新的会话并成为会话的首进程
  22. if (setsid() < 0) {
  23. perror("setsid error");
  24. exit(1);
  25. }
  26. // 关闭不再需要的文件描述符
  27. close(STDIN_FILENO); // 关闭标准输入
  28. close(STDOUT_FILENO); // 关闭标准输出
  29. close(STDERR_FILENO); // 关闭标准错误输出
  30. // 修改工作目录为根目录
  31. if (chdir("/") < 0) {
  32. perror("chdir error");
  33. exit(1);
  34. }
  35. // 重设文件权限掩码
  36. umask(0);
  37. // 子进程的主要逻辑
  38. while (1) {
  39. // 守护进程的具体逻辑
  40. sleep(1);
  41. }
  42. return 0;
  43. }

代码解释:

  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循环持续执行。

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

发表评论

表情:
评论列表 (有 0 条评论,52人围观)

还没有评论,来说两句吧...

相关阅读

    相关 守护进程创建

    1.概念 守护进程也称精灵进程,是在后台运行的一种特殊进程,它脱离控制终端并且周期性的执行某种任务或者等待某种事件的发生,脱离终端是为了避免进程在执行过程中的信息在任何终端上

    相关 创建守护进程

    创建子进程,父进程退出   这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成一程序已经运行完毕的假象。之后的所有工作都