当前位置: 首页 > news >正文

Linux学习——守护进程编程

一、守护进程含义及实现过程

1、含义

        守护进程(Daemon Process) 是操作系统中一种在后台长期运行的特殊进程,通常不与用户直接交互。它独立于控制终端,用于执行周期性任务或系统服务(如日志管理、网络服务等)。典型的守护进程包括 httpd(Web 服务)、mysqld(数据库服务)等。

2、实现过程

1、守护进程相关概念
        进程组:一个或多个进程的集合,进程组由进程组ID标识,进程组长的进程ID和进程组ID一致,并且进程组ID不会由于进程组长的退出而受到影响。
        会话周期:一个或多个进程组的集合,比如用户从登陆到退出,这个期间用户运行的所有进程都属于该会话周期。
        setsid函数:创建一个新会话,并担任该会话组的组长,调用setsid函数的目的:让进程摆脱原会话,原进程组,原终端的控制。如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。
2、创建守护进程的过程
        (1)fork()创建子进程,父进程exit()退出;
        这是创建守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在Shell终端里造成程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在Shell终端里则可以执行其他命令,从而在形式上做到了与控制终端的脱离,在后台工作。

        (2)在子进程中调用 setsid() 函数创建新的会话;
        在调用了fork()函数后,子进程全盘拷贝了父进程的会话期、进程组、控制终端等,虽然父进程退出了,但会话期、进程组、控制终端等并没有改变,因此,这还不是真正意义上的独立开来,而 setsid() 函数能够使进程完全独立出来。

        (3)再次 fork() 一个孙进程并让子进程退出;
        为什么要再次fork呢,假定有这样一种情况,之前的父进程fork出子进程以后还有别的事情要做,在做事情的过程中因为某种原因阻塞了,而此时的子进程因为某些非正常原因要退出的话,就会形成僵尸进程,所以由子进程fork出一个孙进程以后立即退出,孙进程作为守护进程会被init接管,此时无论父进程想做什么都随它了。

        (4)在孙进程中调用 chdir() 函数,让根目录 ”/” 成为孙进程的工作目录;

         这一步也是必要的步骤,使用fork创建的子进程继承了父进程的当前工作目录。由于在进程运行中,当前目录所在的文件系统(如“/mnt/usb”)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让"/"作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如/tmp,改变工作目录的常见函数是chdir。

        (5)在孙进程中调用 umask() 函数,设置进程的文件权限掩码为0;
        文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有个文件权限掩码是050,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用fork函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为0,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是umask。在这里,通常的使用方法为umask(0)。

        (6)在孙进程中关闭任何不需要的文件描述符,关闭输入输出和错误输出(stdin,stdout,stderr),并将其重定向到/dev/null;
        同文件权限码一样,用fork函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法卸下。

        ​ 在上面的第2)步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如printf)输出的字符也不可能在终端上显示出来。所以,文件描述符为0、1和2 的3个文件(常说的输入、输出和报错)已经失去了存在的价值,也应被关闭。

        (7)守护进程退出处理;
        当用户需要外部停止守护进程运行时,往往会使用 kill 命令停止该守护进程。所以,守护进程中需要编码来实现 kill 发出的signal信号处理,达到进程的正常退出。

二、创建一个守护进程

        创建一个守护进程一般有 nohup命令、fork()函数和 daemon()函数三种方法,我们分别在阿里云服务器、树莓派上用这三种方式创建一个守护进程。

1、nohup命令


        nohup命令是“no hang up”的缩写,意为“不挂断”。它允许用户在系统后台运行命令,并确保命令在终端关闭或用户退出后仍然继续运行。默认情况下,nohup命令会将输出重定向到当前目录下的nohup.out文件中,除非另外指定了输出文件。

编写一个简单的脚本loop.sh:

#!/bin/bash
while true; doecho "Running daemon task..." >> /tmp/daemon.logsleep 10
done

赋予脚本执行权限:

chmod +c loop.sh

使用nohup启动守护进程:

nohup ./loop.sh > /dev/null 2>&1 &

验证守护进程是否运行:

ps aux | grep loop.sh

  • 阿里云服务器运行结果

2、fork()函数


        fork是复制进程的函数,程序一开始就会产生一个进程,当这个进程(代码)执行到fork()时,fork就会复制一份原来的进程即就是创建一个新进程,我们称子进程,而原来的进程我们称为父进程,此时父子进程是共存的,他们一起向下执行代码。
        注意的一点:就是调用fork函数之后,一定是两个进程同时执行fork函数之后的代码,而之前的代码以及由父进程执行完毕。

编写C程序fork_test.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>void daemonize() {pid_t pid = fork();if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);}if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程继续运行setsid(); // 创建新会话chdir("/"); // 改变工作目录umask(0); // 重设文件权限掩码// 关闭标准输入输出流close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 执行守护任务while (1) {FILE *fp = fopen("/tmp/daemon_fork.log", "a");fprintf(fp, "Daemon running...\n");fclose(fp);sleep(10);}
}int main() {daemonize();return 0;
}

编译程序

gcc -o fork_test fork_test.c

运行守护进程

./fork_test

验证守护进程是否运行

ps aux | grep fork_test

  • 阿里云服务器运行结果

3、daemon()函数

编写c程序daemon_test.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main() {// 调用 daemon() 函数if (daemon(1, 0) == -1) {perror("daemon failed");exit(EXIT_FAILURE);}// 执行守护任务while (1) {FILE *fp = fopen("/tmp/daemon_builtin.log", "a");fprintf(fp, "Daemon running...\n");fclose(fp);sleep(10);}return 0;
}

