目录
1. 守护进程的含义及编程实现
编程实现守护进程的主要过程:
完整守护进程实现代码
2. 三种创建守护进程的方法实践
方法一:nohup命令
方法二:fork()函数实现
方法三:daemon()函数实现
3. GDB调试原理与实践
GDB调试原理:
GDB调试实践:
详细调试过程
4. SSH反向代理实现树莓派外网访问
树莓派端配置
外部访问树莓派
实验结果:
1. 守护进程的含义及编程实现
守护进程(Daemon)是运行在后台的一种特殊进程,它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程通常在系统启动时运行,在系统关闭时终止。
主要特点:
-
生命周期长,一般在系统启动时运行,系统关闭时终止
-
在后台运行,不拥有控制终端
-
通常以root权限运行
-
不受用户登录/注销影响
编程实现守护进程的主要过程:
-
调用
fork()
创建子进程,父进程退出 -
调用
setsid()
创建新会话 -
改变当前工作目录到根目录
-
重设文件权限掩码
-
关闭不需要的文件描述符
-
处理SIGCHLD信号
完整守护进程实现代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <syslog.h>void daemonize() {pid_t pid;// 1. 创建子进程,终止父进程pid = fork();if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);}if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}// 2. 创建新会话if (setsid() < 0) {perror("setsid failed");exit(EXIT_FAILURE);}// 3. 忽略终端I/O信号signal(SIGTTOU, SIG_IGN);signal(SIGTTIN, SIG_IGN);signal(SIGTSTP, SIG_IGN);// 4. 再次fork确保不是会话组长pid = fork();if (pid < 0) {perror("fork failed");exit(EXIT_FAILURE);}if (pid > 0) {exit(EXIT_SUCCESS); // 父进程退出}// 5. 更改工作目录chdir("/");// 6. 设置文件权限掩码umask(0);// 7. 关闭所有打开的文件描述符for (int fd = sysconf(_SC_OPEN_MAX); fd >= 0; fd--) {close(fd);}// 8. 重定向标准I/O到/dev/nullfreopen("/dev/null", "r", stdin);freopen("/dev/null", "w", stdout);freopen("/dev/null", "w", stderr);// 9. 初始化日志系统openlog("mydaemon", LOG_PID, LOG_DAEMON);
}int main() {daemonize();syslog(LOG_NOTICE, "Daemon started");// 守护进程主循环while (1) {// 执行守护进程任务syslog(LOG_NOTICE, "Daemon is running");sleep(10);}syslog(LOG_NOTICE, "Daemon exiting");closelog();return EXIT_SUCCESS;
}
-
编译程序:
gcc daemon_example.c -o mydaemon
-
启动守护进程:
./mydaemon
-
验证守护进程:
ps -ef | grep mydaemon
2. 三种创建守护进程的方法实践
方法一:nohup命令
-
编写测试脚本 :
#!/bin/bash while true; doecho "$(date): Script is running" >> /tmp/nohup_test.logsleep 5 done
-
赋予执行权限:
chmod +x test_script.sh
-
使用nohup启动:
nohup ./test_script.sh > /dev/null 2>&1 &
-
验证:
# 查看进程 ps -ef | grep test_script# 查看输出 tail -f /tmp/nohup_test.log
方法二:fork()函数实现
-
编译:
gcc daemon_fork.c -o daemon_fork
-
运行:
./daemon_fork
-
验证:
# 查看进程 ps -ef | grep daemon_fork# 查看进程状态 pstree -p | grep daemon_fork# 查看进程打开的文件 sudo lsof -p <pid>
方法三:daemon()函数实现
-
创建文件 :
#include <unistd.h> #include <stdlib.h> #include <syslog.h>int main() {// 使用daemon函数创建守护进程// 参数1: 是否改变工作目录到根目录(0=是)// 参数2: 是否重定向标准I/O到/dev/null(0=是)if (daemon(0, 0) == -1) {exit(EXIT_FAILURE);}// 初始化日志openlog("daemonfunc", LOG_PID, LOG_DAEMON);// 守护进程主循环while (1) {syslog(LOG_NOTICE, "Daemon (func) is running");sleep(15);}closelog();return EXIT_SUCCESS; }
-
编译:
gcc daemon_func.c -o daemon_func
-
运行:
./daemon_func
-
验证:
# 查看进程 ps -ef | grep daemon_func# 查看进程状态 cat /proc/$(pgrep daemon_func)/status
3. GDB调试原理与实践
GDB调试原理:
-
通过ptrace系统调用控制被调试进程
-
可以设置断点、单步执行、查看变量和内存
-
支持源代码级调试
-
可以调试运行中的进程或分析核心转储文件
GDB调试实践:
-
编写测试程序 (test.c):
-
编译时加入调试信息:
-
启动GDB调试:
-
常用GDB命令:
详细调试过程
-
准备测试程序 :
#include <stdio.h> #include <stdlib.h>int calculate(int a, int b) {int result = 0;result = a * b;result += a + b;return result; }int main(int argc, char *argv[]) {if (argc != 3) {printf("Usage: %s <num1> <num2>\n", argv[0]);return 1;}int x = atoi(argv[1]);int y = atoi(argv[2]);printf("Calculating for %d and %d...\n", x, y);int res = calculate(x, y);printf("Final result: %d\n", res);return 0; }
-
编译带调试信息:
gcc -g debug_test.c -o debug_test
-
启动GDB调试会话:
gdb ./debug_test
-
详细调试步骤:
(gdb) break main # 在main函数设置断点 (gdb) run 5 10 # 带参数运行程序 (gdb) next # 执行下一行 (gdb) print x # 查看变量x的值 (gdb) break calculate # 在calculate函数设置断点 (gdb) continue # 继续执行到下一个断点 (gdb) step # 进入函数内部 (gdb) info locals # 查看局部变量 (gdb) watch result # 监视result变量变化 (gdb) backtrace # 查看调用栈 (gdb) frame 1 # 切换到栈帧1 (gdb) list # 查看当前位置源代码 (gdb) disassemble # 查看反汇编代码 (gdb) quit # 退出GDB
4. SSH反向代理实现树莓派外网访问
树莓派端配置
-
生成SSH密钥对(如果没有):
ssh-keygen -t rsa
-
将公钥复制到阿里云服务器:
ssh-copy-id aliyun_user@aliyun_server_ip
-
测试无密码登录:
ssh aliyun_user@aliyun_server_ip
-
建立持久化反向隧道(使用autossh):
# 安装autossh(如果未安装) sudo apt-get install autossh# 创建反向隧道 autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NTR 2222:localhost:22 aliyun_user@aliyun_server_ip
-
设置开机自动启动:
# 创建systemd服务文件 sudo nano /etc/systemd/system/ssh-tunnel.service
[Unit] Description=AutoSSH tunnel service to Aliyun After=network.target[Service] Environment="AUTOSSH_GATETIME=0" ExecStart=/usr/bin/autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -NTR 2222:localhost:22 aliyun_user@aliyun_server_ip -i /home/pi/.ssh/id_rsa User=pi Group=pi Restart=always RestartSec=60[Install] WantedBy=multi-user.target
-
启用并启动服务:
sudo systemctl enable ssh-tunnel.service sudo systemctl start ssh-tunnel.service
外部访问树莓派
从任何机器通过阿里云访问树莓派:
ssh -fN -R 云服务器端口:localhost:树莓派SSH端口 用户名@云服务器IP -p 云服务器SSH端口