进程创建:fork()
进程间通信(Inter Process Communication)
每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。
匿名管道、有名管道
匿名管道:在内核中开辟一块缓冲区用于通信,仅限于父子进程之间的通信。
子进程必须要使用父进程创建的匿名管道的读写句柄,通过这个匿名管道才能实现父子进程的通信
有名管道(FIFO):提供了一个路径名与之关联,以FIFO的文件形式存在于文件系统中,不相关的进程可以通过打开有名管道进行通信。
缺点:无格式的字节流并且大小受限,单向通信
消息队列
保存在内核中的消息链表,在发送数据时,会分成一个个独立的数据单元,也就是消息体(数据块),消息体是用户自定义的数据类型。消息的发送方和接收方要约定好消息体的数据类型,所以每个消息体都是固定大小的存储块。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。
缺点:1.消息队列不适合比较大数据的传输;2.消息队列通信过程中,存在用户态与内核态之间的数据拷贝开销,因为进程写入数据到内核中的消息队列时,会发生从用户态拷贝数据到内核态的过程,同理另一进程读取内核中的消息数据时,会发生从内核态拷贝数据到用户态的过程。
信号
用于异常情况下的工作模式,向进程发送信号,进程捕捉信号并做出回应
共享内存
允许多个进程共享一个给定的存储区,这一段存储区可以被进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程读取,从而实现了进程间的通信。
两种实现方式:内存映射、共享内存
缺点:多进程竞争同个共享资源会造成数据的错乱(需要引入信号量来保护共享资源,确保任何时刻只能有一个进程访问共享资源);
Socket
用于跨网络与不同主机上的进程之间的通信
线程池
线程过多会带来调度开销,进而影响缓存局部性和整体性能。频繁创建线程也会造成开销
线程池维护多个线程,等待并发执行的任务,避免了创建与销毁线程所需的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。
主要参数
核心线程数(corePoolSize):用于处理任务的线程数,通常取决于处理器内核数,空闲时不会被回收
任务队列长度(workQueue):可以处理的最大任务数
最大线程数(maximumPoolSize):可以容纳的最大线程数
最大空闲时间(keepAliveTime):空闲时,非核心线程的存活时间
执行流程
任务进来时,首先判断核心线程是否处于空闲状态,如果有空闲的核心进程,则执行该任务;如果核心线程已满,就将任务保存在任务队列中,等待执行;如果队列满了,则判断线程数是否超过最大线程数,如果没有超出,就创建非核心线程执行任务,如果超出了,就调用handler实现拒绝策略。
socket编程
服务器调用accept()从握手队列中接收连接,返回一个新的socket描述符,用于和该连接的客户端通信。
socket连接包含发送队列、接收队列、等待队列等数据结构。发送队列:写缓冲区;接收队列:读缓存区;等待队列:数据就绪时的回调函数
IO多路复用
水平触发:只要文件描述符关联的读内核缓冲区非空,有数据可以读取,就一直发出可读信号进行通知;当文件描述符关联的内核写缓冲区不满,有空间可以写入,就一直发出可写信号进行通知
边缘触发:当文件描述符关联的读内核缓冲区由空转化为非空的时候,则发出可读信号进行通知,当文件描述符关联的内核写缓冲区由满转化为不满的时候,则发出可写信号进行通知
Select
将socket存放在文件列表中,监听文件列表,如果有数据写入,则将文件列表拷贝至内核,遍历文件列表,读取写入的数据
缺点:1.文件列表大小有限制;2.需要将文件列表从用户态拷贝到内核态,开销大;3.需要遍历文件列表才能找到写入的socket
poll
使用pollfd结构体来存放socket描述符,结构体中的fd为socket描述符,events为感兴趣的事件,revents为当前发生的事件类型。
如果有对应的事件发生,则修改结构体中的revents
使用pollfd结构体来存放socket描述符,用数组存放pollfd,大小没有限制
缺点:1.需要pollfd数组从用户态拷贝到内核态,开销大;2.需要遍历pollfd数组才能找到写入的socket
epoll
使用红黑树管理所有添加的socket连接,创建eventpoll维护一个就绪队列,用于保存收到数据的socket连接
调用epoll_ctl时拷贝进内核并保存,之后每次epoll_wait不拷贝
主要命令:
epoll_create:创建一个eventpoll对象
epoll_ctl:向eventpoll对象中添加要管理的连接(由accept创建),具体为1.分配并初始化epitem;2.设置socket上的等待队列;3.将epitem插入到红黑树中
epoll_wait:首先查看epoll就绪队列上有无可处理的事件,若有,则处理,否则将进程添加到epoll对象的等待队列上,等待其管理的连接上的IO事件
数据接收:
首先根据收到的网络包header里的source和dest信息,在本机上查询对应的socket,将接收数据放到该socket的接收队列上。若触发了事件,则调用等待队列上的回调函数,找到对应的epitem,添加到eventpoll对象的就绪队列中,并唤醒等待队列上的进程。