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

【Linux】【阿里云服务器】【树莓派】学习守护进程编程、gdb调试原理和内网穿透信息

目录

一. 守护进程的含义及编程实现的主要过程

1.1守护进程

1.2编程实现的主要过程

二、在树莓派中通过三种方式创建守护进程

2.1nohup命令创建

2.2fork()函数创建

2.3daemon()函数创建

三、在阿里云中通过三种方式创建守护进程

3.1nohup命令创建

3.2fork()函数创建

3.3daemon()函数创建

四、gdb调试

4.1核心机制

4.2用gdb命令调试一个C程序

五、SSH反向代理,完成树莓派外网访问

5.1阿里云服务器准备

5.2在树莓派上设置SSH反向隧道

5.3验证反向隧道是否成功

5.4验证外网访问树莓派


一. 守护进程的含义及编程实现的主要过程

1.1守护进程

守护进程是运行在后台的独立于终端的进程,通常用于提供系统服务(如Web服务器、数据库等)。其特点包括:

  • 脱离终端控制(不与用户直接交互)。

  • 生命周期长(随系统启动/关闭)。

  • 以root权限或特定用户身份运行。

1.2编程实现的主要过程

(1)屏蔽部分控制终端操作的信号

为了避免终端挂起(SIGHUP)信号影响守护进程,需要在程序开始时忽略该信号。这可以通过signal()函数实现。

#include <signal.h>
​
// 屏蔽SIGHUP信号
signal(SIGHUP, SIG_IGN);

(2) 在后台运行

通过fork()创建一个子进程,父进程退出,子进程继续运行,从而实现程序在后台运行。

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
​
pid_t pid = fork();
if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);
}
if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出
}

(3) 脱离控制终端、登录会话和进程组

调用setsid()创建一个新的会话,使进程脱离控制终端、登录会话和进程组。

#include <unistd.h>
​
if (setsid() < 0) {perror("setsid failed");exit(EXIT_FAILURE);
}

(4) 禁止进程重新打开控制终端

再次忽略SIGHUP信号,确保进程不会重新打开控制终端。

#include <signal.h>
​
// 再次忽略SIGHUP信号
signal(SIGHUP, SIG_IGN);

(5) 关闭打开的文件描述符

关闭所有已打开的文件描述符,防止守护进程继承不必要的文件描述符。

#include <unistd.h>
#include <limits.h>
​
// 关闭所有打开的文件描述符
for (int fd = 0; fd < sysconf(_SC_OPEN_MAX); fd++) {close(fd);
}

(6) 改变当前工作目录

将工作目录改为根目录(/),防止守护进程被挂载的文件系统限制。

#include <unistd.h>
​
// 改变工作目录到根目录
if (chdir("/") < 0) {perror("chdir failed");exit(EXIT_FAILURE);
}

(7) 重设文件创建掩模

设置文件创建掩模为0,允许守护进程创建文件时具有最大权限。

#include <sys/types.h>
#include <sys/stat.h>
​
// 设置文件创建掩模为0
umask(0);

(8) 处理 SIGCHLD 信号

忽略SIGCHLD信号,防止子进程退出影响父进程。

#include <signal.h>
​
// 忽略SIGCHLD信号
signal(SIGCHLD, SIG_IGN);

二、在树莓派中通过三种方式创建守护进程

2.1nohup命令创建

1. 创建测试脚本

nano ~/test_daemon.sh

输入下面内容:

#!/bin/bash
​
while true; doecho "$(date): Daemon is running on Raspberry Pi" >> /tmp/daemon.logsleep 5
done

2. 设置权限并运行

chmod +x ~/test_daemon.sh nohup ~/test_daemon.sh > /dev/null 2>&1 &

3. 检查运行情况

# 查看进程
ps aux | grep test_daemon

2.2fork()函数创建

1. 安装编译工具(如果尚未安装)

sudo apt update sudo apt install build-essential -y

2. 创建C程序

nano ~/fork_daemon.c

输入下面内容:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
​
int main(int argc, char* argv[]) {pid_t process_id = 0;pid_t sid = 0;// 创建子进程process_id = fork();// 创建失败if (process_id < 0) {printf("fork failed!\n");exit(1);}// 父进程退出if (process_id > 0) {printf("process_id of child process %d \n", process_id);exit(0);}// 设置新的会话sid = setsid();if(sid < 0) {exit(1);}// 改变工作目录chdir("/");// 关闭标准输入、输出、错误close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 守护进程主循环while (1) {FILE *log = fopen("/tmp/fork_daemon.log", "a+");if (log != NULL) {time_t now;time(&now);fprintf(log, "[%ld] Daemon is running (fork method)\n", now);fclose(log);}sleep(5);}return 0;
}

