
这篇文章整理几个在嵌入式Linux项目里高频用到的 C 语言小片段。它们都属于“排障/打点/设备信息”的基础能力:能快速把关键上下文打进日志,定位问题会省很多时间。
先放一个通用思路:无论从 sysfs 读文件,还是用 ioctl 拿网卡信息,核心都是“选数据源 → 读取 → 解析 → 格式化输出”。

内存信息经常用来做“是否内存紧张”的快速判断:比如日志里每隔一段时间打点一次 MemAvailable,很多内存泄漏/缓存膨胀的问题会更容易抓到趋势。
代码:
#include<stdio.h>#include<string.h>#define PROC_MEMINFO "/proc/meminfo"intget_meminfo_kb(long *total_kb, long *avail_kb){ FILE *fp = fopen(PROC_MEMINFO, "r");if (NULL == fp) {printf("fopen error\n");return-1; }char line[256] = {0};long total = -1;long avail = -1;while (NULL != fgets(line, sizeof(line), fp)) {if (0 == strncmp(line, "MemTotal:", 9)) {sscanf(line, "MemTotal: %ld kB", &total); }elseif (0 == strncmp(line, "MemAvailable:", 13)) {sscanf(line, "MemAvailable: %ld kB", &avail); }if (total >= 0 && avail >= 0) {break; } } fclose(fp);if (total < 0 || avail < 0) {printf("parse error\n");return-1; } *total_kb = total; *avail_kb = avail;return0;}intmain(void){long total_kb = 0;long avail_kb = 0; get_meminfo_kb(&total_kb, &avail_kb);printf("mem_total = %ld kB, mem_available = %ld kB\n", total_kb, avail_kb);return0;}注意点:
MemTotal/MemAvailable 在多数发行版都能拿到。应用可以定时获取 CPU 温度,比如性能异常、频繁重启、降频等场景,温度往往是第一批要确认的指标。
代码:
#include<stdio.h> #include<unistd.h>#include<stdlib.h>#include<string.h>#include<errno.h>#define CPU_TEMP_FILE0 "/sys/devices/virtual/thermal/thermal_zone0/temp"structcpu_temperature{int integer_part;int decimal_part;};typedefstructcpu_temperaturecpu_temperature_t;cpu_temperature_tget_cpu_temperature(constchar *_cpu_temp_file){ FILE *fp = NULL;cpu_temperature_t cpu_temperature = {0};int temp = 0; fp = fopen(_cpu_temp_file, "r");if (NULL == fp) {printf("fopen file error\n");return cpu_temperature; }fscanf(fp, "%d", &temp); cpu_temperature.integer_part = temp / 1000; cpu_temperature.decimal_part = temp % 1000 / 100; fclose(fp);return cpu_temperature;}intmain(int arc, char *argv[]){cpu_temperature_t cpu_temperature = {0}; cpu_temperature = get_cpu_temperature(CPU_TEMP_FILE0);printf("cpu_temperature = %d.%d ℃\n", cpu_temperature.integer_part, cpu_temperature.decimal_part);return0;}运行结果:

注意点:
thermal_zoneX 可能不同,建议把路径做成可配置(本文保持写死路径,便于演示)。有时候需要先知道文件大小:例如发送文件、预分配缓冲区、做进度条等。
代码:
#include<sys/stat.h> #include<unistd.h> #include<stdio.h> longget_file_size(constchar *_file_name){ FILE * fp = fopen(_file_name, "r");if (NULL == fp) {printf("fopen error\n");return-1; } fseek(fp, 0L, SEEK_END);long size = ftell(fp); fclose(fp);return size;}intmain(){#define FILE_NAME "./get_file_size"long file_size = get_file_size(FILE_NAME);printf("file_size = %ld\n", file_size);return0;}运行结果:

注意点:
fseek/ftell,对“普通文件”足够直观;更严谨的方式是 stat()(本节不展开)。"rb";Linux 上 "r" 通常也能工作。代码:
#include<stdio.h> #include<unistd.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<sys/time.h>#include<time.h>longlongget_sys_time_ms(void){longlong time_ms = 0;structtimevalsys_current_time; gettimeofday(&sys_current_time, NULL); time_ms = ((longlong)sys_current_time.tv_sec*1000000 + sys_current_time.tv_usec) / 1000;return time_ms;}intmain(int arc, char *argv[]){longlong cur_sys_time = get_sys_time_ms();printf("cur_sys_time = %lld ms\n", cur_sys_time);return0;}运行结果:

