
零拷贝
传统的IO过程首先CPU发出指令到磁盘控制器,然后磁盘控制器将数据拷贝到内部缓冲区,发送一个中断,CPU会停下其他工作,将内部缓冲区的数据写入到内存,CPU无法进行其他事情。
DMA直接访问内存技术就是在IO设备和内存传输数据时,用DMA控制器代替CPU的数据搬运工作。
传统的文件传输中,数据读取与写入是从用户空间到内核空间来回复制,内核空间的数据是通过系统IO从磁盘读取和写入的,需要两个系统调用。需要四次用户态与内核态的上下文切换(read和write各一次系统调用,一次系统调用需要两次上下文切换)与四次数据拷贝(磁盘文件到缓冲区,缓冲区到用户缓冲区,用户缓冲区到socket缓冲区,socket缓冲区到网卡)
为了减少上下文切换次数,就是要减少系统调用的次数,于是可以使用mmap函数代替read函数,mmap中内核缓冲区与用户缓冲区是共享的,不需要数据拷贝。而sendfile则可以替代write和read两个系统调用,减少两次上下文切换。
真正的零拷贝技术需要网卡支持SG-DMA,可以直接将缓冲区的描述符和数据长度传到socket缓冲区里,而内核中缓存的数据直接拷贝到网卡中去,最后只需要两次拷贝。
零拷贝的项目最贴近的就是kafka,nginx。
在高并发的场景下,针对大文件的传输方式,应该采用直接IO和异步IO的组合方式代替零拷贝技术。这种使用直接IO应用场景有:一是用户态已经缓存好磁盘,不需要page cache去缓存。二是传输大文件时难以命中page cache,容易导致缓存污染。于是传输小文件才会使用零拷贝。
多路复用
基本的TCP socket调用流程只能一对一通信,用的是同步阻塞方法,这样服务器只能服务一个用户,效率太低下了。
最初的改进方法是多进程,每个用户分配一个线程,主线程负责监听连接,一旦有新的客户端连接完成,会用fork建立一个新的子进程,需要全部复制父进程的文件描述符,内存空间等。而如果子进程退出后,内核还会保留进程的一些信息,如果不回收就会变成僵尸进程。多进程消耗的资源过多,还得进行大量进程的上下文切换。
多线程的方法降低了上下文切换的负担,服务器与客户端TCP完成连接后,通过pthread_create创建线程,然后运行后销毁。但这种方法效率太低,于是提出了使用线程池去提前创建好若干的线程,等到新连接到来后,将socket放入队列,线程池里的线程对取出的socket处理。。由于队列是全局的,还需要加锁避免竞争。
传统的IO过程首先CPU发出指令到磁盘控制器,然后磁盘控制器将数据拷贝到内部缓冲区,发送一个中断,CPU会停下其他工作,将内部缓冲区的数据写入到内存,CPU无法进行其他事情。
DMA直接访问内存技术就是在IO设备和内存传输数据时,用DMA控制器代替CPU的数据搬运工作。
传统的文件传输中,数据读取与写入是从用户空间到内核空间来回复制,内核空间的数据是通过系统IO从磁盘读取和写入的,需要两个系统调用。需要四次用户态与内核态的上下文切换(read和write各一次系统调用,一次系统调用需要两次上下文切换)与四次数据拷贝(磁盘文件到缓冲区,缓冲区到用户缓冲区,用户缓冲区到socket缓冲区,socket缓冲区到网卡)
为了减少上下文切换次数,就是要减少系统调用的次数,于是可以使用mmap函数代替read函数,mmap中内核缓冲区与用户缓冲区是共享的,不需要数据拷贝。而sendfile则可以替代write和read两个系统调用,减少两次上下文切换。
真正的零拷贝技术需要网卡支持SG-DMA,可以直接将缓冲区的描述符和数据长度传到socket缓冲区里,而内核中缓存的数据直接拷贝到网卡中去,最后只需要两次拷贝。
零拷贝的项目最贴近的就是kafka,nginx。
在高并发的场景下,针对大文件的传输方式,应该采用直接IO和异步IO的组合方式代替零拷贝技术。这种使用直接IO应用场景有:一是用户态已经缓存好磁盘,不需要page cache去缓存。二是传输大文件时难以命中page cache,容易导致缓存污染。于是传输小文件才会使用零拷贝。
多路复用
基本的TCP socket调用流程只能一对一通信,用的是同步阻塞方法,这样服务器只能服务一个用户,效率太低下了。
最初的改进方法是多进程,每个用户分配一个线程,主线程负责监听连接,一旦有新的客户端连接完成,会用fork建立一个新的子进程,需要全部复制父进程的文件描述符,内存空间等。而如果子进程退出后,内核还会保留进程的一些信息,如果不回收就会变成僵尸进程。多进程消耗的资源过多,还得进行大量进程的上下文切换。
多线程的方法降低了上下文切换的负担,服务器与客户端TCP完成连接后,通过pthread_create创建线程,然后运行后销毁。但这种方法效率太低,于是提出了使用线程池去提前创建好若干的线程,等到新连接到来后,将socket放入队列,线程池里的线程对取出的socket处理。。由于队列是全局的,还需要加锁避免竞争。