3. 编译运行

gcc ~/fork_daemon.c -o ~/fork_daemon ~/fork_daemon

4. 检查运行情况

ps aux | grep fork_daemon

2.3daemon()函数创建

1. 创建C程序

nano ~/daemon_func.c

输入下面内容:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
​
int main(int argc, char* argv[]) {// 使用daemon()函数创建守护进程// 参数1: 是否改变工作目录到根目录// 参数2: 是否关闭标准输入输出错误if (daemon(1, 0) == -1) {printf("daemon creation failed\n");exit(EXIT_FAILURE);}// 守护进程主循环while (1) {FILE *log = fopen("/tmp/daemon_func.log", "a+");if (log != NULL) {time_t now;time(&now);fprintf(log, "[%ld] Daemon is running (daemon function)\n", now);fclose(log);}sleep(5);}return 0;
}

2. 编译运行

gcc ~/daemon_func.c -o ~/daemon_func ~/daemon_func

3. 检查运行情况

# 查看进程
ps aux | grep daemon_func

三、在阿里云中通过三种方式创建守护进程

3.1nohup命令创建

1. 创建程序

nano nohup_daemon.sh

输入以下内容:

#!/bin/bash
​
while true; doecho "$(date): Daemon is running (nohup method)" >> /var/log/nohup_daemon.logsleep 10
done

2. 编译运行

sudo chmod +x nohup_daemon.sh设置执行权限nohup nohup_daemon.sh > /dev/null 2>&1 &使用nohup启动守护进程

3. 检查运行情况

ps aux | grep nohup_daemon

3.2fork()函数创建

1. 创建程序

nano ~/fork_daemon.c

输入以下内容:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
​
#define LOG_FILE "/var/log/fork_daemon.log"
​
void write_log(const char *message) {FILE *log = fopen(LOG_FILE, "a");if (log != NULL) {time_t now;time(&now);fprintf(log, "[%s] %s\n", ctime(&now), message);fclose(log);}
}
​
int main() {pid_t pid = fork();if (pid < 0) {exit(EXIT_FAILURE);}if (pid > 0) {exit(EXIT_SUCCESS);}umask(0);setsid();chdir("/");close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);while (1) {write_log("Daemon is running (fork method)");sleep(10);}return EXIT_SUCCESS;
}

2. 编译运行

gcc ~/fork_daemon.c -o ~/fork_daemon ~/fork_daemon

3. 检查运行情况

ps aux | grep fork_daemon

3.3daemon()函数创建

1. 创建C程序

nano ~/daemon_func.c

输入下面内容:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
​
int main(int argc, char* argv[]) {// 使用daemon()函数创建守护进程// 参数1: 是否改变工作目录到根目录// 参数2: 是否关闭标准输入输出错误if (daemon(1, 0) == -1) {printf("daemon creation failed\n");exit(EXIT_FAILURE);}// 守护进程主循环while (1) {FILE *log = fopen("/tmp/daemon_func.log", "a+");if (log != NULL) {time_t now;time(&now);fprintf(log, "[%ld] Daemon is running (daemon function)\n", now);fclose(log);}sleep(5);}return 0;
}

2. 编译运行

gcc ~/daemon_func.c -o ~/daemon_func ~/daemon_func

3. 检查运行情况

# 查看进程
ps aux | grep daemon_func

四、gdb调试

4.1核心机制

ptrace系统调用:GDB通过ptrace()接管目标进程的执行权,可访问其内存和寄存器

断点实现:将指定地址的指令替换为int 3(0xCC)触发软中断

符号表加载:读取可执行文件的.symtab和.debug_info节获取变量/函数信息

4.2用gdb命令调试一个C程序

(1)创建 test.c

