(Linux与计算机网络)IO模型

5种IO模型

当我们想要进行IO操作时,不一定有数据让我们读取,也不一定有空间放我们写入的数据,所以IO操作除了要拷贝数据,还要等待时机,即等待数据或者空间就绪。

IO效率高指的是单位时间内传输的数据量大。IO效率低下一般是由于等待的时间过长。单位时间内IO操作的等待时长越少,IO就越高效。因此,阻塞IO和非阻塞IO的IO效率是没有区别的,只是后者可以利用等待的时间去做其它事,提高了整理的效率,但没有提高IO的效率。

IO肯定需要等待时机,但是不同的IO模型处理等待的方式不同。阻塞IO很简单,就是一直检测是否就绪,一直等,非阻塞IO则是每次只检测一次,若失败了就直接返回,先去执行其它任务,执行完了再检测,如此循环。信号驱动IO直接连检测都不检测,让内核通过信号通知,收到信号再处理。多路复用比较特殊,它一次检测多个IO通道,哪个先好就先处理哪个。前面4种由进程亲自处理IO操作的IO模型都属于同步IO,异步IO则是直接让内核去进行IO操作,完成了再通知。

阻塞式IO非常简单,信号驱动IO和异步IO已经很少使用,我们主要讲多路转接和非阻塞IO

非阻塞IO

Linux系统下,套接字socket、管道pipe、命名管道FIFO等都是被视为文件管理的,而用户通过文件描述符对其进行IO,所以,一个文件描述符就对应一个IO通道。我们知道,用户通过文件描述符进行IO操作,其底层就是通过file结构体进行IO。file结构体的成员中有一个文件状态标记,记录了该文件是否使用阻塞IO。通过接口 fcntl 获取并修改文件状态标记,再设置回去,就可以将 对该文件的IO操作 设置为非阻塞,从而进行非阻塞IO。

fcntl接口如下,第一个参数是要操作的文件的文件描述符,传入op的值不同,后面追加的参数就不同。表示获取和设置文件状态标记的op值分别为F_GETFL和F_SETFL。

使用示例如下,第3行调用fcntl时,会获取文件状态标记(是一个位图),并通过返回值返回。

//将fd对应的文件设置为非阻塞 void SetNoBlock(int fd) { int fl = fcntl(fd, F_GETFL); //获取文件状态标记 if (fl < 0) { perror("fcntl"); return; } fcntl(fd, F_SETFL, fl | O_NONBLOCK);//增设非阻塞 }

将文件设置为非阻塞后,IO接口的使用也要有所调整。例如使用read读取非阻塞式的文件时,在很多种情况下返回的都是-1,如读取出错和数据未就绪,为了区分,read会修改全局变量errno,即设置一个错误码,通过错误码的值即可区分返回-1时的具体情况。在读取非阻塞式文件时,要根据errno的值对各个情况分别处理。

多路转接

select

基本用法

多路转接的系统调用接口是select,它会一次检测多个fd(文件描述符),在设定的时间内有fd就绪时就会返回,通知调用方有哪些fd已经就绪了。

第一个参数nfds是要关心的fd中,最大的fd的值+1,用于底层遍历文件描述符表。

最后一个参数timeout是用timeval结构体表示的时间,如下图,单位分别是秒和微秒。如果在timeout表示的时间内没有一个fd就绪,select就会立即返回0,表示超时。如果将timeout的两个成员设置为0,即等待时间为0,select会将每个fd检测一遍,如果没有就绪就立即返回,即非阻塞式检测;如果为nullptr,则select会一直等待直到有fd就绪,即阻塞式检测。

select的返回值有三种,大于0时表示的是就绪的fd个数,等于0表示的是超时,即在timeout指定的时间内没有fd就绪,小于0为报错。

