树莓派4中C程序以服务运行时无法写入USB端口的问题
问题分析与解决方案
核心问题
你的C程序手动运行正常,但通过systemd Path单元触发的服务运行时,出现Bad file descriptor错误,无法写入USB串口/dev/ttyUSB0。根源在于服务触发时机与设备就绪状态不匹配,加上程序本身的串口操作逻辑存在优化空间。
一、修正systemd单元配置
1. 替换Path单元监控目标
原Path单元监控/sys/class/net/usb0(USB以太网接口),但该接口就绪时,USB串口/dev/ttyUSB0可能尚未完成初始化或权限配置。直接监控串口设备节点:
# /etc/systemd/system/usb_trigger.path [Unit] Description=Path unit for LoRa USB serial port [Path] PathExists=/dev/ttyUSB0 Unit=your_service_name.service [Install] WantedBy=multi_user.target
2. 优化Service单元依赖与生命周期
添加设备绑定规则,确保服务与串口设备生命周期同步,避免设备未就绪时启动服务:
# /etc/systemd/system/your_service_name.service [Unit] Description=LoRa CAN Data Forwarder After=sys-devices-platform-serial8250-tty-ttyUSB0.device network.target BindsTo=sys-devices-platform-serial8250-tty-ttyUSB0.device [Service] Type=simple User=fstlisboa Group=dialout ExecStart=/home/fstlisboa/software-13/lora/bin/lora-linux-main.out Restart=always RestartSec=2 [Install] WantedBy=multi_user.target
3. 更可靠的触发方式:使用udev规则
如果串口设备节点名称可能变化(如ttyUSB1),创建udev规则精准匹配LoRa模块,确保设备就绪时触发服务:
创建/etc/udev/rules.d/99-lora-serial.rules:
SUBSYSTEM=="tty", ATTRS{idVendor}=="XXXX", ATTRS{idProduct}=="YYYY", GROUP="dialout", MODE="0660", SYMLINK+="lora_serial", TAG+="systemd", ENV{SYSTEMD_WANTS}="your_service_name.service"
- 替换
XXXX和YYYY为LoRa模块的USB厂商/产品ID(用lsusb命令查看) - 自动创建软链接
/dev/lora_serial,程序中可改用该路径避免设备名称变化问题
二、优化C程序串口操作逻辑
1. 移除无效的内存检查
char msg[strlen(str) + 4];是栈上分配的变长数组,永远不会返回NULL,该判断无效,且可能导致栈溢出。改为动态分配:
void send_msg(Lora lora, char *str) { int serial_port_fd = open_port(USB_PORT); if (serial_port_fd < 0) { return; } size_t msg_len = strlen(str) + 4; char *msg = malloc(msg_len); if (msg == NULL) { perror("ERROR memory allocation failed"); close(serial_port_fd); return; } msg[0] = lora.dest_adress_high; msg[1] = lora.dest_adresss_low; msg[2] = lora.dest_freq; strcpy(msg + 3, str); // 替换原循环,简化逻辑 if (write(serial_port_fd, msg, msg_len) < 0) { perror("ERRO writing msg failed"); } free(msg); close(serial_port_fd); }
2. 避免频繁打开/关闭串口
每次发送都打开关闭串口会增加系统调用开销和出错概率,改为程序初始化时打开一次,全局复用:
int global_serial_fd = -1; int main() { // ...其他初始化逻辑 global_serial_fd = open_port(USB_PORT); if (global_serial_fd < 0) { perror("Failed to open LoRa serial port"); exit(EXIT_FAILURE); } // ...创建线程等操作 // 程序结束时关闭串口 close(global_serial_fd); free(can_lora_array); return 0; } void send_msg(Lora lora, char *str) { if (global_serial_fd < 0) { perror("Serial port not initialized"); return; } size_t msg_len = strlen(str) + 4; char *msg = malloc(msg_len); if (msg == NULL) { perror("ERROR memory allocation failed"); return; } msg[0] = lora.dest_adress_high; msg[1] = lora.dest_adresss_low; msg[2] = lora.dest_freq; strcpy(msg + 3, str); if (write(global_serial_fd, msg, msg_len) < 0) { perror("ERRO writing msg failed"); } free(msg); }
三、验证与调试
- 重新加载systemd配置:
sudo systemctl daemon-reload sudo systemctl enable --now usb_trigger.path your_service_name.service
- 查看服务实时日志:
journalctl -u your_service_name.service -f
- 检查串口设备权限:
ls -l /dev/ttyUSB0
确保组为dialout,权限包含rw。
内容的提问来源于stack exchange,提问作者ElOvo123
相关产品推荐
相关产品推荐