注意点:
gettimeofday() 取的是“墙上时间”(可能被 NTP/手动改时间影响)。如果你要测耗时/间隔,更推荐 clock_gettime(CLOCK_MONOTONIC, ...)MAC 地址经常被用作设备唯一标识(或参与生成设备 ID)。这里用 ioctl(SIOCGIFHWADDR) 从指定网卡获取。
代码:
#include<stdio.h>#include<stdint.h>#include<net/if.h>#include<sys/socket.h>#include<sys/ioctl.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h>intget_netif_mac(constchar *_ifr_name, char *_mac){int32_t ret = -1;structifreqm_ifreq;int32_t sock = 0; sock = socket(AF_INET, SOCK_STREAM, 0);if (sock < 0) {printf("socket err\r\n");goto err; }strncpy(m_ifreq.ifr_name, _ifr_name, IFNAMSIZ); m_ifreq.ifr_name[IFNAMSIZ - 1] = 0; ret = ioctl(sock,SIOCGIFHWADDR, &m_ifreq);if (ret < 0) {printf("ioctl err:%d\r\n",ret);goto err; }snprintf((char *)_mac, 32, "%02x%02x%02x%02x%02x%02x", (uint8_t)m_ifreq.ifr_hwaddr.sa_data[0], (uint8_t)m_ifreq.ifr_hwaddr.sa_data[1], (uint8_t)m_ifreq.ifr_hwaddr.sa_data[2], (uint8_t)m_ifreq.ifr_hwaddr.sa_data[3], (uint8_t)m_ifreq.ifr_hwaddr.sa_data[4], (uint8_t)m_ifreq.ifr_hwaddr.sa_data[5]);return0;err:return-1;}intmain(int argc, char **argv){char mac_str[32] = {0}; get_netif_mac("wlan1", mac_str);printf("mac = %s\n", mac_str);return0;}运行结果:

注意点:
eth0/wlan0,也可能是 enp0s3 这类“可预测命名”。ifr_name 有长度限制(IFNAMSIZ),拷贝时要注意结尾 \0。socket() 只用于拿到 fd 给 ioctl 用,不需要真正发包。有时候需要获取本机 IP 做显示、上报、或者打印在启动日志里,现场排障会方便很多。这里用 ioctl(SIOCGIFADDR) 获取指定网卡的 IPv4 地址。
代码:
#include<stdio.h>#include<net/if.h>#include<sys/socket.h>#include<sys/ioctl.h>#include<arpa/inet.h>#include<unistd.h>#include<string.h>intget_local_ip(constchar *_ifr_name, char *_ip){int ret = -1;int sockfd;structsockaddr_insin;structifreqifr; sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd) {printf("socket error\n");return ret; }strncpy(ifr.ifr_name, _ifr_name, IFNAMSIZ); ifr.ifr_name[IFNAMSIZ - 1] = 0;if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {printf("ioctl error\n"); close(sockfd);return ret; }memcpy(&sin, &ifr.ifr_addr, sizeof(sin));int ip_len = snprintf(_ip, 32, "%s", inet_ntoa(sin.sin_addr)); close(sockfd); ret = ip_len;return ret;}intmain(int argc, char **argv){char ip_str[32] = {0}; get_local_ip("wlan1", ip_str);printf("ip = %s\n", ip_str);return0;}运行结果:

注意点:
getifaddrs(),但 ioctl 版在嵌入式里更常见、依赖更少。磁盘空间不足会导致“写文件失败/升级失败/日志丢失”等一堆诡异问题。落盘前先检查一次剩余空间,能少踩很多坑。
代码:
#include<stdio.h>#include<sys/statvfs.h>unsignedlonglongget_fs_free_bytes(constchar *path){structstatvfsst;if (0 != statvfs(path, &st)) {printf("statvfs error\n");return0; }return (unsignedlonglong)st.f_bsize * (unsignedlonglong)st.f_bavail;}intmain(void){unsignedlonglong free_bytes = get_fs_free_bytes("/");printf("free_bytes = %llu\n", free_bytes);return0;}注意点:
statvfs() 可拿到文件系统块大小和可用块数,换算成字节即可。/、/data、/userdata 等,按实际挂载点传入。CPU 使用率建议用“采样两次再算差值”的方式,避免读到的是累计值。
代码:
#include<stdio.h>#include<unistd.h>#define PROC_STAT "/proc/stat"staticintread_cpu_stat(unsignedlonglong *idle, unsignedlonglong *total){ FILE *fp = fopen(PROC_STAT, "r");if (NULL == fp) {printf("fopen error\n");return-1; }unsignedlonglong user=0,nice=0,system=0,idle_v=0,iowait=0,irq=0,softirq=0,steal=0;int ret = fscanf(fp, "cpu %llu %llu %llu %llu %llu %llu %llu %llu", &user,&nice,&system,&idle_v,&iowait,&irq,&softirq,&steal); fclose(fp);if (8 != ret) {printf("fscanf error\n");return-1; } *idle = idle_v + iowait; *total = user + nice + system + idle_v + iowait + irq + softirq + steal;return0;}intmain(void){unsignedlonglong idle1=0,total1=0,idle2=0,total2=0; read_cpu_stat(&idle1, &total1); usleep(200 * 1000); read_cpu_stat(&idle2, &total2);unsignedlonglong idle_delta = idle2 - idle1;unsignedlonglong total_delta = total2 - total1;double cpu_usage = 0;if (total_delta > 0) { cpu_usage = (double)(total_delta - idle_delta) * 100.0 / (double)total_delta; }printf("cpu_usage = %.2f %%\n", cpu_usage);return0;}注意点:
/proc/stat 的 cpu 行是从开机累计到现在的 tick,需要间隔一小段时间读两次再计算。以上就是本次分享的几个小代码片段。可以这些能力做成一个小工具库(带统一的错误码/日志宏),工程里随手就能复用。