我们知道不同的IO操作等待的事件不同,读取操作等待的是底层有数据,这个被称为读事件就绪,写入操作等待的是底层有空间,它被称为写事件就绪。在select接口中,第二个参数readfds就用于指定 要检测读事件就绪的fd,第三个参数writefds则是指定 要检测写事件就绪的fd。而第四个参数比较特殊,它用于指定 要检测异常状态的fd,如TCP协议的socket收到紧急数据时就会进入异常状态,这个暂时不管。

这三个参数即是输入型参数也是输出型参数,它们的类型都是fd_set*,fd_set是位图结构。以readfds为例,输入时,位图第一位为1表示0号fd要检测读事件就绪,为0则不管该fd,第二位对应的是1号fd,第三位对应2号,以此类推,如下图

检测到有fd就绪,select返回时,位图的某一位为1就表示对应的文件描述符已就绪,不过具体是哪一位需要我们自己遍历一遍位图才知道。其它两个的用法也是类似。

fd_set不能直接用位操作修改,要用系统提供的宏,如下。

void FD_CLR(int fd, fd_set *set); //移除set中对fd的标记,即对应位设为0 int FD_ISSET(int fd, fd_set *set); //检测set中是否标记了fd void FD_SET(int fd, fd_set *set); //将fd加入位图set,即对应位设为1 void FD_ZERO(fd_set *set); //清空set,将位图所有位设为0

注意事项

使用select需要注意几点,select会修改位图,所以要用一个辅助数组存储所有要关心的文件描述符。每次调用select之前,通过辅助数组更新select参数的位图。其次,如果要通过select实现多路转接,那么进行读取操作时,默认读事件不就绪,必须先由select等待,就绪了再进行读取。其它多路转接的接口也是如此。

简单来说就是所有要进行读取的文件描述符都要让select关心,待其返回后再根据位图确认是哪个fd就绪,再调用对应的处理函数进行读取。如TCP的accept获取连接,底层要有连接才能获取并返回,本质就是在等待读事件,所以监听套接字也要让select关心。只要想读取,就要用select确认就绪。在TCP通信中,由于不需要等待用户发送数据,即使是单进程,selectserver也可以处理多个连接。

写入则有些不同,多路转接中,一般默认写事件是就绪的,在网络通信中就是默认发送缓冲区有空间,不一定要设置进selet或其它多路转接的接口进行等待,按需设置即可。这是因为发送缓冲区一般不会写满,如果设置关心写事件,那么基本每次调用多路转接的接口,就会有写事件就绪。所以,直接写入即可,如果写满了,再把写关心设置进epoll。这样一次可能无法完成写入,需要循环写入,并根据实际写入的大小,删除或跳过缓冲中已经写入的部分。还要注意设置写事件关心后,如果后面完成了发送,要把去掉写事件关心。

select有许多缺点,最主要的有两点,其一是由于fd_set的大小有限,所以select能关心的fd数量(每次等待的fd数量)有限。最多1024个。其二是由于输入和输出位置重叠,每次调用select前都需要重新更新位图。为了更高效地使用多路转接,后来又引入了poll,在poll之后还有更高级的epoll。

poll

poll也是一个多路转接的接口如下,第一个参数fds是数组,用于设置要关心的fd和事件,后面展开讲。第二个参数nfds则是数组fds中元素的个数。timeout用于表示超时时间(单位:ms),设为-1表示阻塞式等待。

struct pollfd定义如下,我们使用poll时,在外部创建好struct pollfd数组,为每一个要关心的fd初始化数组中的sruct pollfd,完成后将数组传入poll的参数fds,参数nfds填写数组中有效的pollfd数量即可。

struct pollfd { int fd; /* 文件描述符 */ short events; /* 要关心的事件*/ short revents; /* poll返回时,就绪的事件*/ };

poll内部会根据数组中每一个pollfd的events确定该fd要关心的事件,各个值对应的事件如下,一般只需注意最上面的POLLIN和最下面的POLLOUT即可,分别表示关心读事件和写事件。如果把short类型看成位图,下表中不同事件的值只占位图中的一位。所以要关心一个fd的多个事件时,就用按位或进行合并。

