linux 下 mmap、sendfile

对应前文:linux 下 BIO、NIO、

mmap 内存映射

上一篇提到同步非阻塞NIO时期。
当2个fd时,用户线程对这2个fd轮巡调用获取对应的数据进行处理就行了。
但是当fd非常多的时候比如有1000个fd,就需要轮巡调用1000次,但是这1000个中如果只有10个的数据发生了变化。还是会造成成本的浪费。

因为遍历fd的过程中,用户态发起遍历,拿到对应的fd。传递给内核态,如果有数据内核态则将数据返回给用户态,用户态再处理。在这个过程中虽然已经比BIO时期好很多了,但是随着fd连接增大的时候用户态,内核态之间来回切换还是会造成资源的浪费。

如果能减少系统调用,对应1000个fd只在fd发生改变的时候才发起对应的系统调用就能减少成本。但是这种方式用户态自身无法实现,内核又向前发展开放了新的功能
在这里插入图片描述
select()方法。【select、poll、epoll 】

SELECT(2)                  Linux Programmer’s Manual                 SELECT(2)

NAME
       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing

SYNOPSIS
       /* According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       void FD_CLR(int fd, fd_set *set);
       int  FD_ISSET(int fd, fd_set *set);
       void FD_SET(int fd, fd_set *set);
       void FD_ZERO(fd_set *set);

       #include <sys/select.h>

       int pselect(int nfds, fd_set *readfds, fd_set *writefds,
                   fd_set *exceptfds, const struct timespec *timeout,
                   const sigset_t *sigmask);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600

DESCRIPTION
       select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation
       (e.g., input possible).  A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

用户将需要遍历的1000个fd作为参数调用select方法,内核监控这些fd,当有一个或多个fd发生改变时,将发生改变的fd返回给用户态。用户态在对这些数据进行read。
这种方式对比NIO时。1000次调用变成了1次调用,当有fd改变时再进行下面的操作。减少了用户态和内核态之间的切换,这就是多路复用

但是select方法需要解析传递的fd需要内核进行解析,遍历,再将发生改变的fd返回给用户态。这个过程中有参数间的传递,参数过大也会成为累赘。而内核又暴露了一个方法出来 mmap

最开始的时候,内核有内核的内存地址空间,用户有用户的内存地址空间。而内核的地址空间用户是禁止访问的,所以将大量的fd传递来传递去。为了减少传递和参数间的拷贝,提供了方法开辟出内核和用户的共享空间
在这里插入图片描述
在这里插入图片描述
有了mmap之后,用户态发起select调用不需要传递fds,当产生新的fd时,只需要将fd放入红黑树中,内核直接遍历红黑树中的fd发现有人数据到达了,就fd放入链表中。用户态直接用链表中的fd进行读取。

sendfile 零拷贝

在这里插入图片描述
如果请求需要读取一个文件File1.txt。

没有零拷贝的时候:
1.线程发起read命令,内核将File1.text加载到缓冲区返回给线程。
2.线程发起write命令,内核将File1.text加载到缓冲区,在通过网卡发送出去

有零拷贝的时候:
1.线程发起sendfile命令,内核将File1.text加载到缓冲区直接通过网卡发出去。省去了线程和内核间拷贝的过程,所以称为零拷贝。

SENDFILE(2)                Linux Programmer’s Manual               SENDFILE(2)

NAME
       sendfile - transfer data between file descriptors

SYNOPSIS
       #include <sys/sendfile.h>

       ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

DESCRIPTION
       sendfile() copies data between one file descriptor and another.  Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2)
       and write(2), which would require transferring data to and from user space.

       in_fd should be a file descriptor opened for reading and out_fd should be a descriptor opened for writing.

       If offset is not NULL, then it points to a variable holding the file offset from which sendfile() will start reading data from in_fd.  When sendfile() returns, this variable
       will be set to the offset of the byte following the last byte that was read.  If offset is not NULL, then sendfile() does not modify the current file offset of in_fd; other-
       wise the current file offset is adjusted to reflect the number of bytes read from in_fd.

应用

kafka

kafka 就是 mmap + sendfile 组合在一起的高效应用

在这里插入图片描述
生产者写数据的时候,因为mmap的存在,可以减少kafka和内核之间的数据拷贝。直接将数据写入到文件中。
而在消费者消费数据时,因为sendfile,也减少了kafka和内核间的数据拷贝,直接将文件内容返回给消费者。

redis

epoll + mmap
在这里插入图片描述
数据到达内核之后因为采用了epoll 多路复用和mmap所以能把数据直接放入链表中,redis进行处理

nginx

epoll 多路复用
在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值