news 2026/4/22 20:58:20

网卡获取模组ip失败问题解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
网卡获取模组ip失败问题解析

现象

执行网关后端程序,发现ip不显示,ifconfig也不显示ip:

ifconfig enx62fde47ffdbb enx62fde47ffdbb: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 ether 62:fd:e4:7f:fd:bb txqueuelen 1000 (Ethernet) RX packets 150 bytes 11956 (11.9 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 87 bytes 7754 (7.7 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

但是模组已经拿到ip了

+SPCHMDATAINFO:1,1,sipa_eth0,1,172.170.11.114/16,8.8.8.8 8.8.4.4,,1400,1 notification: ^NDISDUN:sipa_usb0,1,enabled,IP,172.170.11.114,255.255.255.255,172.170.11.114,8.8.8.8,8.8.4.4

解决

用gdb检查后端程序,发现问题:mypopen执行失败。

string UsbnameGet() { string res; // string cmd = "dmesg | grep 'CDC NCM' | grep 'register' | awk '{print $5}' | cut -d: -f1"; // 这个获取的是驱动刚注册时获取的名字 // 获取到的就是实际在用的接口名: string cmd = "for i in /sys/class/net/*; do " "readlink $i/device/driver 2>/dev/null | grep -q cdc_ncm && basename $i; " "done"; if(!myPopen(cmd, res)){ hloge("check 5G net card failed"); return ""; } std::cout<<"5G网卡:"<<res<<std::endl; return res; }

可能是指令没权限,可能mypopen有问题,先逐步排查从最有可能的地方入手:

1.修改mypopen为popen

ifconfig正常显示ip,说明确实是mypopen有问题

std::string UsbnameGet() { std::string res; const char* cmd = "for i in /sys/class/net/*; do " "drv=$(readlink $i/device/driver 2>/dev/null); " "echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i; " "done"; FILE* fp = popen(cmd, "r"); if (!fp) { perror("popen failed"); return ""; } char buf[256]; while (fgets(buf, sizeof(buf), fp)) { res += buf; } int rc = pclose(fp); std::cout << "pclose rc=" << rc << std::endl; std::cout << "RAW res=[" << res << "] len=" << res.size() << std::endl; return res; }
ifconfig enx62fde47ffdbb enx62fde47ffdbb: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500 inet 172.170.11.114 netmask 255.255.255.255 broadcast 0.0.0.0 inet6 fe80::a66d:3382:f0a2:fd41 prefixlen 64 scopeid 0x20<link> ether 62:fd:e4:7f:fd:bb txqueuelen 1000 (Ethernet) RX packets 159 bytes 13476 (13.4 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 119 bytes 12114 (12.1 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

mypopen代码如下:

static bool executeChild(const string &cmd, string &res) { FILE *fp = popen(cmd.c_str(),"r"); if(NULL == fp) { // if fork(2) or pipe(2) calls fail, or cannot callocate memory. // it does not set errno if memory allocation fails. // if the underlying fork(2) or pipe(2) fails, errno is set // appropriately. // if the type arguments is invalid, and this condition is detected, // errno is set to EINVAL. res += static_cast<string>("popen failed. ") + strerror(errno); return false; } char buffer[512] = {0}; while (fgets(buffer, sizeof(buffer), fp) != NULL) { res += buffer; } int status = pclose(fp); //❗❗❗❗❗❗❗逻辑有严重问题❗❗❗❗❗❗❗❗❗❗ if (status == 0) { return true; } if (status == -1) { res += static_cast<string>(" failed to get child status: ") + strerror(errno); } else { if (WIFEXITED(status)) { // 命令不存在,exit_status = 127; 命令不存在或无权限执行,仍然是执行成功,只是exit_status>0 res += " normal termination, exit_status = " + to_string(WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { res += " termination by a signal, signal number = " + to_string(WTERMSIG(status)); } else if (WIFSTOPPED(status)) { res += " the child is stopped, status = " + to_string(WSTOPSIG(status)); } else { res += static_cast<string>(" unknown error: ") + strerror(errno); } } return false; } bool myPopen(string cmd, string &res, int maxTimes) { if (cmd.empty()) { hloge("myPopen cmd null"); return false; } cmd += " 2>&1"; for(int tryTimes = 0; tryTimes < maxTimes; tryTimes++) { bool success = executeChild(cmd, res); // ❗❗❗❗严重问题❗❗❗❗❗ if (success) { res = hv::rtrim(res); return true; } hlogi("mySystem cmd: %s, err msg: %s, try times: %d", cmd.c_str(), res.c_str(), tryTimes+1); res.clear(); } hloge("mySystem cmd: %s, failed", cmd.c_str()); return false; } bool mySystem(string cmd) { string res; return myPopen(cmd, res); }

我的指令是:

for i in /sys/class/net/*; do drv=$(readlink $i/device/driver 2>/dev/null) echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i done

grep -q:匹配到,退出码0,没匹配到,退出码1.

for循环里,只要某次grep -q没匹配,最后一个退出码可能是1

但stdout里早就已经输出了正确的enx62fde47ffdbb\n

这在shell里是完全成功的,但在executeChild()里被当成“失败”

还把stderr 合并进来了,雪上加霜:

cmd += " 2>&1";

stderr 的提示,shell 的调试信息,都被拼进res

然后又在失败分支里继续拼:

res += " normal termination, exit_status = X";

res被污染
更不可能“等于期望的接口名”

修改:

static bool executeChild(const string &cmd, string &res) { FILE *fp = popen(cmd.c_str(),"r"); if (!fp) { res += string("popen failed: ") + strerror(errno); return false; } char buffer[512]; while (fgets(buffer, sizeof(buffer), fp)) { res += buffer; } int status = pclose(fp); // 仅用于日志 if (status != 0) { if (WIFEXITED(status)) { hlogi("cmd exit_status=%d", WEXITSTATUS(status)); } } // ✅ 不用 exit code 决定成败 return !res.empty(); }

问题

按照错误码本身没什么问题呀?

for 循环里最后一次命令的退出码 ≠ 你想表达的“整体成功/失败”

  • for 循环 + grep -q → 非常不适合用 exit code 判断成功

  • cmd 是“语义正确的”,但“退出码语义不可靠”

这个是经典的shell坑,shell的语义本身就不是想象中的“整体成功或失败”

我的指令:

for i in /sys/class/net/*; do drv=$(readlink $i/device/driver 2>/dev/null) echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i done

shell 的退出码规则只有一句话

整个 for 循环的退出码 = 最后一次执行的那条“简单命令”的退出码

不是:

  • 有没有输出

  • 有没有“某一次成功”

  • 有没有basename打印

而是:

最后一个grep -Eq的返回值

系统网卡顺序是:

lo enP5p1s0f0 enP5p1s0f1 ... enx62fde47ffdbb ← 这是 cdc_ncm usb0 usb1 ← 最后一个

执行过程是:

1️⃣ 前面一堆接口
grep -Eq不匹配
→ exit code = 1

2️⃣ 执行到enx62fde47ffdbb
grep -Eq匹配成功
basename被执行
stdout 正确输出enx62fde47ffdbb

3️⃣ 接着继续循环(for 不会停)

4️⃣ 最后一个接口(比如usb1
grep -Eq不匹配
→ exit code = 1

最终结果(致命点)

  • stdout:有我想要的内容

    enx62fde47ffdbb

  • shell 最终退出码:1

这在shell语义里是完全合理的,但是被executeChild当成失败

所以:

不要用 exit code 判命令

C++里已经有:

res += buffer;

正确的工程判断是:

判断项适合吗
popen 是否成功
stdout 是否非空
shell exit code❌(在这个场景)

修改后崩溃!

由于把通用执行器改了,影响了全局:

「exit code == 0」改成了「stdout 非空」

命令实际上成功了,但被当成失败,后续流程被中断。

程序崩溃没有ip,所以只能改回原来的通用执行器,修改UsbnameGet(),使其忽略返回值或者用popen代替。如果没有匹配到,也是返回空。

std::string UsbnameGet() { std::string res; const char* cmd = "for i in /sys/class/net/*; do " "drv=$(readlink $i/device/driver 2>/dev/null); " "echo $drv | grep -Eq 'cdc_ncm|cdc_ether|rndis_host' && basename $i; " "done"; FILE* fp = popen(cmd, "r"); if (!fp) { perror("popen failed"); return ""; } char buf[256]; while (fgets(buf, sizeof(buf), fp)) { res += buf; } int rc = pclose(fp); std::cout << "pclose rc=" << rc << std::endl; std::cout << "RAW res=[" << res << "] len=" << res.size() << std::endl; return res; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 12:36:20

从零开始搭建视觉辅助系统:Qwen3-VL-8B实战案例

从零开始搭建视觉辅助系统&#xff1a;Qwen3-VL-8B实战案例 在电商后台&#xff0c;运营人员正为上千件新品上传图片、逐一手动填写“适用场合”“面料材质”等字段——这曾是每个零售平台都绕不开的繁琐流程。如今&#xff0c;只需一张图加一句提示语&#xff0c;AI就能自动生…

作者头像 李华
网站建设 2026/4/23 12:36:20

java计算机毕业设计时间银行管理系统 基于SpringBoot的社区时间币互助养老平台的设计与实现 面向志愿服务的“时间储蓄”信息管理平台的设计与实现

计算机毕业设计时间银行管理系统75j579&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。老龄化加速空巢率攀升&#xff0c;传统居家养老资金池捉襟见肘&#xff0c;“先存时间、后…

作者头像 李华
网站建设 2026/4/23 11:12:07

CVE-2025-14639:itsourcecode学生管理系统的SQL注入漏洞剖析与应对

CVE-2025-14639: SQL Injection in itsourcecode Student Management System 严重性&#xff1a; 中等 类型&#xff1a; 漏洞 CVE-2025-14639 在itsourcecode Student Management System 1.0中发现一个漏洞。受影响的是文件/uprec.php中的一个未知功能。对参数ID进行操作会导致…

作者头像 李华
网站建设 2026/4/23 11:13:18

百度SEO优化建议:提升Qwen3-32B相关内容排名

Qwen3-32B 模型深度解析&#xff1a;高性能开源大模型的实战价值 在企业智能化转型加速的今天&#xff0c;如何在控制成本的同时获得接近顶级闭源模型的语言能力&#xff0c;成为技术决策者面临的核心挑战。GPT-4 等闭源方案虽性能卓越&#xff0c;但高昂的调用费用、数据外泄风…

作者头像 李华
网站建设 2026/4/23 12:52:20

Latex排版助力科研:结合PyTorch实验结果生成高质量论文

LaTeX 与 PyTorch 联动&#xff1a;打造可复现的科研论文自动化工作流 在深度学习研究中&#xff0c;一个常见的场景是&#xff1a;你刚刚完成了一组实验&#xff0c;终端里滚动着最新的准确率数字&#xff0c;Jupyter Notebook 中散落着几张训练曲线图。接下来呢&#xff1f;…

作者头像 李华