poll返回时,会将就绪事件的值设置进文件描述符对应的pollfd的revent成员中。我们在调用前,初始化数组时不用管这个成员。

不难发现,相比于select,poll将输入和输出的位置分离了,调用前不需要什么重置操作,更加方便。并且由于使用的是数组,所以等待的fd个数没有上限,但是当数量过大时性能仍然会下降。其次,每次调用poll时,需要把大量的pollfd结构从用户态拷贝到内核中(poll是系统调用接口,内核和用户的内存是相互隔离的)。而且大量fd在同一时刻可能只有少数处于就绪状态,随着要监视的描述符数量增长,poll的效率也会不断下降。为此,后来又引入了epoll。

epoll

epoll底层维护了一个红黑树和一个就绪队列,并且不再是使用单一的接口,我们先了解主要的几个接口的大致用法。

epoll_create创建epoll

创建epoll使用的是epoll_create,返回的是epoll结构本身的文件描述符,用于后面管理epoll。参数size现在在epoll_create内部已经被忽略了,不用管,随便设置一个正整数即可。

epoll_ctl控制epoll

控制epoll(就是操作其内部的fd)的接口是epoll_ctl,该接口成功返回0失败返回-1,第一个参数传入前面epoll_create返回的epoll文件描述符,第二个是操作选项,有三种,分别是增添fd的EPOLL_CTL_ADD、修改fd要关心的事件的EPOLL_CTL_MOD、移除关心的fd的EPOLL_CTL_DEL(此时event可以为NULL)。要注意,用epoll_ctl移除对fd的关心时,fd必须是有效的,不能关闭fd后才调用epoll_ctl移除关心。

第三个参数用于指定要操作的fd,最后一个参数用于指定要操作的事件(如读就绪、写就绪)。

struct epoll_event的定义如下,event用于指定事件,具体的值及其解释在后面。成员data是epoll_data联合体类型,用来存储epoll_event的相关信息,由用户自己设置,epoll内部不会修改也不会使用,最主要的就是存储epoll_event属于哪个fd。后面调用epoll_wait获取就绪的事件时,只会返回epoll_event,此时就需要通过data来确认是哪个fd,在TCP通信中,可能还需要保存客户端信息。需要存储多种信息时,可以定义一个结构体,把data设置为该结构体的指针。

struct epoll_event { uint32_t events; /* 用于指定事件 */ epoll_data_t data; /*用户自定义的数据,内核(epoll)不会修改 */ }; union epoll_data { void *ptr; int fd; uint32_t u32; uint64_t u64; };

成员events用法如下,知道最上面两个即可。

宏值(十六进制)意义
EPOLLIN0x001文件描述符可读(包括对端关闭连接、有新数据到达)
EPOLLOUT0x004文件描述符可写(发送缓冲区有空间,可以写入数据)
EPOLLRDHUP0x2000对端关闭连接或半关闭(TCP 收到 FIN,需要 2.6.17+ 内核)
EPOLLPRI0x002有紧急数据可读(如 TCP 带外数据)
EPOLLERR0x008错误发生(默认监听,无需显式添加)
EPOLLHUP0x010挂起(对端主动关闭连接,管道或 socket 被挂断,默认监听)
EPOLLET0x80000000边缘触发模式(Edge Triggered,默认是水平触发 LT)
EPOLLONESHOT0x40000000一次性触发(事件触发一次后自动从监听集合中移除)

epoll_wait等待事件

等待事件的接口是epoll_wait,第一个参数传入epoll自己的文件描述符,第二个参数events是输出型参数,需要用户创建数组并传入,epoll_wait会在events内填写已就绪的文件描述符,maxevents用于表示数组的大小,它决定了一次最多能返回多少事件。

