01_字符设备基本概念_从分类到LED控制链路

字符设备基本概念:从分类到 LED 控制链路

课程位置:驱动初级 → 字符设备 → 基本概念 → 设备驱动的分类、设备驱动框架、一切皆文件、字符设备框架的由来。

主要依据:2026-10-05 11:04《字符设备 基本概念》录音转写;对照 I:\Linux内核驱动\学习系统\04_驱动初级\02_字符设备\01_基本概念 下四节老师笔记和截图。

转写存在大量同音字和重复句。本文按老师的讲课顺序整理;"课程示意"和"实际 Linux 机制"分别说明。LED 完整链路是原理演示,不是已经在 Jetson 上完成的 GPIO 点灯实验。

一、这节课的主线

老师从"设备太多,需要分类"讲起,再画出应用、内核、硬件之间的层次;用普通文件的 open/read/write/close 引出"一切皆文件";最后用 LED 说明:应用为什么能把 /dev/led 当文件操作,以及驱动为何要有设备号、字符设备对象和文件操作回调。(录音 00:03---44:25)

一句话串起来:**应用写设备文件 → 内核按设备号找到字符设备驱动 → 驱动解释写入的数据 → 驱动操作 GPIO → LED 电平改变。**中间每一步都需要相应的注册、映射或硬件准备,单有文件名不能点亮 LED。

二、设备驱动为什么分三类

老师按主要访问方式介绍字符、块、网络三类设备,并选择字符设备作为驱动入门。(录音 00:03---03:36)

类型 本节要抓住的特点 课堂例子 学习时的边界
字符设备 通常以字节流或顺序数据的方式提供读写等接口。 串口、键盘、鼠标;LED 可做成字符设备。 "字符"不等于每次只能传 1 字节,也不代表驱动绝不使用缓冲区。摄像头的 /dev/video0 虽是字符设备节点,传输仍按帧等方式组织。
块设备 按块访问,支持按位置访问数据,适合存储。 硬盘、SD 卡、U 盘。 常配合缓存和 I/O 调度;不是所有 Flash 都必然以块设备形式暴露。
网络设备 接入内核网络子系统,收发网络数据包。 有线网卡、Wi-Fi。 应用通常通过 socket 使用网络;不必要求每张网卡都有可 open 的 /dev/xxx 节点。

老师还提到 GPIO、I²C、SPI、Platform 等名称。它们描述总线、控制器或设备组织方式,与上面按用户接口划分的三类不是同一把尺子:例如一个挂在 I²C 上的传感器可以通过字符设备节点向应用提供数据。

三、设备驱动在系统的哪一层

