零拷贝技术mmap()+write()

socket缓冲区
每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。
write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。
在这里插入图片描述

再介绍一下Socket IO读写的过程:
IO读写一般通过read()和write()系统调用来实现,执行一次read()调用和一次write()调用会发生四次用户态和内核态的转换和四次拷贝。
1、用户进程通过read()方法向操作系统发起调用,指示上下文由用户态转为内核态。
2、DMA控制器将数据从硬盘中被拷贝到内核空间的读缓存区。
3、CPU将数据由内核空间的读缓存区拷贝到用户缓冲区。上下文由内核态转为用户态,read()返回。
4、用户进程通过write()方法向操作系统发起调用,指示上下文由用户态转为内核态。
5、CPU将数据由用户缓冲区拷贝到内核空间的socket缓存区。
6、DMA控制器将数据从socket缓存区拷贝到网卡。上下文由内核态转为用户态。write()返回。

在这里插入图片描述
零拷贝技术(指计算机执行操作的过程中,CPU不需要将数据从某处内存复制到另一处内存,并不是指cpu完全不进行数据拷贝,而是尽量减少内核态与用户态的切换次数和CPU的拷贝次数)

mmap+write():即用mmap取代了read()操作。将用户缓冲区和内核缓冲区进行了映射,这样减少了数据从用户缓冲区到内核缓冲区的一次拷贝。因此mmap+write()需要四次用户态和内核态的切换和三次拷贝。
具体过程:
应用进程调用了 mmap() 后,DMA 会把磁盘的数据拷贝到内核的缓冲区里。接着,应用进程跟操作系统内核「共享」这个缓冲区;
应用进程再调用 write(),操作系统直接将内核缓冲区的数据拷贝到 socket 缓冲区中,这一切都发生在内核态,由 CPU 来搬运数据;
最后,把内核的 socket 缓冲区里的数据,拷贝到网卡的缓冲区里,这个过程是由 DMA 搬运的。

在这里插入图片描述

web服务器在处理IO请求时,一般是将socket交给子线程来处理,然后每个子线程处理一个,但这个问题就是 当IO阻塞,线程就会阻塞,此时会发生线程切换,但当处理高并发的情况下发生线程频繁切换,这会导致很大的时间浪费。因此希望能不能把线程切换的时间耗费节约下来。
所以就给每个子线程多个协程,一个子线程处理多个,但每个协程各自处理一个,每个时刻下还是一个子线程处理一个,但在某个协程IO阻塞时,就可以切换到另一个协程,就避免了线程切换的耗时,使得支持的并发量上升了。也可以说协程可以避免无意义的调度,并由此提高性能。

同时在处理IO的过程中,处理读或者写都需要访问内核中的读缓冲区或写缓冲区,但线程需要读或写的时候缓冲区中并不一定有数据或是可供写入数据的空间,这样就需要线程等待,如果采用阻塞式IO,那么当读缓冲区或写缓冲区不可用时,线程陷入阻塞,引发线程调度,这在高并发场景下会加剧调度开销,而如果使用非阻塞IO,那就是需要频繁查询缓冲区是否可用,这虽然不会引发线程调度,但会造成CPU空耗,造成CPU计算资源的浪费。若采用IO多路复用,就可以同时监听多个socket,这样既避免了高并发下多线程可能面临的调度开销,又避免了轮询造成的CPU空耗。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值