timeout用于设置超时时间(单位:ms),返回值有三种,大于0时表示的是就绪的fd个数,等于0表示的是超时,即在timeout指定的时间内没有fd就绪,小于0为报错。

IO就绪时的通知机制

在了解epoll底层之前,我们需要先了解Linux读写事件就绪的通知机制。我们知道,当文件可读或可写时,需要通知上层进行相应的IO操作。但是要明确的是,对普通文件的读写几乎不会阻塞,想IO就可以IO,不需要等待,更不需要通知,下面的“文件”指的是socket的接收、发送缓冲区、管道pipe、IO设备(如键盘)的缓冲区等被封装成文件的IO对象。

在内核中,每个文件都有自己的等待队列,如果有进程要对该文件进行阻塞式IO,对应的IO系统调用(如select)会创建一个节点,里面存放回调方法,将其接入该文件的等待队列,然后进入阻塞等待,此时进程就阻塞住了。文件就绪时,内核就会唤醒对应的进程,调用等待队列中节点的回调方法,完成IO操作。

前面提到可以通过修改file结构体的文件状态标记,将对该文件的IO设置为非阻塞。但是如果参数指定要进行阻塞等待(timeout != 0),即使文件本身是非阻塞模式,select、epoll_wait等多路转接接口仍然会按照其 timeout 参数的要求阻塞等待事件。文件非阻塞模式不影响多路转接的阻塞行为。最后,一个文件有多个等待队列,例如读取和写入的等待队列就不是同一个。

epoll模型

epoll底层是一个红黑树和一个就绪队列,红黑树中一个节点对应一个要关心的fd,而就绪队列中的一个节点表示一个就绪的fd。调用epoll_create时就会创建eventpoll结构体(下图右上角),其内部包含了红黑树的根节点指针和就绪队列的头节点指针,一开始没有关心的fd时,两个数据结构都是空的。当我们调用epoll_ctl添加要关心的fd时,其内部会为该fd创建一个结构体epitem,将其成员rbn接入红黑树。

当调用epoll_wait时,其内部就是在检查就绪队列是否为空,如果不为空,就将就绪队列中的节点对应的epitem拷贝到用户从参数传入的缓冲区,如果为空,则直接阻塞。

前面提到,对文件进行阻塞式IO时,需要对应的系统调用向其等待队列添加节点,文件就绪时通过调用节点的回调方法唤醒进程,进行IO。我们调用epoll_ctl添加关心的fd时,其内部就会进行上述的操作,比较特殊的是,epoll_ctl在等待队列中注册的回调方法会唤醒进程(如果文件就绪时进程被epoll_wait阻塞),但不会进行IO,毕竟epoll_ctl 就不是用来读写的。这个回调方法会将红黑树中,对应节点的epitem的rdllink成员接入就绪队列。简单来说就是让epitem同时处于红黑树和就绪队列(一开始只在红黑树里),这个操作我们简称激活节点。

那么怎么知道要激活哪个节点呢?在调用epoll_ctl时,其底层除了创建epitem结构体,将其成员rbn设为红黑树的节点,还会为该节点注册一个回调函数,这个回调函数专门用于激活该节点,将该回调函数接入对应的等待队列后,文件就绪、调用回调时就会激活红黑树的对应节点。 这个红黑树+就绪队列+回调机制的模型就是epoll模型。

这种将就绪的数据放入缓存队列的机制和生产者消费者相似,不仅如此,和生产者消费者模型一样,epoll结构也是线程安全的。

select和poll效率低下,因为其原理是遍历一遍用户传入的fd,向所有对应的等待队列注册回调,文件就绪后还要将回调方法从等待队列中移除,否则如果下次用户要关心的fd不同了,进程会被不再关心的fd唤醒。并且,每次调用都需要把用户传入的fd信息全部拷贝到内核的内存。因为select和poll是系统调用接口,系统调用使用的是内核的内存,与用户的内存是相互分隔的。