编译程序

gcc -o daemon_test daemon_test.c

运行守护进程

./daemon_test

验证守护进程是否运行

ps aux | grep daemon_test

阿里云服务器结果:

三、GDB调试原理

        GDB(GNU Debugger)是一个强大的调试工具,用于调试使用C、C++等语言编写的程序。它允许用户在程序执行期间检查变量值、设置断点、单步执行代码以及分析崩溃转储等。

1、基本原理


        符号表:当编译一个程序时,如果启用了调试信息(例如通过-g选项),编译器会生成包含函数名、变量名及其类型等信息的符号表。GDB利用这些信息来帮助开发者理解正在运行的程序。
        控制流管理:GDB可以暂停(breakpoints)、继续(continue)、单步执行(step into/over)程序的执行流程,以便于观察特定时刻的状态。
        数据操作:GDB能够读取和修改程序中变量的值,这有助于测试不同的条件分支或修正错误状态。
        信号处理:GDB能够捕获并响应来自操作系统或其他进程的信号(如SIGSEGV、SIGINT等),从而允许对异常情况下的程序进行调试。
        远程调试支持:GDB还支持通过网络连接到另一个运行中的程序实例,实现远程调试功能。


2、调试C程序


编写并编译你的C程序:
创建一个名为 test.c 的文件,并编写如下代码:

#include <stdio.h>int add(int a, int b) {return a + b;
}int main() {int x = 5;int y = 7;printf("Sum is %d\n", add(x, y));return 0;
}

然后,使用 -g 选项编译这个程序以包含调试信息:

gcc -g -o test test.c

  • 启动GDB调试会话

gdb ./test

  • 设置断点

在你感兴趣的函数或行号处设置断点。例如,在 add 函数入口处设置断点:

(gdb) break add

或者按行号设置断点:

(gdb) break test.c:6

  • 运行程序

输入 run 来开始执行程序:

(gdb) run

程序将在遇到第一个断点时暂停。

  • 检查状态

使用命令查看当前局部变量的值:

(gdb) print a
(gdb) print b

也可以查看调用栈信息:

(gdb) backtrace

  • 单步执行

使用 next 或 step 命令逐行执行代码。区别在于 step 会进入函数内部,而 next 则跳过函数调用直接到下一行。

(gdb) next
(gdb) step

  • 结束调试

完成调试后,你可以使用 quit 退出GDB:

(gdb) quit

四、SSH反向代理树莓派

1、 确保树莓派和阿里云服务器的 SSH 服务正常运行

检查树莓派的ssh服务

sudo systemctl status ssh

如果未启用,请启动并设置开机自启:

sudo systemctl enable ssh
sudo systemctl start ssh

检查阿里云服务器的SSH服务

sudo systemctl status ssh

2、在阿里云服务器上检查端口是否被占用

sudo netstat -tuln | grep 9624

如果有输出,说明该端口已被占用,否则,该端口可以使用。

3、在树莓派上建立 SSH反向代理

使用 ssh 命令建立反向隧道,将树莓派的 SSH 服务映射到阿里云服务器的指定端口(例如:9699):

ssh -R 9699:localhost:22 huhs@114.55.126.125

// huhs:阿里云服务器的登录用户

// 9699:指定远程端口

// 114.55.126.125 阿里云服务器公网IP

在阿里云服务器上运行下面命令,测试是否可以通过localhost:9699访问树莓派

ssh -p 9699 hhs@localhost

// xlq:树莓派登录用户

// localhost: 指的是阿里云服务器的本地回环接口(即 127.0.0.1)

直接从外网访问树莓派

ssh -p 9699 hhs@114.55.126.125

相关文章:

  • Android平台 Hal AIDL 系列文章目录
  • 人工智能应用工程师(工信部教考中心)
  • 信息系统项目管理师_第十一章 项目采购管理
  • C++代码优化
  • 若依同步企业微信架构及ACTIVITI
  • 【VSCode】在 VSCode 中运行 HTML 页面并通过 HTTPS 访问
  • linux查看及修改用户过期时间
  • 【Java面试系列】Spring Boot微服务架构下的分布式事务设计与实现详解 - 3-5年Java开发必备知识
  • 【多线程-第四天-SDWebImage的常见面试题-进一步解读SDWebImage的源代码 Objective-C语言】
  • 使用Docker搭建开源Email服务器
  • stl 容器 – map
  • DDoS防御发展史
  • 7.Rust+Axum:打造高效 RESTful API 的最佳实践
  • Python中type()函数的深度探索:类型检查与动态类创建
  • 吴恩达强化学习复盘(1)聚类算法|K-Means算法
  • 解决jupyter notebook修改路径下没有c.NotebookApp.notebook_dir【建议收藏】
  • js实现的前端水印
  • 人工智能代理重塑数字成功:为何面向机器的营销是下一前沿
  • OOM 未触发 JVM 崩溃的可能原因
  • 如何-批量修改文件夹的命名
  • 科普|军团菌肺炎:春末夏初的隐形健康威胁
  • “万人大院”重组,上海交大校长丁奎岭:人才培养事关生存发展,再难也要改
  • 青创上海—2025浦东徒步行活动举行,“青年草坪创新创业湃对”正式亮相
  • 广西旱情如何?农业厅:近半数农田墒情不足至干旱,本月降雨将渐增
  • “月光女神”莎拉·布莱曼来上海,再现好莱坞浮华幻梦
  • 公安部道研中心:虚假宣传自动驾驶致严重后果,责任人或处2年以下刑期