守护进程编程以及ssh反向代理
目录
- 一、守护进程概述
- 1.守护进程的编程实现过程
- 2、代码实现示例
 
- 二、创建守护进程
- 使用 nohup 命令
- 1. 创建测试脚本 demo_nohup.sh
- 2. 赋予执行权限并启动
- 3. 验证守护进程
 
- 使用 fork()函数
- 1. 编写 C 程序 daemon_fork.c
- 2. 编译并运行
- 3. 验证守护进程
 
- 使用 daemon() 函数
- 1. 编写 C 程序 daemon_glibc.c
- 2. 编译并运行
- 3. 验证守护进程
 
- 验证守护进程特性
- 停止守护进程
 
- 三、 掌握gdb调试原理,在阿里云服务器或树莓派上用 gdb命令调试一个c程序
- GDB 调试原理
- 调试环境准备(通用步骤)
- 1. 安装 GDB
- 2. 编写测试程序
- 3. 编译带调试信息的程序
 
- GDB 调试实战
- 1. 启动 GDB
- 2. 常用调试命令
- 3. 完整调试流程演示
 
 
- 四、借助阿里云服务器,使用SSH反向代理,完成树莓派的外网访问
- 中转服务器设置
- 内网主机(树莓派)ssh连接中转服务器
 
 
一、守护进程概述
 守护进程(Daemon)是操作系统中一类长期在后台运行的特殊进程,它独立于控制终端,主要用于执行系统级服务或周期性任务(如日志管理、网络服务等)。其核心特征包括:脱离用户终端控制、生命周期与系统运行周期同步、资源消耗低且通常以特定权限运行。
 正常情况下,当我们运行一个前台或后台进程时,一旦离开当前会话(终端),那该会话中的所有前后台进程也随即结束,当你重新打开会话时,已经“物是人非,难遇故人”了。而守护进程就可以不受会话的限制,可在前后台一直运行直至结束的进程。
 守护进程的实现有两种方式:自编和利用现有程序伪装
1.守护进程的编程实现过程
在类Unix系统中,实现守护进程需遵循以下标准化步骤,这些步骤旨在确保进程与环境的完全隔离:
-  创建子进程并终止父进程 
 通过fork()系统调用生成子进程后,父进程立即退出,使子进程成为“孤儿进程”并由init进程接管。这一操作使守护进程形式上脱离终端控制,并消除与Shell终端的直接关联pid_t pid = fork(); if (pid > 0) exit(0); // 父进程退出
-  创建新会话组 
 子进程调用setsid()创建新的会话组并成为组长,切断与原终端、进程组和控制终端的联系,这是实现完全后台运行的关键if (setsid() < 0) exit(1); // 失败则终止
-  改变工作目录 
 将当前目录切换至根目录(/)或其他安全路径,避免因挂载点未卸载导致的系统问题chdir("/"); // 切换到根目录
-  重设文件权限掩码 
 调用umask(0)重置文件创建掩码,确保后续生成文件的权限不受父进程限制
-  关闭继承的文件描述符 
 关闭从父进程继承的标准输入、输出和错误描述符(通常为0/1/2),或将其重定向至/dev/null,防止意外占用终端资源close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); open("/dev/null", O_RDONLY); // 重定向标准输入
 6.守护进程功能实现(无限循环)
2、代码实现示例
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <unistd.h>  
#include <sys/wait.h>  
#include <sys/types.h>  
#include <fcntl.h>  int main() 
{    pid_t pid;  int i, fd, len;  char *buf = "守护进程运行中.\n";  len = strlen(buf)+1;pid = fork();	//1.1 创建子进程if (pid < 0) {  printf("fork error!");  exit(1);  }if (pid>0) 		// 1.2父进程退出  exit(0);  setsid(); 		// 2.在子进程中创建新会话。  chdir("/"); 	// 3.设置工作目录为根目录  umask(0); 		// 4.设置权限掩码  for(i=0; i<getdtablesize(); i++) //5.关闭用不到的文件描述符  close(i);//6.守护进程功能实现while(1) {			// 死循环表征它将一直运行fd = open("/var/log/daemon.log",O_CREAT|O_WRONLY|O_APPEND,0600);if(fd < 0) {printf("Open file failed!\n");exit(1);  }  write(fd, buf, len);  // 将buf写到fd中  close(fd);  sleep(10);  printf("error: Never run here!\n");return 1;}  return 0;  
}二、创建守护进程
创建一个守护进程一般有 nohup命令、fork()函数和 daemon()函数三种方法
使用 nohup 命令
1. 创建测试脚本 demo_nohup.sh
nano demo_nohup.sh
#!/bin/bash
while true; doecho "$(date) >> 守护进程运行中..." >> /tmp/nohup_demo.logsleep 5
done
2. 赋予执行权限并启动
chmod +x demo_nohup.sh
nohup ./demo_nohup.sh > /dev/null 2>&1 &
3. 验证守护进程
ps aux | grep demo_nohup
tail -f /tmp/nohup_demo.log  # 查看日志输出

使用 fork()函数
1. 编写 C 程序 daemon_fork.c
nano daemon_fork.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>void create_daemon() {pid_t pid = fork();if (pid < 0) exit(1);if (pid > 0) exit(0);  // 父进程退出setsid();              // 创建新会话chdir("/");            // 切换工作目录到根umask(0);              // 重置文件权限掩码// 关闭所有打开的文件描述符for (int fd = sysconf(_SC_OPEN_MAX); fd >= 0; fd--) {close(fd);}// 重定向标准流到 /dev/nullopen("/dev/null", O_RDWR);  // stdindup(0);                     // stdoutdup(0);                     // stderr
}int main() {create_daemon();// 守护进程主逻辑while (1) {int fd = open("/tmp/fork_demo.log", O_WRONLY | O_CREAT | O_APPEND, 0644);if (fd != -1) {dprintf(fd, "%ld >> 守护进程运行中...\n", time(NULL));close(fd);}sleep(5);}return 0;
}
2. 编译并运行
gcc daemon_fork.c -o daemon_fork
./daemon_fork
3. 验证守护进程
ps aux | grep daemon_fork
tail -f /tmp/fork_demo.log