而epoll只有用户调用epoll_clt增加要关心的节点时,才会将fd信息拷贝到内核,epoll_wait每次只需查看就绪队列,不用遍历所有fd,并且只有用户调用epoll_ctl删除fd时,才会移除fd对应文件的等待队列的回调方法。

epoll工作模式

epoll有两种工作模式,一种是水平触发(LT,Level Triggerred),一种是边缘触发(ET,Edge Triggered)。

在LT模式下,用户调用epoll_wait获取就绪队列中的就绪事件后,只有当用户处理完就绪事件,内核才会将对应的事件从就绪队列中移除。例如当socket的接收缓冲区收到数据后,内核调用等待队列的回调,将epoll红黑树的对应epitem节点激活到就绪队列,epoll_wait将就绪队列的节点对应的事件返回,此时内核不会立即将就绪队列中的节点移除,而是动态检查底层的接收缓冲区中的数据是否读取完毕,读取完毕了才会移除,否则不会。没有移除时,用户再次调用epoll_wait,由于就绪队列中仍有该事件的节点,所以epoll_wait仍会返回该事件。那么此时在用户看来,LT模式下,就算一次没有把数据读取完,下次epoll_wait还会返回该事件,可以慢慢处理数据。

如果是在ET模式下则相反,epoll_wait将就绪队列中的事件返回后,不管用户会不会处理完,内核会立即将队列中对应的节点移除,下次调用epoll_wait时,epoll_wait就不会把该事件返回了,这就迫使用户必须一次将数据读取完,由于缓冲区中的数据可能会很多,用户用于暂存数据的缓存不够,所以必须循环读取。而写入操作相比与LT模式则没什么变化,因为一次写入没有把数据写完是底层缓冲区的问题,不是用户的问题,缓冲区空间不够了才会一次写不完,把缓冲区写满也算处理完写事件。

需要补充的是,epoll结构是在内核中维护,内核随时可以操作就绪队列,所以即使用户没有再调用epoll_wait或者其它的接口,内核仍然可以选择是否移除就绪队列中的节点。

使用ET模式需要注意,要让epoll关心的所有fd必须设置为非阻塞。我们知道ET模式下是循环读取的,如果文件是阻塞式的,那么当缓冲区读完时,用于读取数据的接口(如read、recv)就会阻塞住,只要进程被IO接口阻塞了,那就不是多路转接。

ET与LT相比看起来没什么优势,但它可以通过独特的机制让程序员必须一次处理完所有数据,在TCP通信中,这样可以高效利用缓冲区空间、提高发送量,进而提高TCP的IO效率。

相关新闻

提示词生成代码的15个质量底线:从语义偏移到代码审查实战

提示词生成代码的15个质量底线:从语义偏移到代码审查实战

我先说一个真实感受&#xff1a;用提示词生成代码&#xff0c;最大的风险不是“生成的代码跑不通”&#xff0c;而是“跑通了&#xff0c;但你觉得它没问题”。我见过很多次这样的场景——开发者把需求丢给大模型&#xff0c;拿到一段看似完整、甚至带注释的函数&#xff0c;直…

2026/10/20 10:49:41 阅读更多 →
TCP三次握手背后的设计逻辑:从网络八股文到排查工具箱

TCP三次握手背后的设计逻辑:从网络八股文到排查工具箱

聊个我自己的面试糗事。某次面后端岗&#xff0c;面试官问TCP三次握手&#xff0c;我嘴巴比脑子快&#xff0c;SYN、SYNACK、ACK三件套背得行云流水。对方听完点了点头&#xff0c;接着问&#xff1a;“第二次握手丢了&#xff0c;客户端和服务端分别会怎么反应&#xff1f;”我…

2026/10/20 10:49:41 阅读更多 →
百万QPS秒杀系统设计:令牌桶、Redis分片、分布式锁与配置中心

