核心思想:一个服务器和多个客户端进行通信,每来一个客户端进行连接就创建一个进程,将接收到的文件描述符给对应的子进程,形成一对一的访问机制。当一个客户端关闭的时候,就退出子进程。 在这个过程中父进程扮演的角色是?
等待接受客户端的连接有连接就创建一个子进程将通信的文件描述符关闭子进程扮演的角色?
进行与客户端的通信关闭监听的文件描述符(减少资源的开销)缺点: a:启动和关闭进程带来开销 b:系统最多只能创建512个进程,文件描述符默认的限制也是1024,无法形成大规模的访问
核心思想: 每有一个客户端连接,就创建一个子线程,通过回调函数函数进行业务的处理,并将文件描述符传给创建的线程。因为每一个线程都需要一份独立的数据 缺点:多线程存在线程创建的开销,而且如果大量的用户访问还存在线程切换的问题
核心思想: 不再由应用程序自己监听客户端的连接,取而代之由内核替应用程序监视文件 主要有select()/poll()/epoll()这三种方法 3.1 select() 核心思想: 创建文件描述符集合,调用select()函数完成内核检测有多少个文件描述符发生变化,函数的返回值是有多少个文件描述符发生变化 优点:跨平台 缺点:
每次调用select()函数都需要把fd集合把用户态拷贝到内核态这个开销会很大需要线性遍历文件描述符数组,随着fd的增加效率会显著的下降3.2poll() poll的方法和select()一样只是将select中的读集合、写集合、出错集合、封装成结构体中的事件 3.3epoll()(最高效) 核心思想:不仅能够返回有多少个文件描述符发生变化,而且还能返回这些变化的文件描述符时哪些。 epolll效率高效的原因在于数据结构的设计
typedef union epoll_data { void *ptr; int fd; //一般情况下,都用的是这个文件描述符 uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ };epoll_data保留了fd利用它直接可以定位客户端的文件描述符 epoll_wait()的核心理解 1 返回就绪事件的个数 2 将就绪客户端的信息保存到event数组中,将所以的就绪事件从内核事件表中复制到events指向的数组中
1水平触发方式 只用fd对应的缓冲区中有数据epoll_wait()就返回 返回的次数与发送的次数无关 epoll默认的工作方式 2边缘触发方式 不管数据有没有读完,只有客户端发送数据的时候,才触发,通过把缓冲区的数据挤出来的方式,发出数据的 3边缘非阻塞方式 这是最好的方法 核心思想: 把缓冲区中的数据全部读完,通过改变文件描述符的默认属性,将其设置为非阻塞的方式来进行实现的