当前位置: 首页 > news >正文

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

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

  • 一、环境配置(只是概述)
    • 1.VMware Workstation Pro 安装过程
    • 2.Samba 服务器安装与配置过程
  • 二、链表
    • 1、核心基础信息
    • 2、核心链表操作梳理
    • 3、完整典型代码实现
  • 三、文件I/0
    • 1、文件 IO 基础与项目应用场景
    • 2、文件 IO 核心系统调用(无缓存)
  • 四、串口操作
    • 1、核心概念:串口设备文件标识
    • 2、串口核心操作流程(必按此步骤)
    • 3、串口专属关键配置(基于 termios 结构体)
    • 4、串口配置专属函数(4 个核心,头文件termios.h/unistd.h)
    • 5、示例代码
  • 五、库与系统调用
    • 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 available=yes browseable=yes writable=yes

5.重启 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 是否为 NULL

3、完整典型代码实现

  1. 头文件:link_list.h(声明结构体、函数、全局变量)
#ifndefLINK_LIST_H#defineLINK_LIST_H#include<stdio.h>#include<stdlib.h>#include<string.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
  1. 实现文件:link_list.c(实现所有链表操作)
#include"link_list.h"// 定义全局链表头、尾指针linklist envlinkHead,envlinkTail;// 创建空链表:初始化头节点,设置全局头/尾指针linklistCreateEmptyLinklist(){linklist h=(linklist)malloc(sizeof(linknode));if(h==NULL){printf("Create head node error!\n");returnNULL;}h->next=NULL;// 头节点无有效数据,next置NULLenvlinkHead=h;// 全局头指针指向头节点envlinkTail=h;// 全局尾指针初始也指向头节点returnh;}// 链表判空:空返回1,非空返回0intEmptylinklist(linklist h){return(NULL==h->next)?1:0;}// 尾插节点:成功返回0,失败返回-1intInsertLinknode(link_datatype x){// 申请新节点内存linklist q=(linklist)malloc(sizeof(linknode));if(NULL==q){printf("InsertLinknode Error\n");return-1;}// 尾指针的next指向新节点,更新尾指针envlinkTail->next=q;envlinkTail=q;// 给新节点赋值,next置NULL(尾部节点)q->data=x;q->next=NULL;return0;}// 遍历链表,打印所有节点数据voidshow_list(linklist h){if(Emptylinklist(h)){printf("Linklist is empty!\n");return;}inti=0;linklist p=h->next;// 从头节点后第一个有效节点开始while(p!=NULL){printf("node [%d]: msg_type=0x%x, text=%s\n",i,p->data.msg_type,p->data.text);i++;p=p->next;// 指针后移}}// 提取头节点后第一个有效节点,返回该节点(空链表返回NULL)linklistGetLinknode(linklist h){// 链表为空,直接返回NULLif(1==Emptylinklist(h)){returnNULL;}linklist p=h->next;// 指向待提取的节点h->next=p->next;// 头节点next指向提取节点的下一个,从链表移除该节点// 若提取的是最后一个节点,更新尾指针为头节点if(p->next==NULL){envlinkTail=h;}returnp;// 返回提取的节点}
  1. 测试文件:main.c(模拟项目使用,验证所有操作)
#include"link_list.h"intmain(){// 1. 创建空链表linklist head=CreateEmptyLinklist();if(head==NULL){return-1;}printf("===== 创建空链表完成 =====\n");// 2. 定义并赋值3个消息数据,插入链表link_datatype data1,data2,data3;data1.msg_type=0x1;memcpy(data1.text,"1111",sizeof(data1.text));InsertLinknode(data1);data2.msg_type=0x2;memcpy(data2.text,"2222",sizeof(data2.text));InsertLinknode(data2);data3.msg_type=0x3;memcpy(data3.text,"nnnn",sizeof(data3.text));InsertLinknode(data3);printf("===== 3个节点插入完成,遍历链表 =====\n");show_list(head);// 3. 提取节点并释放内存,遍历验证linklist tmpnode=GetLinknode(head);if(tmpnode!=NULL){printf("\n===== 提取节点:text=%s =====\n",tmpnode->data.text);free(tmpnode);// 释放提取的节点内存,避免内存泄漏}printf("===== 提取节点后,遍历链表 =====\n");show_list(head);// 4. 继续提取所有节点,验证空链表tmpnode=GetLinknode(head);free(tmpnode);tmpnode=GetLinknode(head);free(tmpnode);printf("\n===== 提取所有节点后,遍历链表 =====\n");show_list(head);return0;}