老师用手机"按手电筒按钮"的例子:上层界面提出"灯亮"的需求,内核中的驱动负责把请求变成硬件动作。分层让应用通过受控接口使用设备,也让多个开发者遵守共同的访问方式。(录音 05:24---17:35)
#mermaid-svg-eHwzUmsXgM98D8Ld{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eHwzUmsXgM98D8Ld .error-icon{fill:#552222;}#mermaid-svg-eHwzUmsXgM98D8Ld .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eHwzUmsXgM98D8Ld .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eHwzUmsXgM98D8Ld .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eHwzUmsXgM98D8Ld .marker.cross{stroke:#333333;}#mermaid-svg-eHwzUmsXgM98D8Ld svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eHwzUmsXgM98D8Ld p{margin:0;}#mermaid-svg-eHwzUmsXgM98D8Ld .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster-label text{fill:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster-label span{color:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster-label span p{background-color:transparent;}#mermaid-svg-eHwzUmsXgM98D8Ld .label text,#mermaid-svg-eHwzUmsXgM98D8Ld span{fill:#333;color:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld .node rect,#mermaid-svg-eHwzUmsXgM98D8Ld .node circle,#mermaid-svg-eHwzUmsXgM98D8Ld .node ellipse,#mermaid-svg-eHwzUmsXgM98D8Ld .node polygon,#mermaid-svg-eHwzUmsXgM98D8Ld .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eHwzUmsXgM98D8Ld .rough-node .label text,#mermaid-svg-eHwzUmsXgM98D8Ld .node .label text,#mermaid-svg-eHwzUmsXgM98D8Ld .image-shape .label,#mermaid-svg-eHwzUmsXgM98D8Ld .icon-shape .label{text-anchor:middle;}#mermaid-svg-eHwzUmsXgM98D8Ld .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eHwzUmsXgM98D8Ld .rough-node .label,#mermaid-svg-eHwzUmsXgM98D8Ld .node .label,#mermaid-svg-eHwzUmsXgM98D8Ld .image-shape .label,#mermaid-svg-eHwzUmsXgM98D8Ld .icon-shape .label{text-align:center;}#mermaid-svg-eHwzUmsXgM98D8Ld .node.clickable{cursor:pointer;}#mermaid-svg-eHwzUmsXgM98D8Ld .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eHwzUmsXgM98D8Ld .arrowheadPath{fill:#333333;}#mermaid-svg-eHwzUmsXgM98D8Ld .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eHwzUmsXgM98D8Ld .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eHwzUmsXgM98D8Ld .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eHwzUmsXgM98D8Ld .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eHwzUmsXgM98D8Ld .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eHwzUmsXgM98D8Ld .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster text{fill:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld .cluster span{color:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eHwzUmsXgM98D8Ld .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eHwzUmsXgM98D8Ld rect.text{fill:none;stroke-width:0;}#mermaid-svg-eHwzUmsXgM98D8Ld .icon-shape,#mermaid-svg-eHwzUmsXgM98D8Ld .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eHwzUmsXgM98D8Ld .icon-shape p,#mermaid-svg-eHwzUmsXgM98D8Ld .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eHwzUmsXgM98D8Ld .icon-shape .label rect,#mermaid-svg-eHwzUmsXgM98D8Ld .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eHwzUmsXgM98D8Ld .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eHwzUmsXgM98D8Ld .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eHwzUmsXgM98D8Ld :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open/write/close
应用层:按钮或测试程序
系统调用
VFS:通用文件接口
字符设备:按设备号找到 file_operations
LED 驱动回调:解释命令并控制 GPIO
硬件:GPIO 电平改变,LED 亮或灭

  • 应用层只能使用内核允许的接口,不能把芯片手册上的物理寄存器地址当成普通指针直接写。
  • 系统调用 让 open/write/close 从用户态进入内核态;VFS提供通用文件操作入口,并把设备文件操作分发到相应驱动。
  • 字符设备驱动 属于内核代码。若驱动直接访问内存映射的硬件寄存器,可能需要 ioremap() 建立内核可用的 I/O 映射,再使用合适的 I/O 访问函数;若使用 GPIO 子系统,则通过其接口操作。ioremap()不是"应用地址转内核地址"的步骤,也不是每个 LED 驱动都必须自己调用的函数。Linux 内核 I/O 文档
  • U-Boot发生在 Linux 启动之前,是引导程序;老师把它放在图的硬件附近是为了说明它能直接初始化硬件,不能把 U-Boot 当成 Linux 的"硬件层"或设备文件操作的一环。

四、"一切皆文件"到底是什么意思

老师先用普通文件 /home/yhai/log 演示:open() 得到文件描述符 fd,write(fd, buf, len) 写入,read(fd, buf, len) 读出,最后 close(fd)。写完后同一个打开文件的当前偏移量 通常已经在末尾,立即读取可能得到 0 字节;课堂选择关闭后重新打开,使偏移量回到开头。对于支持定位的普通文件,也可使用 lseek(fd, 0, SEEK_SET)。(录音 19:01---25:52)

这个例子的目的是建立统一操作形式。设备文件 /dev/led 也可以用 open/write/close,但内核最终调用的是驱动提供的函数,数据不一定像普通文件那样存到磁盘。因此"一切皆文件"是统一接口的设计思想,不是说所有设备都等同于普通磁盘文件。(录音 25:52---35:27)

名称 例子 作用 容易混淆的地方
文件路径 /dev/led 应用用它找到设备节点。 名字可改;仅有这个名字不能找到物理 LED。
设备号 dev_t 课堂示意:主号 500、次号 0 设备节点记录的编号,用来关联内核注册的字符设备。 主号主要标识分配给驱动的一组设备号,次号常区分实例;500 只是课堂例子,不能随意占用。
文件描述符 fd open() 返回的非负整数 当前进程用它引用一次已打开的文件。 fd 不是主设备号或次设备号,也不是固定的硬件编号。
驱动回调 open、write、release 等 内核收到文件操作时调用的实现。 设备节点必须能对应到已注册驱动及其回调。

课堂示意命令 mknod /dev/led c 500 0 中,c 表示字符设备节点 ,后两个数是主、次设备号。实际使用时,要先确认驱动确实注册了这组设备号、该编号没有冲突;许多系统会由 udev/mdev 创建节点。touch /dev/led 创建的是普通文件,不能替代字符设备节点。mknod 手册

五、把老师的 LED 例子走完整

老师在"设备驱动框架"和"一切皆文件"中画了 /dev/led,在"字符设备框架的由来"中概括出注册设备号 → 初始化字符设备 → 实现文件操作三步。(录音 14:27---17:35、31:16---33:49、35:47---44:25)把板上的实际路径补全如下:

