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

守护进程编程以及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系统中,实现守护进程需遵循以下标准化步骤,这些步骤旨在确保进程与环境的完全隔离:

  1. 创建子进程并终止父进程
    通过fork()系统调用生成子进程后,父进程立即退出,使子进程成为“孤儿进程”并由init进程接管。这一操作使守护进程形式上脱离终端控制,并消除与Shell终端的直接关联

    pid_t pid = fork();
    if (pid > 0) exit(0);  // 父进程退出
    
  2. 创建新会话组
    子进程调用setsid()创建新的会话组并成为组长,切断与原终端、进程组和控制终端的联系,这是实现完全后台运行的关键

    if (setsid() < 0) exit(1);  // 失败则终止
    
  3. 改变工作目录
    将当前目录切换至根目录(/)或其他安全路径,避免因挂载点未卸载导致的系统问题

    chdir("/");  // 切换到根目录
    
  4. 重设文件权限掩码
    调用umask(0)重置文件创建掩码,确保后续生成文件的权限不受父进程限制

  5. 关闭继承的文件描述符
    关闭从父进程继承的标准输入、输出和错误描述符(通常为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  # 查看日志输出

image-20250418133452413


使用 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

image-20250418133853994


使用 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

image-20250418134558532


验证守护进程特性

所有方法创建的守护进程应满足以下特征:

  1. 无控制终端

    ps -eo pid,tty,cmd | grep -E 'nohup|daemon_fork|daemon_glibc'
    

    输出中的 TTY 列应为 ?

  2. 父进程为 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 基于以下核心机制实现调试功能:

  1. 进程控制
    • 通过 ptrace 系统调用监控目标进程。
    • 控制被调试进程的执行(启动、暂停、单步执行)。
  2. 断点机制
    • 在代码地址插入 int 3 指令(0xCC),触发中断信号 SIGTRAP。
    • 断点命中后恢复原指令内容。
  3. 符号解析
    • 依赖编译时生成的调试信息(-g 选项)。
    • 映射机器指令到源代码行号、变量名等。
  4. 内存访问
    • 直接读写被调试进程的内存空间。
    • 支持查看寄存器、堆栈数据。

调试环境准备(通用步骤)

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退出 GDBquit
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

image-20250418130453875

四、借助阿里云服务器,使用SSH反向代理,完成树莓派的外网访问

目标:让其他人可以从任何地方用笔记本电脑,通过访问阿里云服务器的端口(穿透)登录到你小组树莓派系统。

中转服务器设置

1.关闭对应端口的防火墙

sudo ufw allow 9623

image-20250417131741573

2.设置ssh配置文件:

image-20250417132857887

3.重启ssh

systemctl restart ssh

内网主机(树莓派)ssh连接中转服务器

image-20250415155139767

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

image-20250418095722289

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

ss -ntl

image-20250417115721587

在另外一台电脑上输入,以下命令连接树莓派

ssh -p [你绑定的端口号] [树莓派用户]@云服务器地址

image-20250418100948714

参考链接:

Linux守护进程的编写及使用方法

inux系统编程之进程(八)守护进程详解及创建,daemon()使用

一招教会你基于阿里云ECS服务器实现【内网穿透SSH访问家庭树莓派】

Ubuntu用autossh实现内网穿透(反向隧道)

GDB调试入门指南

原来gdb的底层调试原理这么简单

linux下如何自定义或编写一个守护进程

相关文章:

  • C语言==》字符串断行
  • 每日文献(十四)——Part one
  • 前端:uniapp框架中<scroll-view>如何控制元素进行局部滚动
  • Vue3笔记
  • 文献分享 | 使用ANSYS Fluent进行除雾系统设计分析
  • LIB-ZC, 一个跨平台(Linux)平台通用C/C++扩展库, 多关键字搜索模块
  • vue3学习笔记之条件渲染
  • 关于EXPLAIN ANALYZE 工具的解析及使用方法(AI)
  • centos7更换yum源不生效
  • KingbaseV8
  • easyExcel单元格合并
  • 【TVM教程】microTVM TFLite 指南
  • C++11中的std::condition_variable
  • CCS编译器无法导入工程文件,检查原因是版本编译器编译问题
  • 自适应布局,平均分配,自动换行,上下对齐
  • 大模型相关面试问题原理及举例
  • java+postgresql+swagger-多表关联insert操作(九)
  • 【Scratch编程入门】字母动画制作
  • NHANES指标推荐:CMI
  • LLaMA Factory多模态微调实践:微调Qwen2-VL构建文旅大模型
  • 国际市场开心果价格上涨35%,背后助力是一条点击过亿的短视频
  • 报告:去年物业服务百强企业营业收入均值同比增长3.52%
  • 凭春晚分会场爆火的无锡,为何请来了上海主流媒体和网络大V
  • 女外交官杨扬出任中国驻圭亚那大使
  • 青创上海-2025浦东徒步行倒计时1天,明日浦东世博文化公园不见不散
  • 上海电网新能源发电功率首破400万千瓦,绿电交易规模跨越式增长