4.使用测试

gcc main.c link_list.c-o run./run=====创建空链表完成==========3个节点插入完成,遍历链表=====node[0]:msg_type=0x1,text=1111node[1]:msg_type=0x2,text=2222node[2]:msg_type=0x3,text=nnnn=====提取节点:text=1111==========提取节点后,遍历链表=====node[0]:msg_type=0x2,text=2222node[1]:msg_type=0x3,text=nnnn=====提取所有节点后,遍历链表=====Linklist is empty!

三、文件I/0

1、文件 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、打开标志flags;2. 创建设备时加传:权限mode(8 进制);3. 常用flags:O_RDONLY/WRONLY/RDWR(读写模式,互斥)、O_CREAT(创建文件)、O_APPEND(追加写入)、O_TRUNC(清空原有数据);4. 不可创建设备文件,需用mknod()
close()unistd.h关闭已打开的文件描述符1. 传参:文件描述符fd;2. 进程终止时内核自动关闭其打开的所有文件;3. 关闭后释放文件上的记录锁
read()unistd.h从可读文件 / 设备读取数据到缓冲区1. 传参:fd、数据缓冲区buf、最大读取字节数count;2. 从文件当前位移量开始读取,成功后位移量增加实际读取字节数;3. 返回值:实际读取字节数 / 0(文件末尾)/-1(出错)
write()unistd.h向可写文件 / 设备写入缓冲区数据1. 传参:fd、待写数据缓冲区buf、待写字节数count;2. 带O_APPEND时,每次写操作前将位移量移至文件末尾;3. 返回值通常与count不同,需循环写入确保数据全部写入;4. 常见错误:磁盘满、超出文件长度限制
lseek()sys/types.h、unistd.h显式定位文件的当前位移量1. 传参:fd、偏移量offset(可正可负)、基准点whence;2. 基准点:SEEK_SET(文件开头)、SEEK_CUR(当前位置)、SEEK_END(文件末尾);3. 特性:仅记录位移量,不触发实际 IO;仅对常规文件有效,对 socket / 管道 / FIFO 无效;位移量可大于文件长度,写操作会形成文件空洞

四、串口操作

1、核心概念:串口设备文件标识

Windows下
• COM3 …
Linux下
• ttyUSB0…
• ttyS0…

2、串口核心操作流程(必按此步骤)

打开串口设备文件(open)→ 配置串口参数(波特率/数据位/校验位等)→ 读写串口数据(read/write)→ 关闭串口(close)

3、串口专属关键配置(基于 termios 结构体)

所有串口通信参数(波特率、数据位、超时等)都通过struct termios结构体配置,头文件#include <asm-generic/termbits.h>(都是linux内核自带的),只需关注3 个核心成员,其余默认即可:

  1. c_cflag(控制模式,最核心):配置波特率、数据位、校验位、停止位
    串口通信的基础参数,必配项,常用常量:
    CLOCAL:忽略调制解调器控制线,必设,避免串口被其他进程占用;
    CREAD:启用串口接收功能,必设,否则无法读取串口数据;
    CS8:设置数据位为 8 位(最常用,还有 CS5/CS6/CS7),必设;
    PARENB:启用奇偶校验(无需校验则不设);
    PARODD:搭配 PARENB,设为奇校验(不设则为偶校验);
    CSTOPB:设置 2 个停止位(不设则为 1 个,最常用)。
  2. c_cc [VTIME/VMIN](控制字符,配置读写超时 / 最小字节数)
    open 函数未设置非阻塞模式时才生效,控制 read 读取串口数据的返回规则,串口通信必配,单位 / 含义固定:
    VTIME:等待时间,单位百毫秒(0.1 秒),如 VTIME=5 代表等待 0.5 秒;
    VMIN:要求读取的最小字节数,如 VMIN=24 代表至少读到 24 字节才返回。
    最常用组合(项目解析环境数据用)
    VMIN>0 + VTIME>0:读到VMIN 指定的字节数 或 VTIME 超时,满足其一就返回,适配固定长度的传感器数据读取。
  3. 波特率
    不属于 termios 结构体成员,但需通过专属函数设置,输入 / 输出波特率必须一致(如协调器和 Ubuntu 都设为 9600),常用值:9600、115200。