百万QPS秒杀系统设计:令牌桶、Redis分片、分布式锁与配置中心

废话不多说&#xff0c;直接进入正题。先说下背景。我接手这套秒杀系统时&#xff0c;运营给的需求一句话就讲完了&#xff1a;“20 万商品全部参加活动&#xff0c;100 万人预约&#xff0c;开抢那一下全给我接住。”但这句话背后是整个后端团队三个月的改造。标题里提到的令牌…

2026/10/20 10:49:41 阅读更多 →

最新新闻

TensorFlow原生ResNet-18/50/101/152开箱即用实现

TensorFlow原生ResNet-18/50/101/152开箱即用实现

简介&#xff1a;本资源是一套基于TensorFlow实现的ResNet系列模型&#xff08;18/50/101/152层&#xff09;精简可复用代码&#xff0c;面向深度学习初学者与算法工程师&#xff0c;解决模型结构复现难、预训练权重适配不畅等常见开发痛点&#xff0c;适用于图像分类、迁移学习…

2026/10/20 11:34:31 阅读更多 →
ThinkSystem RAID 530-8i 驱动安装指南:Windows Server 2016 兼容性实战

ThinkSystem RAID 530-8i 驱动安装指南:Windows Server 2016 兼容性实战

简介&#xff1a;本资源是专为联想ThinkSystem服务器运维人员及Windows Server系统管理员提供的RAID 530-8i阵列卡驱动合集&#xff0c;解决在Windows Server 2012 R2与2016&#xff08;64位&#xff09;环境下识别、安装及配置该硬件的关键兼容性问题。压缩包共21个文件&#…

2026/10/20 11:34:31 阅读更多 →
DeepSeek V4.1 Flash登顶OpenRouter:实用主义模型的胜利

DeepSeek V4.1 Flash登顶OpenRouter:实用主义模型的胜利

1. 先说结论&#xff1a;这个榜单到底意味着什么打开 OpenRouter 的模型排行榜页面&#xff0c;你会看到模型列表按“请求量”排顺序。DeepSeek V4.1 Flash 能窜到前面&#xff0c;光靠技术发布会上的参数吹嘘是不够的&#xff0c;真正的关键是它把“便宜、快、够用”这三个词做…

2026/10/20 11:34:31 阅读更多 →
某红书x-s加密算法补环境全解析:从环境检测到稳定生成

某红书x-s加密算法补环境全解析:从环境检测到稳定生成

简介&#xff1a;针对某红书x-s签名参数生成的补环境版本算法包&#xff0c;主要面向爬虫逆向、JavaScript加密分析与Python服务端调试人员&#xff1b;资源采用Python execjs调用JavaScript实现&#xff0c;并附完整可用的接口调用Demo&#xff0c;便于直接复现x-s参数生成逻辑…

2026/10/20 11:34:31 阅读更多 →
NTP部署后的时间同步监控:健康度指标与告警实践

NTP部署后的时间同步监控:健康度指标与告警实践

NTP部署完之后&#xff0c;很多团队的第一反应是“终于搞定时间同步了&#xff0c;后面不用管了”。但真正的运维工作恰恰是从这里开始的。我自己就经历过一个挺典型的场景&#xff1a;某个业务模块突然出现大量订单时间错乱&#xff0c;排查了半天才发现是NTP服务早在一个月前…

2026/10/20 11:34:31 阅读更多 →
VS2019快速集成wxWidgets二进制包实战指南

VS2019快速集成wxWidgets二进制包实战指南

1. 项目概述&#xff1a;为什么“直接使用二进制文件”是wxWidgets开发中最常被卡住的第一关 在Visual Studio 2019环境下配置wxWidgets&#xff0c;绝大多数人踩进的第一个坑&#xff0c;不是代码写错&#xff0c;也不是逻辑出问题&#xff0c;而是——连编译都过不去。你下载…

2026/10/20 11:33:30 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →