首页 > 代码库 > nginx源码分析--进程间通信机制 & 同步机制

nginx源码分析--进程间通信机制 & 同步机制

         Nginx源码分析—进程间通信机制

从nginx的进程模型可以知道,master进程和worker进程需要通信,nginx中通信的方式有套接字、共享内存、信号。对于master进程,从外部接受信号,master进程主要就是监控、接受外部信号,将有必要的信号传递给worker进程,master进程大部分时间都是阻塞在sigsuspend()函数调用上。Worker进程屏蔽了所有的外部信号,那么Master进程就通过套接字和worker进程通信,worker进程修改全局变量,使得worker进程接受到了master进程的命令(进程间的套接字被epoll监控)。

共享内存是存放父子进程中都需要的全局变量,比如当前系统接受到的连接,正在处理的连接数目等。

既然使用了共享内存,那么就需要考虑同步,nginx中的同步使用了原子操作、文件锁、信号量(这里信号量的值为1,也就是相当于一个互斥锁)。虽说实现了这三种,但是对外都是使用了ngx_shmtx_t变量,这个变量成为一个互斥锁,使用上述三种同步方式实现了一种对外的互斥锁,接下来看看实现方式:

首先声明一点,在nginx中都是非阻塞的,所以在获取锁的时候也希望是非阻塞的

对于共享内存,使用shmgt &mmap(可以实现文件到内存的映射,也可以不使用文件的方式来实现映射)来实现,共享内存中存放的变量有已经建立成功的TCP连接数、已经处理过的TCP连接数等共6类,共享内存的初始化在Main中实现,共享内存中数据的初始化在ngx_event_module_init函数中实现

上述几种锁的属性设计到阻塞&非阻塞 导致睡眠&不导致睡眠,至于阻塞和非阻塞就是看能否马上得到调用的结果,根据前提条件选择不同的锁,比如这里用原子操作实现的自旋锁就是阻塞锁。就是如果没有获取锁,那么就连续不断的尝试一段时间,如果是想占有一个被短时间占用的资源的话,可以使用这种阻塞锁,,如果占用的时间过长,不适合使用阻塞锁,会占用过多的CPU资源。睡眠锁就是在获取锁不成功会导致进程睡眠,缺点是一旦睡眠需要另一个进程来唤醒,逻辑复杂同时CPU的切换消耗时间。

用户可以从锁的使用时间长短角度来选择使用哪一种锁,当锁的使用时间很短,可使用自旋锁,如果锁使用时间长,使用睡眠锁。阻塞锁就是在一段时间上不停的获取锁直到锁获取成功。

信号量和文件锁都是睡眠锁,信号量使用的sem_wait,文件锁使用fcntl(),不过在死啊用的时候要么成功,要么睡眠。

信号量sem_init可以用于线程间也可以用于进程间,实现互斥锁的方法:信号量为0,嗲用sem_post加1,不会有阻塞,调用sem_wait将信号量间1,如果信号量不大于0,就阻塞当前进程(进程进入睡眠),直到其他进程将信号量的值为正数后,这时才能继续通过信号量减1而使得当前进程继续进行。Sem_post解锁,sem_wait实现加锁。

对于稳健锁,ngx_trylock_fd实现不会阻塞,不会使得进程睡眠的互斥锁,ngx_lock_fd提供的互斥锁在锁已经被的进程拿到时将会导致当前进程进入睡眠状态。

 

基于原子操作,信号量文件锁,Ngxin封装了一个互斥锁。

原子变量锁优先级高于文件锁。

如果不支持原子操作,会使用文件锁实现ngx_shmtx_t互斥锁,使用文件锁来提供阻塞、非阻塞的互斥锁。

支持原子操作却不支持信号量

支持原子操作的同时,操作系统也支持信号量

后两种的区别在于,支持信号量只会影响进程ngx_shmtx_lock方法持有锁的方式,不支持信号量时,和自旋锁一样,支持信号量,自旋锁在阻塞一段时间后没有获取锁,那么信号量导致进程进入睡眠状态,只有等到别的进程释放这个锁才会被激活

在不使用信号量时,nginx互斥锁和自旋锁相似,而在使用信号量后将会使用可能让进程进入睡眠,不建议使用带信号量的ngx_shmtx_lock取锁方法。


其实做了这么多的封装也是为了系统可移植

nginx源码分析--进程间通信机制 & 同步机制