4、串口配置专属函数(4 个核心,头文件termios.h/unistd.h)

函数核心功能关键参数 / 使用说明
tcgetattr(int fd, struct termios *tp)获取串口当前配置fd:串口文件描述符;tp:termios 结构体指针,用于接收当前配置,配置前必调
tcsetattr(int fd, int opt, const struct termios *tp)设置串口配置opt 设为 TCSANOW(修改立即生效,必选);配置完成后必调,让参数生效
cfsetispeed(struct termios *tp, speed_t speed)设置串口输入波特率speed 设为 B9600/B115200(对应波特率 9600/115200)
cfsetospeed(struct termios *tp, speed_t speed)设置串口输出波特率与输入波特率设为相同值,如 B9600

5、示例代码

#include<fcntl.h>#include<unistd.h>#include<termios.h>intmain(){intfd;structtermiosnewtio;// 1. 打开串口设备(只读,不阻塞)fd=open("/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库函数(用户态) → 再调用系统调用(内核态) 系统调用(内核态) → 操作系统内核
http://www.cnnetsun.cn/news/1646417.html

相关文章:

  • 救命!2026爆款PPT一键制作工具实测,新手也能5分钟出片,告别熬夜手搓无标题
  • 快速验证电路构想:用快马平台一键生成mos管交互仿真原型
  • 3种让Windows用户告别模拟器的APK直装方案:开源工具APK-Installer全解析
  • OpenCore Configurator:告别复杂配置,三步搞定黑苹果引导设置
  • 2025届最火的六大AI学术网站实测分析
  • 2026最权威的AI辅助写作平台实际效果
  • Ostrakon-VL-8B真实案例:某连锁咖啡店通过AI巡检降低人工巡店成本65%
  • VMware vSphere Replication 9.0实战:跨存储灾备从规划到落地
  • 实战应用:基于快马平台开发企业内网服务可用性监控系统
  • SEO_详解SEO工作原理及其对业务增长的重要性
  • 百川2-13B量化模型知识蒸馏:为OpenClaw定制轻量技能模型
  • 【功率预测+储能】储能都配上了,为什么很多场站还是没把收益拉起来?
  • AG32芯片烧录神器:深度体验AGM DAP-LINK下载器的三大核心功能
  • UniversalTime:嵌入式系统毫秒级确定性时间库
  • Flutter环境配置全攻略:flutter doctor命令详解与常见问题解决
  • Python课程大作业避坑指南:以‘世界杯连连看’为例,聊聊初学者常犯的5个错误
  • SOLIDWORKS Simulation 带孔矩形板拓扑优化实战:从建模到轻量化设计
  • (实战经验)LoRA微调ChatGLM-6B避坑指南:FP16梯度问题与tokenizer修复
  • 3分钟掌握前端Word文档生成:用纯JavaScript创建专业DOCX文件
  • DOM 节点
  • 3步拯救无法扫描的二维码:零基础也能学会的修复技巧
  • 深入理解Qt字节序转换:从qFromBigEndian源码看跨平台数据处理的底层实现
  • OpenClaw技能开发:为SecGPT-14B编写自定义漏洞检测模块
  • 从VPULSE到VPWL_ENH:手把手教你用Pspice搭建和仿真5种常见信号源电路
  • 别再乱插了!工程师必懂的接插件选型避坑指南(从BTB到FPC,附选型清单)
  • 自动化工具效能倍增:KeymouseGo从重复操作解放到流程智能化指南
  • Courant-Fischer 定理:从特征值到奇异值的几何视角
  • 智能修复与配置还原:华硕设备显示异常的零基础解决方案
  • 基于氢储能的热电联供型微电网优化调度方法附Matlab代码
  • Z-Image-Turbo_UI界面快速上手:访问localhost:7860,三步生成图片