0. 先明确硬件和协议

确认 LED 接在哪个 GPIO、引脚复用和电气连接、有效电平是高还是低。应用和驱动还要约定:例如写入一个二进制字节 1 表示亮、0 表示灭。这只是例子;若驱动约定接收 ASCII 字符 '1'/'0' 或使用 ioctl,应用必须按该约定发送。

1. 驱动向内核登记设备

驱动申请或注册设备号,例如课程示意的 500:0;准备 struct file_operations,把 .open、.write、.release 等成员指向自己的回调;初始化并添加 struct cdev,让这组设备号与文件操作关联。课程后续章节会具体讲 alloc_chrdev_region() / register_chrdev_region()、cdev_init() 和 cdev_add()。Linux 字符设备 API 文档

驱动还需取得并初始化对应 GPIO 资源,把引脚配置成适当模式。注册设备号解决"内核怎样找到这个驱动",硬件初始化解决"驱动怎样实际控制这个 LED";两件事都不可省。

2. 板上出现匹配的设备节点

/dev/led 必须是设备号与驱动一致的字符设备节点 。它可以由设备管理机制创建;若教学实验手动执行 mknod /dev/led c 500 0,前提是驱动确实注册了 500:0。节点中的编号让 VFS 在打开它时找到对应字符设备;路径中的 led 字样本身并不负责匹配驱动。

3. 应用打开设备

应用调用 fd = open("/dev/led", O_WRONLY)。系统调用进入内核,VFS 查看设备节点的主、次设备号,找到已注册的 cdev 和 file_operations,调用驱动的 .open(若实现了),成功后向应用返回 fd。这个 fd 只在当前进程的文件描述符表里有意义。

4. 应用发送"亮/灭",驱动改变 GPIO

下面是双方约定接收二进制单字节 1/0 时的应用侧示意 ;当前课堂模块没有提供这个接口,不可直接拿来操作之前只打印日志的 led.ko。

c 复制代码
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    unsigned char on = 1;
    int fd = open("/dev/led", O_WRONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }
    ssize_t n = write(fd, &on, sizeof(on));
    if (n != (ssize_t)sizeof(on)) {
        if (n < 0)
            perror("write");
        else
            fprintf(stderr, "short write\n");
        close(fd);
        return 1;
    }
    close(fd);
    return 0;
}

老师板书中的 write(fd, 1) 只是表达"写入 1"的简写;真实系统调用是 write(fd, 缓冲区地址, 字节数),成功时返回实际写入的字节数,应检查返回值。write 手册

完整分发过程是:write() 系统调用 → VFS → 驱动的 .write 回调 → 安全地读取用户传入的数据并解释 1/0 → 通过 GPIO 子系统或已映射的寄存器设置输出电平 → LED 根据电路的有效电平亮或灭。把 on 改为 0 再写一次,驱动按约定执行相反动作。是否真的亮灯,要观察实物或测量引脚,不能仅靠模块加载日志判断。

5. 关闭、卸载与清理

close(fd) 关闭这个文件描述符;当对应的打开文件不再被引用时,若驱动实现了 .release,内核会调用它。close() 本身不必然"把设备让给别人",只有驱动专门实现独占访问时才有这种约束。以后卸载模块时,驱动还需移除 cdev、释放设备号与 GPIO 等资源;在有人使用设备时,卸载可能受模块引用计数阻止。
GPIO 与实体 LED LED 字符设备驱动 系统调用与 VFS 应用 GPIO 与实体 LED LED 字符设备驱动 系统调用与 VFS 应用 #mermaid-svg-qUyzgzj7l9No003W{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-qUyzgzj7l9No003W .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qUyzgzj7l9No003W .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qUyzgzj7l9No003W .error-icon{fill:#552222;}#mermaid-svg-qUyzgzj7l9No003W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qUyzgzj7l9No003W .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qUyzgzj7l9No003W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qUyzgzj7l9No003W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qUyzgzj7l9No003W .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qUyzgzj7l9No003W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qUyzgzj7l9No003W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qUyzgzj7l9No003W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qUyzgzj7l9No003W .marker.cross{stroke:#333333;}#mermaid-svg-qUyzgzj7l9No003W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qUyzgzj7l9No003W p{margin:0;}#mermaid-svg-qUyzgzj7l9No003W .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUyzgzj7l9No003W text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-qUyzgzj7l9No003W .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qUyzgzj7l9No003W .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-qUyzgzj7l9No003W .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-qUyzgzj7l9No003W .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-qUyzgzj7l9No003W #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-qUyzgzj7l9No003W .sequenceNumber{fill:white;}#mermaid-svg-qUyzgzj7l9No003W #sequencenumber{fill:#333;}#mermaid-svg-qUyzgzj7l9No003W #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-qUyzgzj7l9No003W .messageText{fill:#333;stroke:none;}#mermaid-svg-qUyzgzj7l9No003W .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUyzgzj7l9No003W .labelText,#mermaid-svg-qUyzgzj7l9No003W .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-qUyzgzj7l9No003W .loopText,#mermaid-svg-qUyzgzj7l9No003W .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-qUyzgzj7l9No003W .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-qUyzgzj7l9No003W .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-qUyzgzj7l9No003W .noteText,#mermaid-svg-qUyzgzj7l9No003W .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-qUyzgzj7l9No003W .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUyzgzj7l9No003W .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUyzgzj7l9No003W .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-qUyzgzj7l9No003W .actorPopupMenu{position:absolute;}#mermaid-svg-qUyzgzj7l9No003W .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-qUyzgzj7l9No003W .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-qUyzgzj7l9No003W .actor-man circle,#mermaid-svg-qUyzgzj7l9No003W line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-qUyzgzj7l9No003W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open("/dev/led") 按设备号找到 cdev,调用 .open 返回 fd write(fd, &on, 1) 调用 .write 解释数据并设置引脚电平 LED 状态改变 close(fd) 调用 .release(若已实现)

