【Linux 物联网网关主控系统-Linux主控部分(一)】

张开发
2026/5/31 22:57:16 15 分钟阅读
【Linux 物联网网关主控系统-Linux主控部分(一)】
Linux 物联网网关主控系统-Linux主控部分一一、环境配置(只是概述)1.VMware Workstation Pro 安装过程2.Samba 服务器安装与配置过程二、链表1、核心基础信息2、核心链表操作梳理3、完整典型代码实现三、文件I/01、文件 IO 基础与项目应用场景2、文件 IO 核心系统调用无缓存四、串口操作1、核心概念串口设备文件标识2、串口核心操作流程必按此步骤3、串口专属关键配置基于 termios 结构体4、串口配置专属函数4 个核心头文件termios.h/unistd.h5、示例代码五、库与系统调用1.两个核心概念2.核心区别一、环境配置(只是概述)1.VMware Workstation Pro 安装过程1.安装前准备获取 VMware15.5 安装包可通过官网https://www.vmware.com/go/getworkstationwin或指定资料包获取。2.许可协议确认启动安装向导阅读并接受 VMWARE 最终用户许可协议点击下一步。3.自定义安装设置选择安装位置默认 C:\Program Files (x86)\VMware\VMware Workstation可选择是否安装增强型键盘驱动完成后下一步接着进行用户体验设置选择是否启动时检查更新、是否加入客户体验提升计划点击下一步。4.快捷方式与安装选择在桌面、开始菜单创建快捷方式确认后开始安装等待虚拟网络驱动等组件安装完成。5.安装完成与激活安装向导完成后点击 “完成”可选择立即输入许可证密钥提供密钥如 UG5J2-0ME12-M89WY-NPWXX-WQH88 等也可选择试用 30 天。2.Samba 服务器安装与配置过程一Ubuntu 侧安装与配置1.安装 Samba 组件执行命令sudo apt-get install samba samba-common自 动安装相关依赖包。2.创建共享目录并授权-新建目录mkdir work-赋予最高权限sudo chmod 777 work-可通过cd work/和pwd验证目录路径默认 /home/peng/work。3.创建 Samba 用户解决创建失败问题-初始创建命令sudo smbpasswd -a yikou设置密码为 1若失败需先创建-系统账号和组。-失败补救sudo groupadd yikou -g6000创建组→sudo useradd yikou -u 6000 -g 6000 -s /sbin/nologin -d /dev/null创建系统用户→再次执行sudo smbpasswd -a yikou设置密码。4.修改 Samba 配置文件打开配置文件sudo vim /etc/samba/smb.conf在文件尾部添加配置[yikou]path/home/peng/work availableyes browseableyes writableyes5.重启 Samba 服务执行/etc/init.d/smbd restart提示 “ok” 即表示服务重启成功。二Windows 侧映射网络驱动器1.验证网络互通在 Windows cmd 中执行ping Ubuntu的IP示例 192.168.0.112确保能正常连通。2.映射网络驱动器-打开 “此电脑”点击 “映射网络驱动器”选择驱动器号示例 Y。-输入文件夹路径\192.168.0.112\yikou点击完成。3.输入凭据访问在弹出的 Windows 安全中心中输入 Samba 用户名yikou和密码1即可访问 Ubuntu 的共享目录实现 Linux 与 Windows 目录内容同步。二、链表1、核心基础信息1. 链表应用背景Linux 内核大量使用链表如 list_head用于设备 / 驱动 / 内存管理、等待队加粗样式列等本项目使用单链表共 2 处应用boa 进程、主控进程核心操作插入节点、读取 / 提取节点。2. 自定义数据结构链表的节点数据和节点本体通过typedef定义是所有操作的基础数据域存储消息类型和消息文本// 链表节点的数据类型存储消息包typedefstructmsg_pack{charmsg_type;// 消息类型1字节chartext[27];// 消息文本27字节}link_datatype;// 链表节点结构体定义typedefstruct_node_{link_datatype data;// 节点数据域struct_node_*next;// 节点指针域指向下一个节点}linknode,*linklist;// linknode节点类型 linklist节点指针类型链表句柄3. 全局链表头 / 尾指针用于标记单链表的头部和尾部方便尾插法插入节点项目核心插入方式linklist envlinkHead,envlinkTail;// 全局变量链表头指针、链表尾指针2、核心链表操作梳理项目实现单链表4 个核心操作均基于尾插法实现插入提取节点为头节点后第一个节点头删法操作逻辑简洁适配项目实际需求各操作的功能、思路如下操作名称函数原型核心功能关键思路创建空链表linklist CreateEmptyLinklist()初始化头节点设置全局头/尾指针返回链表头申请头节点内存→头节点 next 置 NULL→全局头/尾指针指向头节点插入节点尾插int InsertLinknode(link_datatype x)向链表尾部插入新节点传入待存储的消息数据申请新节点内存→尾指针 next 指向新节点→更新尾指针为新节点→赋值数据 next 置 NULL遍历所有节点void show_list(linklist h)遍历整个链表打印所有节点的消息文本从头部 next 开始遍历→循环打印节点数据→指针后移至 NULL 结束提取节点头删linklist GetLinknode(linklist h)提取头节点后第一个有效节点从链表中移除返回该节点空链表返回 NULL判断链表是否为空→指向待提取节点→头节点 next 指向提取节点的下一个→更新尾指针若提取最后一个节点链表判空辅助int Emptylinklist(linklist h)判断链表是否为空空返回 1非空返回 0直接判断头节点 next 是否为 NULL3、完整典型代码实现头文件link_list.h声明结构体、函数、全局变量#ifndefLINK_LIST_H#defineLINK_LIST_H#includestdio.h#includestdlib.h#includestring.h// 链表节点的数据类型消息包typedefstructmsg_pack{charmsg_type;chartext[27];}link_datatype;// 链表节点结构体typedefstruct_node_{link_datatype data;struct_node_*next;}linknode,*linklist;// 全局链表头、尾指针externlinklist envlinkHead,envlinkTail;// 链表核心操作函数声明linklistCreateEmptyLinklist();// 创建空链表intInsertLinknode(link_datatype x);// 尾插节点voidshow_list(linklist h);// 遍历链表intEmptylinklist(linklist h);// 判空辅助linklistGetLinknode(linklist h);// 提取节点#endif实现文件link_list.c实现所有链表操作#includelink_list.h// 定义全局链表头、尾指针linklist envlinkHead,envlinkTail;// 创建空链表初始化头节点设置全局头/尾指针linklistCreateEmptyLinklist(){linklist h(linklist)malloc(sizeof(linknode));if(hNULL){printf(Create head node error!\n);returnNULL;}h-nextNULL;// 头节点无有效数据next置NULLenvlinkHeadh;// 全局头指针指向头节点envlinkTailh;// 全局尾指针初始也指向头节点returnh;}// 链表判空空返回1非空返回0intEmptylinklist(linklist h){return(NULLh-next)?1:0;}// 尾插节点成功返回0失败返回-1intInsertLinknode(link_datatype x){// 申请新节点内存linklist q(linklist)malloc(sizeof(linknode));if(NULLq){printf(InsertLinknode Error\n);return-1;}// 尾指针的next指向新节点更新尾指针envlinkTail-nextq;envlinkTailq;// 给新节点赋值next置NULL尾部节点q-datax;q-nextNULL;return0;}// 遍历链表打印所有节点数据voidshow_list(linklist h){if(Emptylinklist(h)){printf(Linklist is empty!\n);return;}inti0;linklist ph-next;// 从头节点后第一个有效节点开始while(p!NULL){printf(node [%d]: msg_type0x%x, text%s\n,i,p-data.msg_type,p-data.text);i;pp-next;// 指针后移}}// 提取头节点后第一个有效节点返回该节点空链表返回NULLlinklistGetLinknode(linklist h){// 链表为空直接返回NULLif(1Emptylinklist(h)){returnNULL;}linklist ph-next;// 指向待提取的节点h-nextp-next;// 头节点next指向提取节点的下一个从链表移除该节点// 若提取的是最后一个节点更新尾指针为头节点if(p-nextNULL){envlinkTailh;}returnp;// 返回提取的节点}测试文件main.c模拟项目使用验证所有操作#includelink_list.hintmain(){// 1. 创建空链表linklist headCreateEmptyLinklist();if(headNULL){return-1;}printf( 创建空链表完成 \n);// 2. 定义并赋值3个消息数据插入链表link_datatype data1,data2,data3;data1.msg_type0x1;memcpy(data1.text,1111,sizeof(data1.text));InsertLinknode(data1);data2.msg_type0x2;memcpy(data2.text,2222,sizeof(data2.text));InsertLinknode(data2);data3.msg_type0x3;memcpy(data3.text,nnnn,sizeof(data3.text));InsertLinknode(data3);printf( 3个节点插入完成遍历链表 \n);show_list(head);// 3. 提取节点并释放内存遍历验证linklist tmpnodeGetLinknode(head);if(tmpnode!NULL){printf(\n 提取节点text%s \n,tmpnode-data.text);free(tmpnode);// 释放提取的节点内存避免内存泄漏}printf( 提取节点后遍历链表 \n);show_list(head);// 4. 继续提取所有节点验证空链表tmpnodeGetLinknode(head);free(tmpnode);tmpnodeGetLinknode(head);free(tmpnode);printf(\n 提取所有节点后遍历链表 \n);show_list(head);return0;}4.使用测试gcc main.c link_list.c-o run./run创建空链表完成3个节点插入完成遍历链表node[0]:msg_type0x1,text1111node[1]:msg_type0x2,text2222node[2]:msg_type0x3,textnnnn提取节点text1111提取节点后遍历链表node[0]:msg_type0x2,text2222node[1]:msg_type0x3,textnnnn提取所有节点后遍历链表Linklist is empty!三、文件I/01、文件 IO 基础与项目应用场景1.核心定义Linux 遵循一切皆文件文件 IO 即文件的输入输出通过内核提供的系统调用实现对各类文件的读写操作是无缓存的底层 IO 操作。2.项目应用协调器通过串口与 Ubuntu 上位机通信串口被模拟为字符设备文件/dev/ttyUSB0/1、/dev/ttyS0/1应用程序直接通过open/read/write/close等系统调用操作这些设备文件实现数据交互。3.Linux 文件类型共 8 类核心关注与项目相关的字符设备文件c串口、键盘等此外还包括普通文件-、目录文件d、块设备文件b、管道文件p、套接字s、硬 / 软链接等每种文件有专属标识和特性。2、文件 IO 核心系统调用无缓存函数头文件核心功能关键参数 / 特性open()sys/types.h、sys/stat.h、fcntl.h打开 / 创建文件返回文件描述符最小未用非负整数1. 必传文件路径pathname、打开标志flags2. 创建设备时加传权限mode8 进制3. 常用flagsO_RDONLY/WRONLY/RDWR读写模式互斥、O_CREAT创建文件、O_APPEND追加写入、O_TRUNC清空原有数据4. 不可创建设备文件需用mknod()close()unistd.h关闭已打开的文件描述符1. 传参文件描述符fd2. 进程终止时内核自动关闭其打开的所有文件3. 关闭后释放文件上的记录锁read()unistd.h从可读文件 / 设备读取数据到缓冲区1. 传参fd、数据缓冲区buf、最大读取字节数count2. 从文件当前位移量开始读取成功后位移量增加实际读取字节数3. 返回值实际读取字节数 / 0文件末尾/-1出错write()unistd.h向可写文件 / 设备写入缓冲区数据1. 传参fd、待写数据缓冲区buf、待写字节数count2. 带O_APPEND时每次写操作前将位移量移至文件末尾3. 返回值通常与count不同需循环写入确保数据全部写入4. 常见错误磁盘满、超出文件长度限制lseek()sys/types.h、unistd.h显式定位文件的当前位移量1. 传参fd、偏移量offset可正可负、基准点whence2. 基准点SEEK_SET文件开头、SEEK_CUR当前位置、SEEK_END文件末尾3. 特性仅记录位移量不触发实际 IO仅对常规文件有效对 socket / 管道 / FIFO 无效位移量可大于文件长度写操作会形成文件空洞四、串口操作1、核心概念串口设备文件标识Windows下• COM3 …Linux下• ttyUSB0…• ttyS0…2、串口核心操作流程必按此步骤打开串口设备文件open→ 配置串口参数波特率/数据位/校验位等→ 读写串口数据read/write→ 关闭串口close3、串口专属关键配置基于 termios 结构体所有串口通信参数波特率、数据位、超时等都通过struct termios结构体配置头文件#include asm-generic/termbits.h都是linux内核自带的只需关注3 个核心成员其余默认即可c_cflag控制模式最核心配置波特率、数据位、校验位、停止位串口通信的基础参数必配项常用常量CLOCAL忽略调制解调器控制线必设避免串口被其他进程占用CREAD启用串口接收功能必设否则无法读取串口数据CS8设置数据位为 8 位最常用还有 CS5/CS6/CS7必设PARENB启用奇偶校验无需校验则不设PARODD搭配 PARENB设为奇校验不设则为偶校验CSTOPB设置 2 个停止位不设则为 1 个最常用。c_cc [VTIME/VMIN]控制字符配置读写超时 / 最小字节数open 函数未设置非阻塞模式时才生效控制 read 读取串口数据的返回规则串口通信必配单位 / 含义固定VTIME等待时间单位百毫秒0.1 秒如 VTIME5 代表等待 0.5 秒VMIN要求读取的最小字节数如 VMIN24 代表至少读到 24 字节才返回。最常用组合项目解析环境数据用VMIN0 VTIME0读到VMIN 指定的字节数 或 VTIME 超时满足其一就返回适配固定长度的传感器数据读取。波特率不属于 termios 结构体成员但需通过专属函数设置输入 / 输出波特率必须一致如协调器和 Ubuntu 都设为 9600常用值9600、115200。4、串口配置专属函数4 个核心头文件termios.h/unistd.h函数核心功能关键参数 / 使用说明tcgetattr(int fd, struct termios *tp)获取串口当前配置fd串口文件描述符tptermios 结构体指针用于接收当前配置配置前必调tcsetattr(int fd, int opt, const struct termios *tp)设置串口配置opt 设为 TCSANOW修改立即生效必选配置完成后必调让参数生效cfsetispeed(struct termios *tp, speed_t speed)设置串口输入波特率speed 设为 B9600/B115200对应波特率 9600/115200cfsetospeed(struct termios *tp, speed_t speed)设置串口输出波特率与输入波特率设为相同值如 B96005、示例代码#includefcntl.h#includeunistd.h#includetermios.hintmain(){intfd;structtermiosnewtio;// 1. 打开串口设备只读不阻塞fdopen(/dev/ttyUSB0,O_RDWR);// 2. 获取当前串口配置tcgetattr(fd,newtio);// 3. 配置核心参数启用接收、忽略控制线、8位数据位newtio.c_cflag|CLOCAL|CREAD|CS8;// 4. 配置读写规则最小24字节超时500毫秒newtio.c_cc[VMIN]24;newtio.c_cc[VTIME]5;// 5. 设置波特率为9600cfsetispeed(newtio,B9600);cfsetospeed(newtio,B9600);// 6. 使配置立即生效tcsetattr(fd,TCSANOW,newtio);// 7. 读写串口数据与普通文件IO一致charbuf[256];read(fd,buf,256);// 读传感器数据write(fd,cmd,3);// 写指令给协调器// 8. 关闭串口close(fd);return0;}五、库与系统调用1.两个核心概念系统调用System Call系统调用是用户程序访问 Linux 内核的唯一合法入口相当于用户空间和内核空间的 “官方桥梁”。本质内核提供的、运行在内核态的底层接口作用帮用户程序完成硬件操作、文件读写、进程管理等特权操作用户程序不能直接操作硬件必须通过系统调用常用的open()/read()/write()/close() 都是系统调用库函数Library Function库函数是C 语言标准库glibc封装好的 API 集合运行在用户态是给开发者用的 “便捷工具包”。2.核心区别分类特点例子不涉及系统调用的库函数纯用户态操作不访问内核只做数据计算/处理abs()求绝对值、strlen()算字符串长度、toupper()转大写涉及系统调用的库函数底层封装了系统调用最终还是要进入内核态fopen()底层调用open()、fread()底层调用read()、fwrite()底层调用write()、pthread_create()创建线程底层调用系统调用而涉及系统调用的库函数和系统调用的关系应用程序用户态 ↗️ 直接调用系统调用 ↘️ 调用C库函数用户态 → 再调用系统调用内核态 系统调用内核态 → 操作系统内核

更多文章