Linux内核驱动开发:从传统proc接口到现代seq_file与proc_ops的迁移指南

news2026/5/18 22:43:43
1. 项目概述为什么我们需要关注/proc的新接口如果你在Linux内核驱动开发领域摸爬滚打过几年一定对/proc文件系统这个“老伙计”又爱又恨。爱它是因为在调试和状态监控时它提供了一个极其简单、直观的窗口让你能通过cat、echo这样的基础命令就能和内核空间“对话”。恨它是因为传统的/proc接口也就是我们常用的create_proc_entry、proc_mkdir那一套在代码结构、安全性、性能上确实有点跟不上现代内核开发的节奏了。最近几年内核社区一直在推动/proc接口的现代化改造引入了基于seq_file和proc_ops的新范式。这不仅仅是API名字变了背后是整个设计哲学和最佳实践的升级。很多刚接触驱动开发的朋友可能还在沿用老教程里的方法或者面对新旧API并存的内核源码感到困惑。今天我就结合自己从老接口迁移到新接口的实际经历把这套“新接口”掰开揉碎了讲清楚。你会发现它不仅仅是“能用”更是“好用”和“应该用”。无论是为了写出更健壮、更安全的驱动代码还是为了在面试、Code Review时展现出你对内核最新进展的了解掌握这套新接口都至关重要。2. 新旧接口对比与核心设计思想变迁2.1 传统/proc接口的“痛点”回顾在深入新接口之前我们得先明白老接口为什么会被逐渐弃用。这有助于我们理解新接口设计的出发点。传统的做法主要依赖于linux/proc_fs.h中定义的几个函数和结构体核心是struct proc_dir_entry和struct file_operations的一个简化版。一个典型的创建过程是这样的static int my_proc_read(char *page, char **start, off_t off, int count, int *eof, void *data) { return sprintf(page, “Current value: %d\n”, some_internal_value); } static int my_proc_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { char buf[32]; if (copy_from_user(buf, buffer, min(count, sizeof(buf)-1))) return -EFAULT; buf[min(count, sizeof(buf)-1)] ‘\0’; sscanf(buf, “%d”, some_internal_value); return count; } static int __init my_module_init(void) { struct proc_dir_entry *entry; entry create_proc_entry(“my_driver_status”, 0666, NULL); if (entry) { entry-read_proc my_proc_read; entry-write_proc my_proc_write; entry-data some_private_data; } return 0; }这段代码看起来挺简洁对吧但它隐藏了几个大问题缓冲区管理噩梦read_proc回调需要开发者自己管理一个静态页面缓冲区page。你必须非常小心地计算偏移量off和剩余计数count确保不会写出界。对于输出内容长度不确定的情况比如遍历一个链表代码会变得异常复杂和脆弱。接口不一致/proc的写操作回调write_proc参数和VFS层标准的file_operations.write完全不同增加了学习成本和出错几率。扩展性差很难在这种接口上实现复杂的迭代操作比如输出一个大的列表。虽然可以通过设置*start魔法值来 hack但这并非官方推荐且容易出错。逐渐被弃用在内核源码树中create_proc_entry、read_proc_t等类型和函数早已被标记为“过时”deprecated。继续使用会导致编译警告并且在未来的内核版本中可能会被彻底移除。注意从内核版本5.6左右开始这些老接口的声明已被移至linux/proc_fs.h的#ifdef CONFIG_PROC_FS块之外并明确标记为__deprecated。如果你在较新的内核如5.10上编译使用老接口的模块会看到大量的警告。这不仅是美观问题更是一个明确的信号是时候升级你的代码了。2.2 新接口的核心seq_file与proc_ops新接口的设计哲学是“统一”和“简化”。它主要建立在两大支柱上seq_file接口用于处理所有需要“读取”的/proc文件。它抽象了迭代输出大数据集的过程自动处理缓冲区、偏移和分页让开发者只需关注“如何生成下一个数据项”。proc_ops结构体用于定义文件操作。它替代了老接口中直接赋值read_proc、write_proc的方式提供了一个更接近标准VFSfile_operations的结构但专为/proc优化例如通常不需要实现llseek因为seq_file会处理。这种设计的优势是显而易见的安全性缓冲区由seq_file核心管理基本消除了缓冲区溢出的风险。一致性写操作回调与普通文件操作完全一致使用copy_from_user等标准方法。功能强大轻松支持大文件、动态内容、格式化输出seq_printf。面向未来这是内核社区认可和维护的现代方式。3. 新接口实战一步步创建现代/proc文件理论说再多不如一行代码。我们来看一个完整的例子创建一个名为my_driver_stats的/proc文件它可以读写一个驱动内部的统计计数器。3.1 定义驱动内部数据与proc_ops首先定义我们需要的私有数据和一个proc_ops结构。#include linux/seq_file.h #include linux/proc_fs.h static int driver_counter 0; static struct proc_dir_entry *my_proc_entry; // 这是 seq_file 操作的核心开始迭代 static void *my_seq_start(struct seq_file *s, loff_t *pos) { // 我们只有一个“数据项”即计数器值所以 // 如果 pos 为0返回一个非NULL指针通常返回数据本身或SEQ_START_TOKEN // 如果 pos 1表示迭代结束返回NULL return *pos ? NULL : driver_counter; } // 移动到下一个“数据项” static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos) { (*pos); return NULL; // 因为我们只有一项所以下一次调用就结束 } // 结束迭代通常用于清理这里不需要 static void my_seq_stop(struct seq_file *s, void *v) { // 无操作 } // 显示一个数据项 static int my_seq_show(struct seq_file *s, void *v) { int *counter (int *)v; seq_printf(s, “Driver internal counter: %d\n”, *counter); seq_printf(s, “Last accessed at: %llu ns\n”, ktime_get_ns()); // 示例添加时间戳 return 0; // 成功返回0 } // 将上述操作组装成一个 seq_operations 结构体 static struct seq_operations my_seq_ops { .start my_seq_start, .next my_seq_next, .stop my_seq_stop, .show my_seq_show, }; // 这是打开 /proc 文件时的回调它将 seq_operations 与 seq_file 关联 static int my_proc_open(struct inode *inode, struct file *file) { return seq_open(file, my_seq_ops); } // 定义文件的写操作与标准VFS的write完全一样 static ssize_t my_proc_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char kbuf[32]; int val; if (count sizeof(kbuf)) return -EINVAL; if (copy_from_user(kbuf, buf, count)) return -EFAULT; kbuf[count] ‘\0’; if (kstrtoint(kbuf, 10, val)) // 安全地将字符串转换为整数 return -EINVAL; driver_counter val; return count; // 返回成功写入的字节数 } // 关键的 proc_ops 结构体 static const struct proc_ops my_proc_fops { .proc_open my_proc_open, .proc_read seq_read, // 使用 seq_file 提供的标准读方法 .proc_write my_proc_write, .proc_lseek seq_lseek, // 使用 seq_file 提供的标准定位方法 .proc_release seq_release, // 使用 seq_file 提供的标准释放方法 };代码解读与心得seq_operations定义了如何遍历你的数据。对于简单的单值输出start和next的逻辑看起来有点“大材小用”但这就是seq_file的通用范式。一旦你需要输出一个链表或数组这个框架的优势就体现出来了——你只需要在next里移动到下一个节点即可。my_proc_open是连接proc_ops和seq_operations的桥梁。seq_open这个调用是关键。proc_ops中的成员都以proc_为前缀与通用的file_operations区分开。注意我们直接使用了seq_read、seq_lseek、seq_release这些seq_file框架提供的通用函数这避免了重复造轮子。在写操作中我使用了kstrtoint而不是老旧的sscanf。这是内核推荐的更安全的字符串转换函数能更好地处理错误和边界情况。3.2 创建与注册/proc文件有了proc_ops创建文件就变得非常直观和现代。static int __init my_driver_init(void) { // 使用 proc_create 创建文件并直接关联 proc_ops my_proc_entry proc_create(“my_driver_stats”, 0666, NULL, my_proc_fops); if (!my_proc_entry) { pr_err(“Failed to create /proc/my_driver_stats\n”); return -ENOMEM; } pr_info(“/proc/my_driver_stats created successfully.\n”); return 0; } static void __exit my_driver_exit(void) { if (my_proc_entry) proc_remove(my_proc_entry); // 统一使用 proc_remove 进行清理 pr_info(“Driver module exited.\n”); } module_init(my_driver_init); module_exit(my_driver_exit);关键变化与技巧proc_create这是新接口的核心创建函数。参数依次是文件名、权限位、父目录NULL表示在/proc根目录、proc_ops结构体指针。它一次性完成了老接口中create_proc_entry和后续回调函数赋值的所有工作代码更紧凑。权限位0666表示所有用户可读可写。在生产环境中你需要仔细考虑权限比如0644全局可读仅root可写可能更安全。proc_remove在模块退出时使用这个函数来删除/proc条目。它比老接口的remove_proc_entry更常用是通用的清理函数。3.3 编译、测试与效果验证编译并插入模块后你可以立即进行测试# 1. 查看文件是否存在 $ ls -l /proc/my_driver_stats -rw-rw-rw- 1 root root 0 Apr 26 10:00 /proc/my_driver_stats # 2. 读取文件内容初始值为0 $ cat /proc/my_driver_stats Driver internal counter: 0 Last accessed at: 1714118400123456789 ns # 3. 写入一个新值 $ echo “42” /proc/my_driver_stats # 4. 再次读取确认值已改变 $ cat /proc/my_driver_stats Driver internal counter: 42 Last accessed at: 1714118410987654321 ns看到效果了吗输出是格式化的、清晰的并且我们轻松地添加了时间戳这种额外信息。写入操作也符合Linux命令的直觉。整个过程没有手动计算任何缓冲区偏移代码逻辑清晰安全性也大大提升。4. 进阶技巧与复杂场景处理掌握了基础用法后我们来看看如何用新接口处理更复杂的场景这些才是体现其威力的地方。4.1 输出复杂数据结构链表遍历假设你的驱动维护了一个客户端连接链表struct client_conn现在需要把它输出到/proc。static int my_seq_show(struct seq_file *s, void *v) { struct client_conn *conn (struct client_conn *)v; seq_printf(s, “Client ID: %d, Address: %pI4, Active: %s\n”, conn-id, conn-ip_addr, conn-is_active ? “yes” : “no”); return 0; } static void *my_seq_start(struct seq_file *s, loff_t *pos) { struct client_conn *conn; loff_t i 0; // 加锁保护链表非常重要 mutex_lock(client_list_lock); // 遍历链表直到找到 *pos 指向的位置 list_for_each_entry(conn, client_list, list) { if (i *pos) return conn; // 返回找到的节点 } return NULL; // 链表遍历完毕 } static void *my_seq_next(struct seq_file *s, void *v, loff_t *pos) { struct client_conn *conn (struct client_conn *)v; (*pos); // 获取链表中的下一个节点 conn list_next_entry(conn, list); // 如果下一个节点就是链表头说明结束了 if (conn-list client_list) return NULL; return conn; } static void my_seq_stop(struct seq_file *s, void *v) { // 在迭代结束时释放锁 mutex_unlock(client_list_lock); }实操心得锁是关键start函数里加锁stop函数里释放锁。这确保了在遍历链表的过程中链表结构不会被其他执行线程如中断处理程序、其他系统调用修改从而避免内核崩溃或数据混乱。这是生产级驱动必须考虑的。seq_file会自动处理分页。当用户用cat命令时内核会多次调用start、show、next、stop这个序列每次输出一“页”数据。开发者完全不用操心off和count。list_next_entry是Linux内核链表辅助函数用起来比直接操作list_head更安全方便。4.2 创建目录与多文件组织一个功能完善的驱动往往需要在/proc下创建一个专属目录里面放置多个状态文件。static struct proc_dir_entry *my_proc_dir NULL; static struct proc_dir_entry *entry_stats, *entry_config, *entry_debug; static int __init my_driver_init(void) { // 1. 首先创建目录 my_proc_dir proc_mkdir(“my_awesome_driver”, NULL); if (!my_proc_dir) return -ENOMEM; // 2. 在目录下创建多个文件 entry_stats proc_create(“stats”, 0444, my_proc_dir, stats_proc_fops); entry_config proc_create(“config”, 0644, my_proc_dir, config_proc_fops); entry_debug proc_create(“debug”, 0200, my_proc_dir, debug_proc_fops); // 只写文件 if (!entry_stats || !entry_config || !entry_debug) { // 创建失败需要清理已创建的资源 if (entry_stats) proc_remove(entry_stats); if (entry_config) proc_remove(entry_config); if (entry_debug) proc_remove(entry_debug); proc_remove(my_proc_dir); return -ENOMEM; } return 0; } static void __exit my_driver_exit(void) { // 清理时直接移除目录即可目录下的文件会被自动递归移除 if (my_proc_dir) proc_remove(my_proc_dir); // 不需要再单独移除 entry_stats 等 }重要提示proc_mkdir用于创建目录它返回一个struct proc_dir_entry *可以作为proc_create的父目录参数。错误处理创建多个文件时必须对每个proc_create的返回值做检查。一旦某个失败需要清理之前已经成功创建的所有条目和目录否则会造成/proc文件系统“泄漏”。简化清理在模块退出时只需要移除目录proc_remove(my_proc_dir)内核会自动递归删除该目录下的所有文件。这是一种更简洁且不易出错的清理方式。4.3 使用single_open简化单次输出对于前面那个简单的计数器例子我们用了完整的seq_operations四件套。如果确定你的proc文件只输出一段固定的、不需要迭代的内容比如一次性打印所有统计信息有一个更简单的方案single_open。static int my_single_show(struct seq_file *s, void *unused) { seq_printf(s, “Total interrupts: %lu\n”, interrupt_count); seq_printf(s, “IO errors: %lu\n”, io_error_count); seq_printf(s, “Current mode: %s\n”, operational_mode ? “ACTIVE” : “STANDBY”); // ... 一次性输出所有信息 return 0; } static int my_single_open(struct inode *inode, struct file *file) { return single_open(file, my_single_show, NULL); // 第三个参数是私有数据这里不需要 } static const struct proc_ops my_single_fops { .proc_open my_single_open, .proc_read seq_read, .proc_lseek seq_lseek, .proc_release single_release, // 注意这里用 single_release };使用场景判断用single_open当你的show函数逻辑是固定的一次性生成所有输出数据量不大且不需要根据文件读取位置*pos来动态输出不同内容时。代码量最少。用完整的seq_operations当需要遍历一个很大的数据结构链表、数组、哈希表或者输出内容可能很大需要内核自动分页时。这是更通用、更强大的模式。5. 常见陷阱、调试技巧与性能考量即使理解了原理在实际编码和调试中还是会遇到一些坑。这里分享几个我踩过的雷和总结的经验。5.1 并发与锁的陷阱这是/proc接口开发中最容易出错的地方。/proc文件可以被多个进程同时读取甚至同时读写。问题场景在my_seq_show函数里你正在遍历一个链表并打印每个节点的值。与此同时一个写操作my_proc_write删除了链表中的一个节点。如果没有锁保护seq_show可能访问到一个已经被释放的节点内存导致内核Oops崩溃。解决方案使用内核锁如 mutex 或 spinlock。在seq_start中加锁在seq_stop中解锁确保整个迭代序列的原子性。这是最常用的方法。RCU读-复制-更新如果读操作极其频繁而写操作很少可以考虑使用RCU机制来保护链表。这能极大提升读性能但实现复杂度较高。在seq_start中使用rcu_read_lock在seq_stop中使用rcu_read_unlock写操作则使用list_replace_rcu等。调试技巧可以使用pr_debug在start、stop、show函数中加入日志观察并发访问时函数的调用顺序。也可以使用内核的lockdep锁依赖检测工具来发现潜在的锁顺序死锁问题。5.2 内存分配与格式化输出避免在show函数中分配大内存seq_printf会向seq_file的内部缓冲区写入。这个缓冲区大小是有限的通常一页4KB。如果你试图一次性格式化一个超长的字符串可能会失败。对于很长的行考虑分多次调用seq_printf或者使用seq_puts输出纯字符串用seq_putc输出单个字符。处理seq_printf失败seq_printf实际上会返回一个错误码负数但在很多示例代码中被忽略了。在严谨的驱动中应该检查其返回值。if (seq_printf(s, “Some format: %d\n”, var) 0) return -ENOMEM; // 或其它错误码5.3 文件权限与安全最小权限原则不要随意给0666所有用户可读写。思考这个文件的作用。纯状态信息0444只读。可配置参数0644root可写其他用户只读。调试接口可能触发敏感操作0200仅root可写甚至可以考虑通过内核启动参数来控制是否创建此类调试文件。用户输入验证在write回调中必须严格验证从用户空间copy_from_user过来的数据。检查长度、范围、格式。使用kstrtoint、kstrtoul等安全转换函数而不是不安全的sscanf或简单的simple_strtol。5.4 性能考量/proc不是高性能接口每次cat一个文件都会执行一整套内核函数调用open, read, …。它设计用于偶尔的状态查询和调试绝不能用于高频、实时的数据通道。如果你需要从内核向用户空间高速传输数据应该考虑netlink、relayfs或debugfs特别是debugfs_create_blob用于大块数据。输出内容的计算代价如果seq_show中需要计算复杂的统计信息例如遍历所有数据结构进行求和要考虑这个计算成本。如果该proc文件可能被频繁读取比如被监控脚本每秒调用这种计算可能成为性能瓶颈。可以考虑在驱动内部周期性地更新一个缓存值proc接口只输出这个缓存值。从传统的create_proc_entry到现代的proc_create配合seq_file这不仅仅是API的更新更是内核开发向着更安全、更一致、更强大方向发展的一个缩影。迁移到新接口需要一点学习成本但带来的代码健壮性和可维护性的提升是巨大的。下次当你为驱动添加调试接口时别再犹豫直接使用proc_create和seq_file吧。它会让你的代码看起来更专业也更经得起时间的考验。在实际项目中我通常会为复杂的驱动建立一个proc目录里面用不同的文件来区分统计信息、运行时配置和调试开关这套新接口让这种组织变得非常清晰和易于管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2623067.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…