ssize_t sendfile(
int out_fd,
int in_fd,
off_t *offset,
size_t count
);
函数名字已经把函数的功能解释的很明显了:发送文件。指定要发送的文件描述符和网络套接字描述符,一个函数搞定!
I/O优化:多路复用技术
每个线程都要阻塞在recv等待对方的请求,这来访问的人多了,线程开的就多了,大量线程都在阻塞,系统运转速度也随之下降。
这个时候,你需要多路复用技术,使用select模型,将所有等待(accept、recv)都放在主线程里,工作线程不需要再等待。
过了一段时间之后,网站访问的人越来越多了,就连select也开始有点应接不暇,老板继续让你优化性能。
这个时候,你需要升级多路复用模型为epoll。
select有三弊,epoll有三优。
select底层采用数组来管理套接字描述符,同时管理的数量有上限,一般不超过几千个,epoll使用树和链表来管理,同时管理数量可以很大。
select不会告诉你到底哪个套接字来了消息,你需要一个个去询问。epoll直接告诉你谁来了消息,不用轮询。
select进行系统调用时还需要把套接字列表在用户空间和内核空间来回拷贝,循环中调用select时简直浪费。epoll统一在内核管理套接字描述符,无需来回拷贝。
用上了epoll多路复用技术,开发了3.0版本,你的网站能同时处理很多用户请求了。
之前的方案中,工作线程总是用到才创建,用完就关闭,大量请求来的时候,线程不断创建、关闭、创建、关闭,开销挺大的。这个时候,你需要:
线程池技术
我们可以在程序一开始启动后就批量启动一波工作线程,而不是在有请求来的时候才去创建,使用一个公共的任务队列,请求来临时,向队列中投递任务,各个工作线程统一从队列中不断取出任务来处理,这就是线程池技术。
多线程技术的使用一定程度提升了服务器的并发能力,但同时,多个线程之间为了数据同步,常常需要使用互斥体、信号、条件变量等手段来同步多个线程。这些重量级的同步手段往往会导致线程在用户态/内核态多次切换,系统调用,线程切换都是不小的开销。
在线程池技术中,提到了一个公共的任务队列,各个工作线程需要从中提取任务进行处理,这里就涉及到多个工作线程对这个公共队列的同步操作。