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

【嵌入式】【阿里云服务器】【树莓派】学习守护进程编程、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博客

相关文章:

  • css图片设为灰色
  • Allure测试报告按测试终端和测试类型智能分类查看
  • 前端为什么需要单元测试?
  • 数据仓库分层架构解析:从理论到实战的完整指南​​
  • AXOP36061S: 60V 高压单通道运算放大器
  • 视频分析设备平台EasyCVR化解高速服务区管理难题,打造全方位智能安防监控方案
  • 《AI赋能职场:大模型高效应用课》第8课 AI辅助职场沟通与协作
  • 杂书的读书笔记
  • 线程池七个参数的含义
  • python有序列表
  • 【Triton 教程】triton_language.full
  • C++手撕STL-其一
  • 大语言模型的训练、微调及压缩技术
  • InternVL 3的技术深度分析,代码与原理
  • 【ELF2学习板】交叉编译FFTW
  • 记录学习的第二十九天
  • 浅析数据库面试问题
  • Langchain+chain+数据库操作
  • 守护者进程小练习
  • 2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(二级)答案 + 解析
  • 85岁眼科专家、武汉大学人民医院原眼科主任喻长泰逝世
  • 日本长野一夜连震47次,当局呼吁警惕更大地震
  • 徐州沛县一村委会因无资质处理固废,被环保部门罚款19万元
  • 2025“上海之夏”向全球邀约,首批城市定制活动集中亮相
  • 2025年世界互联网大会亚太峰会人工智能大模型论坛举行
  • 左眼失明左耳失聪,办理残疾人证被拒?县残联:双眼残疾才能办