#include <stdio.h>
​
int multiply(int x, int y) {
​return x * y;
​
}
​
int divide(int x, int y) {
​if (y == 0) {
​fprintf(stderr, "Error: Division by zero\n");
​return 0;
​}
​return x / y;
​
}
​
int main() {
​int a = 10, b = 0, c = 20, d;
​d = multiply(a, c);
​printf("Multiply result: %d\n", d);
​d = divide(a, b);
​printf("Divide result: %d\n", d);
​return 0;

在 main 函数中,变量 a 被初始化为 10,b 被初始化为 0,c 被初始化为 20,d 未初始化。

调用 multiply(a, c) 计算 a 和 c 的乘积,即 10 * 20,结果为 200。这个结果被赋值给 d,所以此时 d 的值为 200。接下来打印 Multiply result: 200。然后调用 divide(a, b) 计算 a 和 b 的商,即 10 / 0。由于 b 的值为 0,这将导致除以零的错误。divide 函数会打印错误信息 "Error: Division by zero" 并返回 0。这个结果被赋值给 d,所以此时 d 的值变为 0。最后打印 Divide result: 0。

(2)编译带调试信息

gcc -g test.c -o test # -g选项生成调试符号

(3)启动 gdb 调试

gdb ./test

(4)设置断点

break multiplybreak divide

(5)运行程序

run

(6)单步执行

next

一直next,直到出现divide函数,执行step命令进入到divide含糊内部进行单步调试

step

使用print命令来检查传入 divide函数的参数想和y的值,确保他们的预期值

print xprint y

(7)单步执行

step

程序已经执行了 divide 函数中的 if (y == 0) 条件检查。由于 y 的值是 20(不等于 0),程序将继续执行 if 语句块之外的代码,继续单步执行step

程序已经执行到了 fprintf(stderr, "Error: Division by zero\n"); 这一行,因为在 divide 函数中检测到了除以零的情况,GDB 显示了 fprintf 函数的调用信息

(8)检查d的输出值(d在main函数里面,要检查d的值就要退出divide函数并返回到调用点,使用finish命令)

finish

(9)继续执行程序(程序将继续执行并打印 Divide result: 后跟 d 的值)

continue

完成调试,退出gdb时,使用quit命令

五、SSH反向代理,完成树莓派外网访问

5.1阿里云服务器准备

(1)开放安全组端口

通过命令sudo ufw status可以查看到当前激活的端口,查看是否有我们将要连接的端口。

若没有,需要通过命令sudo ufw allow 9613/tcp 进行端口的开放

(2)修改阿里云的SSH配置文件,允许端口转发

sudo nano /etc/ssh/sshd_config

确保文件中有如下配置:

GatewayPorts yes
AllowTcpForwarding yes

配置完成后重启SSH服务:

sudo systemctl restart sshd

5.2在树莓派上设置SSH反向隧道

首先通过电脑cmd命令行登录进入树莓派

进入后通过命令:ssh -fN -R 端口号:localhost:22 <用户名>@<阿里云IP地址>在树莓派上建立反向SSH隧道

5.3验证反向隧道是否成功

建立成功后我们可以通过一系列命令测试反向隧道是否成功

(1)在阿里云服务器上检查监听端口

sudo netstat -tuln | grep <端口号>

(2)通过阿里云连接树莓派

ssh -p 6000 pi@localhost

5.4验证外网访问树莓派

我们通过其他电脑,连接与树莓派不同的WIFI

输入命令ssh -p <端口号> <树莓派地址>@<阿里云IP地址>

然后就可以成功通过外网访问树莓派

参考博客:

Linux系统编程——特殊进程之守护进程_linux c++ 守护进程 exec 自动重启-CSDN博客

相关文章:

  • 接口自动化测试(二)
  • 【零基础】基于 MATLAB + Gurobi + YALMIP 的优化建模与求解全流程指南
  • 大模型时代:AI应用的变革与挑战
  • Linux系统之----冯诺依曼结构
  • AI编程方法第五弹:测试很重要
  • 智谱AI大模型免费开放:开启AI创作新时代
  • docker镜像被覆盖了怎么办?通过sha256重新上传镜像
  • 第 7 期:DDPM 采样提速方案:从 DDPM 到 DDIM
  • 【论文阅读20】-CNN-Attention-BiGRU-滑坡预测(2025-03)
  • zset.
  • 《软件设计师》复习笔记(4.2)——关系代数、函数依赖、范式
  • 容性串扰-信号与电源完整性分析
  • 公务员行测之速算分数记忆检验-无答案版本
  • 5.常用控件-QWidget|enabled|geometry|window frame(C++)
  • nuxt3路由切换页面出不来,刷新可以
  • C++17 信号量模拟实现
  • Json 在线格式化 - 加菲工具
  • AUTOSAR图解==>AUTOSAR_SWS_E2ETransformer
  • Sigma-Delta ADC(ΣΔ-ADC)中的量化器简介
  • YOLOv11改进:基于小波卷积WTConv的大感受野目标检测网络-
  • 管理规模归零,华夏基金“ETF规模一哥”张弘弢清仓卸任所有产品
  • 新疆维吾尔自治区政协原副主席窦万贵一审被控受贿超2.29亿
  • 外交部介绍中印尼“2+2”机制首次部长级会议将讨论的议题
  • 6名驴友庐山西海探险走失被追缴2万救援费,组织者被追缴4千
  • 上海浦东法院两年受理涉民企知识产权案件8984件,总标的超27亿元
  • 3月上海新房价格环比领涨全国,一线城市二手住宅价格环比由跌转涨