当前位置: 首页 > 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下如何自定义或编写一个守护进程

http://www.xdnf.cn/news/22141.html

相关文章:

  • AUTOSAR图解==>AUTOSAR_SWS_CryptoInterface
  • 淘宝商品搜索爬虫:Python 实现教程
  • 江苏广电HC2910-创维代工-Hi3798cv200-2+8G-海美迪安卓7.0-强刷包
  • 深度学习-torch,全连接神经网路
  • 软硬链接与动静态库基本概念(快速回顾)
  • 浅析数据库面试问题
  • `get_peft_model` 是 `peft` 库什么方法
  • 【CPP】死锁产生、排查、避免
  • 国内主要半导体厂家
  • Java 接入deepseek(非流式)
  • 数据资产登记导则详解 | 企业如何规范化登记与管理数据资产?
  • 机械臂速成小指南(二十五):机械臂与人工智能的有机结合
  • 基于 S2SH 架构的企业车辆管理系统:设计、实现与应用
  • OOM 未触发 JVM 崩溃的可能原因
  • 椰汁椰肉生产车间设计
  • 怎么隐藏QTabWidget内的页面
  • Git 解决“Filename too long”问题
  • 企业应用大模型报告:如何应对变革,构建专属“我的AI”
  • 生物化学笔记:医学免疫学原理22 肿瘤及肿瘤治疗
  • vue3+vite 实现.env全局配置
  • 大模型时代:AI应用的变革与挑战
  • 冰箱在储存各种疫苗时要经过多少道程序又会面临哪些风险?
  • linux获取cpu使用率(sy%+us%)
  • 文件二进制读写和文本读写以及编码解码
  • Android 12系统静态壁纸深度定制指南
  • day2-小白学习JAVA---java第一个程序
  • 电力资源配置逐步从计划模式转向市场驱动
  • 内存函数和动态内存管理
  • leetcode刷题日记——同构字符串
  • 北京SMT贴片厂精密制造关键工艺