使用 daemon() 函数
1. 编写 C 程序 daemon_glibc.c
nano daemon_glibc.c
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>int main() {if (daemon(0, 0) == -1) {  // 参数:nochdir=0, noclose=0perror("daemon() 失败");exit(1);}// 守护进程主逻辑while (1) {FILE *fp = fopen("/tmp/daemon_demo.log", "a");if (fp) {fprintf(fp, "%ld >> 守护进程运行中...\n", time(NULL));fclose(fp);}sleep(5);}return 0;
}
2. 编译并运行
gcc daemon_glibc.c -o daemon_glibc
./daemon_glibc
3. 验证守护进程
ps aux | grep daemon_glibc
tail -f /tmp/daemon_demo.log

验证守护进程特性
所有方法创建的守护进程应满足以下特征:
-  无控制终端: ps -eo pid,tty,cmd | grep -E 'nohup|daemon_fork|daemon_glibc'输出中的 TTY列应为?
-  父进程为 init (PID 1): ps -o ppid= -p <守护进程PID>
停止守护进程
# 查找 PID
ps aux | grep -E 'nohup|daemon_fork|daemon_glibc'# 杀死进程
sudo kill <PID>
| 方法 | 优点 | 缺点 | 
|---|---|---|
| nohup | 无需编程,简单快速 | 功能有限,依赖 shell 环境 | 
| fork() | 完全控制守护进程行为 | 代码复杂度高 | 
| daemon() | glibc 封装,代码简洁 | 部分系统标记为弃用,灵活性低 | 
三、 掌握gdb调试原理,在阿里云服务器或树莓派上用 gdb命令调试一个c程序
GDB 调试原理
GDB 基于以下核心机制实现调试功能:
- 进程控制: - 通过 ptrace系统调用监控目标进程。
- 控制被调试进程的执行(启动、暂停、单步执行)。
 
- 通过 
- 断点机制: - 在代码地址插入 int 3指令(0xCC),触发中断信号 SIGTRAP。
- 断点命中后恢复原指令内容。
 
- 在代码地址插入 
- 符号解析: - 依赖编译时生成的调试信息(-g选项)。
- 映射机器指令到源代码行号、变量名等。
 
- 依赖编译时生成的调试信息(
- 内存访问: - 直接读写被调试进程的内存空间。
- 支持查看寄存器、堆栈数据。
 
调试环境准备(通用步骤)
1. 安装 GDB
-  树莓派(Raspberry Pi OS): sudo apt update && sudo apt install gdb
2. 编写测试程序
创建 test.c:
nano test.C
输入以下内容:
#include <stdio.h>int sum(int a, int b) {return a + b;
}int main() {int x = 5, y = 3;int result = sum(x, y);printf("Result: %d\n", result);return 0;
}
3. 编译带调试信息的程序
gcc -g test.c -o test
关键点:必须使用 -g 选项保留调试符号
GDB 调试实战
1. 启动 GDB
gdb ./test
2. 常用调试命令
| 命令 | 作用 | 示例 | 
|---|---|---|
| break [位置] | 设置断点 | break main | 
| run [参数] | 启动程序 | run | 
| next/n | 单步执行(跳过函数) | next | 
| step/s | 单步进入函数 | step | 
| print [变量]/p | 打印变量值 | print x | 
| info locals | 查看当前栈帧的局部变量 | info locals | 
| backtrace/bt | 查看函数调用栈 | backtrace | 
| continue/c | 继续运行到下一个断点 | continue | 
| quit | 退出 GDB | quit | 
3. 完整调试流程演示
# 启动 GDB
(gdb) break main          # 在 main 函数设置断点
(gdb) run                 # 启动程序# 程序会在 main 函数开头暂停(gdb) next                # 执行 int x = 5, y = 3;
(gdb) print x             # 输出 x 的值(应为 5)
(gdb) step                # 进入 sum 函数
(gdb) info locals         # 查看 a 和 b 的值
(gdb) next                # 执行 return a + b;
(gdb) print $rax          # 查看返回值(x86_64 架构)
(gdb) continue            # 继续执行到程序结束
(gdb) quit                #退出gdb

四、借助阿里云服务器,使用SSH反向代理,完成树莓派的外网访问
 目标:让其他人可以从任何地方用笔记本电脑,通过访问阿里云服务器的端口(穿透)登录到你小组树莓派系统。
中转服务器设置
1.关闭对应端口的防火墙
sudo ufw allow 9623

2.设置ssh配置文件:

3.重启ssh
systemctl restart ssh
内网主机(树莓派)ssh连接中转服务器

ssh -p 22 -qngfNTR [端口号]:localhost:22 服务器账号@[服务器地址]

阿里云服务器查看监听端口:
ss -ntl

在另外一台电脑上输入,以下命令连接树莓派
ssh -p [你绑定的端口号] [树莓派用户]@云服务器地址

参考链接:
Linux守护进程的编写及使用方法
inux系统编程之进程(八)守护进程详解及创建,daemon()使用
一招教会你基于阿里云ECS服务器实现【内网穿透SSH访问家庭树莓派】
Ubuntu用autossh实现内网穿透(反向隧道)
GDB调试入门指南
原来gdb的底层调试原理这么简单
linux下如何自定义或编写一个守护进程