六、老师说的"三步曲"与下一节

课堂三步 它解决的问题 下一步学习重点
① 注册设备号 让内核为字符设备分配或认可一组主、次设备号。 固定编号和动态分配的区别、如何释放编号。
② 初始化字符设备 用 cdev 关联设备号和 file_operations;硬件资源也要单独准备。 cdev_init/cdev_add、失败时清理、GPIO 初始化。
③ 实现文件操作 把通用的 open/read/write/release 等转为具体硬件行为。 回调参数、用户与内核之间的数据传递、ioctl 的控制命令。

ioctl 是老师在结尾提出的特殊控制接口,可用于不能方便地表达为普通读写的数据或配置操作,如串口工作模式;是否需要它应由设备接口设计决定,并非每个 LED 都必须实现。(录音 43:44---44:25)

七、与刚完成的内核模块实验有什么不同

前一节在 Jetson 上实测的 led.ko 已经证明 AArch64 模块可以编译、加载、打印入口日志并卸载 。它没有展示设备号注册、cdev、/dev/led、file_operations 或 GPIO 实际控制。因此那次结果是模块生命周期验收 ,本节是字符设备工作链路的概念学习 ;下一阶段需要逐项实现并验证上述接口,最后才可声称"应用写 /dev/led 控制了实体 LED"。

八、复习时能否独立讲清楚

  1. 为什么当前虚拟机里的 x86-64 led.ko 不能用于 AArch64 Jetson?它与字符设备框架问题有什么区别?
  2. /dev/led、主/次设备号、进程内的 fd、file_operations 各负责哪一段关联?
  3. 为什么 touch /dev/led 不够?为什么手动 mknod 后也不代表 LED 必然能亮?
  4. 从应用 write(fd, &on, 1) 到 GPIO 电平变化,要经过哪些内核环节?
  5. 哪些结论已在板上实测,哪些仍停留在本节的设计示意?
相关推荐
朝朝辞暮i1 小时前
C++ 第 40 章:ROS2 Publisher + Timer + Subscriber + Callback + Executor 完整闭环
开发语言·c++·算法·ros2
唐青枫1 小时前
别只会给页面加 ROLE_ADMIN:Symfony Security 从登录到资源授权实战
php·symfony
专注仿真1 小时前
决策树的算法
开发语言·高性能算法
Evand J1 小时前
【电机滤波例程2】负载转矩增广扩展卡尔曼滤波(EKF)原理与MATLAB例程:五维PMSM状态与负载阶跃估计。订阅专栏后可查看完整代码
开发语言·matlab·电机·ekf·卡尔曼滤波
清水白石0081 小时前
Python 对象模型深度解析:从“一切皆对象”到 id、type、isinstance 底层机制与小整数缓存原理
开发语言·python·缓存
燕落南枝1 小时前
c语言 洛谷P1765手机题解
c语言·开发语言
Rosanci1 小时前
Codex 下载与本地部署实战:从安装到运行全流程指南
开发语言·前端·算法·chatgpt·codex
zhangzeyuaaa1 小时前
深入 Ruby:Block、Proc、Lambda 核心区别与最佳实践
开发语言·前端·ruby
蜗牛互联网2 小时前
Java 17调用Embeddings API:余弦相似度与FAQ拒答阈值
java·开发语言·人工智能