1.服务端常见的四种IO模型?
- 同步阻塞IO(Blocking IO):即传统的IO模型。
- 同步非阻塞IO(Non-blocking IO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的NIO并非Java的NIO(New IO)库。
- IO多路复用(IO Multiplexing):即经典的Reactor设计模式,有时也称为异步阻塞IO,Java中的Selector和Linux中的epoll都是这种模型。
- 异步IO(Asynchronous IO):即经典的Proactor设计模式,也称为异步非阻塞IO。
同步是指用户线程发起IO请求后需要等待;阻塞是指IO操作需要彻底完成后才返回到用户空间。
2.IO 多路复用是什么?
- I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
3.IO多路复用的解决方案select,poll,epoll?
- select通过遍历文件描述符来获取已经就绪的socket,最大的缺陷就是单个进程所打开的FD是有一定限制的,默认值是1024。对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低。需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
- poll也是通过遍历文件描述符来获取已经就绪的socket,它将用户传入的数组拷贝到内核空间。没有最大连接数的限制,原因是它是基于链表来存储的。
- epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
4.进程 / 线程 / 协程区别?
- 进程是系统进行资源分配和调度的基本单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。
- 线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。
- 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
5.go 和 swoole 的协程实现有啥区别?
-
Swoole4与Go协程在设计上是完全一致的,均是stackful的,每个协程拥有独立的运行栈。协程调度器使用汇编代码,切换协程上下文。
-
woole4的协程调度器是单线程的,因此不存在数据同步问题,同一时间只会有一个协程在运行
Go协程调度器是多线程的,同一时间可能会有多个协程同时执行。 -
Swoole4提供的defer关键词与Go的defer存在差异。
-
Swoole不允许多个协程同时读取同一个Socket。Go的defer是绑定函数的,在当前函数退出时会执行defer任务。Swoole4的defer设计为在协程退出时一起执行。