├── README.md ├── 07.MsgQueue.md ├── 01.ShareMemory和MemoryPool.md ├── 03.三种MemoryPool(下).md ├── 04.锁和信号(上).md ├── 02.三种MemoryPool(上).md ├── 06.Pipe管道.md ├── 05.锁和信号(下).md ├── 14.Server模块详解(下).md ├── 08.Reactor模块-epoll.md ├── 09.Factory模块(上).md ├── 11.Worker,Connection.md ├── 13.Server模块详解(上).md ├── 12.ReactorThread模块.md ├── 15.Timer模块分析.md └── 10.Factory模块(下).md /README.md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | 4 | ###作者 5 | Lancelot(李丹阳) from **LinkedDestiny**(**牵机工作室**) 6 | 7 | ###描述 8 | 这些记录是我在学习分析swoole源码时写下的,由于个人能力所限,其中的分析难免有不全或错误的地方。如果你发现了这些错误和不全,请联系我并可以做出改正。 9 | 10 | ###邮箱 11 | simonarthur2012@gmail.com 12 | 13 | ###目录 14 | 1. [ShareMemory和MemoryPool](01.ShareMemory和MemoryPool.md) 15 | 2. [三种MemoryPool(上)](02.三种MemoryPool(上).md) 16 | 3. [三种MemoryPool(下)](03.三种MemoryPool(下).md) 17 | 4. [锁和信号(上)](04.锁和信号(上).md) 18 | 5. [锁和信号(下)](05.锁和信号(下).md) 19 | 6. [Pipe管道](06.Pipe管道.md) 20 | 7. [MsgQueue](07.MsgQueue.md) 21 | 8. [Reactor模块-epoll](08.Reactor模块-epoll.md) 22 | 9. [Factory模块(上)](09.Factory模块(上).md) 23 | 10. [Factory模块(下)](10.Factory模块(下).md) 24 | 11. [Worker,Connection](11.Worker,Connection.md) 25 | 12. [ReactorThread模块](12.ReactorThread模块.md) 26 | 13. [Server模块详解(上)](13.Server模块详解(上).md) 27 | 14. [Server模块详解(下)](14.Server模块详解(下).md) 28 | 29 | ###更新计划 30 | - 第十五章 Timer模块分析 31 | - 第十六章 http_server模块分析 32 | - 第十七章 swoole.c详解(上) 33 | - 第十八章 swoole.c详解(下) 34 | - 第十九章 swoole_process详解 35 | - 第二十章 Table.c模块分析 36 | - 第二十一章 总结与回顾 37 | 38 | 章节内容和章节数可能会根据实际情况调整和变更。 39 | 40 | ###说明 41 | 写下这些记录,最初的目的是希望自己能深入的理解Swoole的底层机制,从源头掌握Swoole,同时也希望自己能从学会Swoole中那些精妙的设计。另一方面,我也希望我的记录能帮助到那些同样希望理解Swoole核心的人,也能让那些怀疑、抨击Swoole的人看到Swoole强大的核心。 42 | -------------------------------------------------------------------------------- /07.MsgQueue.md: -------------------------------------------------------------------------------- 1 | Swoole版本:1.7.5-beta 2 | 3 | 版本更换通知:从第七章开始,我把分析的源码版本升到了1.7.5-beta。在已经分析过的代码中存在了一些变动,但没有到会影响理解的地步,所以再次不再更新前面的章节。如果确认前面的章节有重大变更需要重新分析的,我会给出声明。 4 | 5 | Swoole内部封装了Linux提供的msg queue用于提供消息队列的操作。消息队列的声明在swoole.h文件的360 – 378 行。声明如下: 6 | 7 | typedef struct _swQueue_Data 8 | { 9 | long mtype; /* type of received/sent message */ 10 | char mdata[sizeof(swEventData)]; /* text of the message */ 11 | } swQueue_data; 12 | 13 | typedef struct _swQueue 14 | { 15 | void *object; 16 | int blocking; 17 | int (*in)(struct _swQueue *, swQueue_data *in, int data_length); 18 | int (*out)(struct _swQueue *, swQueue_data *out, int buffer_length); 19 | void (*free)(struct _swQueue *); 20 | int (*notify)(struct _swQueue *); 21 | int (*wait)(struct _swQueue *); 22 | } swQueue; 23 | 24 | int swQueueMsg_create(swQueue *p, int wait, int msg_key, long type); 25 | 第一个结构体swQueue_Data封装了消息队列中的消息,其中 mtype 用于标记该消息是需要发送的消息还是从队列中读出的消息,mdata用于存放消息内容(swEventData结构体将在下一章解析)。 最后一行的函数用于创建一个消息队列,第二个参数wait指定阻塞类型,msg_key指定创建的消息队列的键值,type标记消息队列读数据的类型 type=0,接受该队列的第一个消息,并将它返回给调用者 type>0,接受类型type的第一个消息 type<0,接受小于等于type绝对值的最低类型的第一个消息 swQueueMsg_create函数的具体声明在Msg.c文件中,其核心代码如下: 26 | 27 | swQueueMsg *object = sw_malloc(sizeof(swQueueMsg)); 28 | if (object == NULL) 29 | { 30 | return -1; 31 | } 32 | if (blocking == 0) 33 | { 34 | object->ipc_wait = IPC_NOWAIT; 35 | } 36 | else 37 | { 38 | object->ipc_wait = 0; 39 | } 40 | p->blocking = blocking; 41 | msg_id = msgget(msg_key, IPC_CREAT | O_EXCL | 0666); 42 | 源码解释:申请内存,分配消息队列的结构体内存,并判定消息队列的阻塞类型。接着调用msgget函数创建消息队列。 这里说明msgget第三个参数中的几个值的意义。IPC_CREAT表明,若不存在以key为索引的消息队列,则创建新的队列;否则,返回已创建的消息队列的id。O_EXCL表示若打开一个已存在的消息队列,则返回-1。0666为该消息队列的读写权限。 swQueueMsg有对应的三个操作函数,分别是: 43 | 44 | int swQueueMsg_in(swQueue *p, swQueue_data *in, int data_length); 45 | int swQueueMsg_out(swQueue *p, swQueue_data *out, int buffer_length); 46 | void swQueueMsg_free(swQueue *p); 47 | 这三个函数用于向队列中写入消息、从队列中读出消息、释放队列。 1. swQueueMsg_in函数循环调用msgsnd直到消息发送成功或无法再次发送。 2. swQueueMsg_out函数调用msgrcv函数接收指定类型的消息 3. swQueueMsg_free函数调用msgctl释放一个消息队列,IPC_RMID参数代表立即释放全部资源。 -------------------------------------------------------------------------------- /01.ShareMemory和MemoryPool.md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | 4 | 5 | 我接触PHP的时间不长,最开始只认为PHP是用来做网站开发,是一个比JSP要简单的语言。后来,因为工作需要,一位学长建议我使用Ngnix + PHP 搭建应用服务器,并建议使用现有的框架。一番搜索之下,我意外发现了[Swoole](http://www.swoole.com) 6 | 7 | 接下来的半年里,我一直使用Swoole扩展作为我的服务器核心。Swoole稳定而高效的性能以及优秀的架构设计使我的开发变得简单、高效。因此,我希望能够更加深入的了解Swoole的核心,学习它的架构设计,也能锻炼自己的C语言能力。 8 | 9 | 因此,我将不定期更细这一系列,将我在阅读、理解Swoole源码过程中的心得体会记录下来,也希望我的记录能帮助那些同样希望理解Swoole源码的人。本人能力有限,C语言能力也只能说刚刚入门,难免会有误解的地方,希望大家能及时指正。 10 | 11 | ---------- 12 | ##Swoole版本:1.7.4-stable 13 | ------------- 14 | 15 | Swoole没有采用多线程模型而是使用的多进程模型,在一定程度上减少了访问数据时加锁解锁的开销,但同时也引入了新的需求:共享内存。 16 | 17 | 直接上源码吧,在Swoole中,对于ShareMemory的声明在swoole.h 464行-478行,如下: 18 | ```c 19 | #define SW_SHM_MMAP_FILE_LEN 64 20 | typedef struct _swShareMemory_mmap 21 | { 22 | int size; 23 | char mapfile[SW_SHM_MMAP_FILE_LEN]; 24 | int tmpfd; 25 | int key; 26 | int shmid; 27 | void *mem; 28 | } swShareMemory; 29 | 30 | void *swShareMemory_mmap_create(swShareMemory *object, int size, char *mapfile); 31 | int swShareMemory_mmap_free(swShareMemory *object); 32 | void *swShareMemory_sysv_create(swShareMemory *object, int size, int key); 33 | int swShareMemory_sysv_free(swShareMemory *object, int rm); 34 | ``` 35 | 这里,Rango声明了一个结构体swShareMemory表示共享内存的结构。在内存中,一个完整的swShareMemory存储形式如下: 36 | 37 | | swShareMemory | mem | 38 | 39 | 其中,首部swShareMemory存放了共享内存的相关信息,尾部的void *mem指针指向共享内存的起始地址。 40 | int size代表共享内存的大小(不包括swShareMemory结构体大小), 41 | char mapfile[]代表共享内存使用的内存映射文件的文件名, 42 | int tmpfd为内存映射文件的描述符, 43 | int key代表使用Linux自带的shm系列函数创建的共享内存的key值, 44 | int shmid为shm系列函数创建的共享内存的id(类似于fd)。 45 | 46 | 接下来看对应的四个处理函数,从名字上可以区分,mmap代表使用内存映射文件的共享内存,sysv代表使用shm系列函数的共享内存。这四个函数的实现写在ShareMemory.c文件中。这里重点解析 swShareMemory_mmap_create 方法和swShareMemory_sysv_create 方法。 47 | 48 | 下面贴上swShareMemory_mmap_create的核心代码: 49 | ```c 50 | #ifdef MAP_ANONYMOUS 51 | flag |= MAP_ANONYMOUS; 52 | #else 53 | if(mapfile == NULL) 54 | { 55 | mapfile = "/dev/zero"; 56 | } 57 | if((tmpfd = open(mapfile, O_RDWR)) < 0) 58 | { 59 | return NULL; 60 | } 61 | strncpy(object->mapfile, mapfile, SW_SHM_MMAP_FILE_LEN); 62 | object->tmpfd = tmpfd; 63 | #endif 64 | mem = mmap(NULL, size, PROT_READ | PROT_WRITE, flag, tmpfd, 0); 65 | ``` 66 | 先解释一下 MAP_ANONYMOUS 宏,该宏定义在 mman-linux.h 内,定义如下: 67 | ```c 68 | # define MAP_ANONYMOUS 0x20 /* Don't use a file. */ 69 | ``` 70 | 使用这个宏代表使用mmap生成的映射区不与任何文件关联。我的理解是:此处创建一个仅存在与内存中虚拟文件,用来存放需要共享的内容。 71 | 72 | 其次解释 /dev/zero 。熟悉Linux的应该都知道 /dev/zero 和 /dev/null , 这是Linux系统中的两个特殊文件,/dev/zero的特性是,所有写入该文件的数据都会消失,如果从该文件中读取内容,只能读取到连续的‘\0’。 73 | 74 | 源码解释: 75 | 如果定义了 MAP_ANONYMOUS 宏,则添加进flag中。否则,判定mapfile是否为空,如果为空,则指定mapfile为/dev/zero ,然后打开文件,将描述符赋值给tmpfd。最后,通过mmap打开大小为size的映射内存,指定内存可读可写,并将内存地址赋值给mem。 76 | 77 | swShareMemory_sysv_create的核心源码如下: 78 | ```c 79 | if (key == 0) 80 | { 81 | key = IPC_PRIVATE; 82 | } 83 | if ((shmid = shmget(key, size, SHM_R | SHM_W | IPC_CREAT)) < 0) 84 | { 85 | swWarn("shmget() failed. Error: %s[%d]", strerror(errno), errno); 86 | return NULL; 87 | } 88 | if ((mem = shmat(shmid, NULL, 0)) < 0) 89 | { 90 | swWarn("shmat() failed. Error: %s[%d]", strerror(errno), errno); 91 | return NULL; 92 | } 93 | else 94 | { 95 | object->key = key; 96 | object->shmid = shmid; 97 | object->size = size; 98 | object->mem = mem; 99 | return mem; 100 | } 101 | ``` 102 | 源码解释: 103 | 如果key 为0,则表示需要创建新的共享内存,则将key赋值为IPC_PRIVATE 宏。然后,调用shmget方法,如果key为IPC_PRIVATE,则创建一个大小为size的可读可写的共享内存,并获取到共享内存标识符shmid,否则获取到IPC值为key的共享内存标识符shmid。接着,调用shmat方法获取到shmid对应的共享内存首地址,并赋值给mem。 104 | 105 | 两个free方法则是分别调用munmap方法和shmctl方法释放内存,不再贴上源码。 106 | 107 | 时间缘故,这次分析就到这里,下一篇将介绍Swoole的三种类型的MemoryPool的具体实现。 108 | -------------------------------------------------------------------------------- /03.三种MemoryPool(下).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.4-stable 5 | ------------- 6 | 接下来是RingBuffer。这相当于一个循环数组,每一次申请的一块内存在该数组中占据一个位置,这些内存块是可以不等长的,因此每个内存块需要有一个记录其长度的变量。这里贴出swRingBuffer_head结构体的代码: 7 | ```c 8 | typedef struct _swRingBuffer_item 9 | { 10 | volatile uint32_t lock; 11 | volatile uint32_t length; 12 | } swRingBuffer_head; 13 | ``` 14 | 每一个结构体代表一个RingBuffer中的内存块,其中lock变量标记该内存块是否被占用,length变量标记该内存块的长度。 15 | 接着是swRingBuffer结构体的声明: 16 | ```c 17 | typedef struct _swRingBuffer 18 | { 19 | uint8_t shared; // 可共享 20 | size_t size; // 内存池大小 21 | volatile off_t alloc_offset; // 分配内存的起始长度 22 | volatile off_t collect_offset; // 可用内存的终止长度 23 | volatile uint32_t free_n; // 有多少个内存块待回收 24 | void *memory; // 内存池的起始地址 25 | } swRingBuffer; 26 | ``` 27 | 每一个结构体代表一个RingBuffer内存池。这里先要说明一下RingBuffer的三个变量:alloc_offset,collect_offset,free_n。alloc_offset变量是分配内存的起始地址,代表的是RingBuffer现有的可用空间的起始地址;collect_offset变量是分配内存的终止地址,代表的是RingBuffer现有的可用空间的结束地址。为了方便理解,大家可以想象一下循环队列,alloc_offset和collect_offset就是标记队头和队尾的标记,每一次分配内存就相当于入队,每一次释放内存就相当于出队。而free_n变量是用于标记当前还有多少个已释放的内存块待回收。这是因为RingBuffer采用的是连续分配,可能会存在一些已经被free的内存块夹在两个没有free的内存块中间,没有被立即回收,就需要一个变量去通知内存池回收这些内存。 28 | 29 | RingBuffer的创建函数为swRingBuffer_new,其声明在swoole.h文件的514 – 517行。入下: 30 | ```c 31 | /** 32 | * RingBuffer, In order for malloc / free 33 | */ 34 | swMemoryPool *swRingBuffer_new(size_t size, uint8_t shared); 35 | ``` 36 | 该函数的具体定义在RingBuffer.c中,创建过程与FixedPool基本类似,就不再额外分析,大家自行阅读源码即可。 37 | 38 | 和FixedPool类似,RingBuffer也拥有4个函数用于操作内存池,其函数声明如下: 39 | ```c 40 | static void swRingBuffer_destory(swMemoryPool *pool); 41 | static sw_inline void swRingBuffer_collect(swRingBuffer *object); 42 | static void* swRingBuffer_alloc(swMemoryPool *pool, uint32_t size); 43 | static void swRingBuffer_free(swMemoryPool *pool, void *ptr); 44 | ``` 45 | 其中alloc、destroy、free三个函数的功能很明确,swRingBuffer_collect函数用于回收已经不被占用的内存。这里着重分析alloc函数和collect函数。 46 | 首先是collect函数。在发现内存池剩余不足或分配内存结束后,RingBuffer都会调用collect函数去回收已经没有被占用的内存。其核心代码如下: 47 | 48 | ```c 49 | for(i = 0; imemory + object->collect_offset); 52 | 53 | swTraceLog(SW_TRACE_MEMORY, "collect_offset=%d, item_length=%d, lock=%d", object->collect_offset, item->length, item->lock); 54 | 55 | //can collect 56 | if (item->lock == 0) 57 | { 58 | object->collect_offset += (sizeof(swRingBuffer_head) + item->length); 59 | if (object->free_n > 0) 60 | { 61 | object->free_n --; 62 | } 63 | if (object->collect_offset >= object->size) 64 | { 65 | object->collect_offset = 0; 66 | } 67 | } 68 | else 69 | { 70 | break; 71 | } 72 | } 73 | ``` 74 | 源码解释:每一次循环,都会获取当前collect_offset指向的地址代表的内存块,并获取其swRingBuffer_head结构,如果该内存块已经被free,则将collect_offset标记后移该内存块的长度,回收该内存。如果发现collect_offset超出了内存池大小,则将collect_offset移到内存池头部。 75 | 76 | alloc函数太长,在此不贴出源码,只写出伪代码供分析: 77 | 78 | start_alloc: 79 | if( alloc_offset < collect_offset ) // 起始地址在终止地址左侧 80 | { 81 | head_alloc: 82 | 计算剩余内存大小 83 | 84 | if( 内存足够 ) 85 | goto do_alloc 86 | else if( 内存不足且已经回收过内存) 87 | return NULL; 88 | else 89 | { 90 | try_collect = 1; 91 | 调用collect 92 | goto start_alloc 93 | } 94 | } 95 | else // 起始地址在终止地址右侧(终止地址被移动到了首部) 96 | { 97 | 计算从alloc_offset到内存池尾部的剩余内存大小 98 | if( 内存足够 ) 99 | goto do_alloc 100 | else 101 | { 102 | 标记尾部剩余内存为可回收状态 103 | 将alloc_offset移动到首部 104 | goto head_alloc 105 | } 106 | } 107 | do_alloc: 108 | 实际分配内存块并设置属性,移动alloc_offset标记 109 | 如果free_n大于0,则回收内存。 110 | 111 | 最后是MemoryGlobal。MemoryGlobal是一个比较特殊的内存池。说实话我没有看懂它的作用,所以我决定先暂时跳过MemoryGlobal,等了解其具体使用场景时再来分析这一块。 112 | -------------------------------------------------------------------------------- /04.锁和信号(上).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.4-stable 5 | ------------- 6 | 写在开头的废话:原本计划是于第四章开始reactor模块的分析,但是发现reactor模块牵扯到太多的其他模块,没法一开始就直接分析reactor,只能将其延后。一番考察之后,我决定从swoole的Lock模块入手继续分析。这一块我的知识储备有很大的空缺,估计会比较吃力…… 7 | 8 | -------------------------------- 我是分割线 --------------------------------------- 9 | 10 | Swoole中关于Lock模块的全部声明在swoole.h文件的 364 – 461行(结构体声明)以及 534 – 551 行(函数声明),包括swFileLock(文件锁),swMutex(互斥锁),swRWLock(读写锁),swSpinLock(自旋锁),swAtomicLock(原子锁),swSem(信号量)和swCond(条件变量)。其中除了swCond以外,其他锁和信号都通过swLock做了进一步封装。 11 | 12 | Swoole声明了一个枚举类型SW_LOCKS用来代表这些不同类型的锁,其定义如下: 13 | ```c 14 | enum SW_LOCKS 15 | { 16 | SW_RWLOCK = 1, 17 | #define SW_RWLOCK SW_RWLOCK 18 | SW_FILELOCK = 2, 19 | #define SW_FILELOCK SW_FILELOCK 20 | SW_MUTEX = 3, 21 | #define SW_MUTEX SW_MUTEX 22 | SW_SEM = 4, 23 | #define SW_SEM SW_SEM 24 | SW_SPINLOCK = 5, 25 | #define SW_SPINLOCK SW_SPINLOCK 26 | SW_ATOMLOCK = 6, 27 | #define SW_ATOMLOCK SW_ATOMLOCK 28 | }; 29 | ``` 30 | 首先是swLock结构体,该结构体是全部锁类型的总封装,其定义在swoole.h文件的429 – 449行,声明如下: 31 | ```c 32 | struct _swLock 33 | { 34 | int type; // 锁的类型,取值于SW_LOCKS 35 | union // 锁的对象,一个union,稍后分析 36 | { 37 | swMutex mutex; 38 | swRWLock rwlock; 39 | swFileLock filelock; 40 | swSem sem; 41 | swAtomicLock atomlock; 42 | #ifdef HAVE_SPINLOCK 43 | swSpinLock spinlock; 44 | #endif 45 | } object; 46 | int (*lock_rd)(struct _swLock *lock); // 以下是六个操作函数 47 | int (*lock)(struct _swLock *lock); 48 | int (*unlock)(struct _swLock *lock); 49 | int (*trylock_rd)(struct _swLock *lock); 50 | int (*trylock)(struct _swLock *lock); 51 | int (*free)(struct _swLock *lock); 52 | }; 53 | ``` 54 | 这里的union(中译“联合体”)是一个非常好的设计,这保证了在同一时刻,一个swLock只会存在一个类型的锁。这里需要做一下特别说明,lock_rd和trylock_rd两个函数是专门为了swFileLock和swRWLock设计的,其他锁没有这两个函数。 55 | 56 | 那么,一个个来。首先是文件锁,文件锁的结构体声明在swoole.h的 385 – 389 行: 57 | ```c 58 | //文件锁 59 | typedef struct _swFileLock 60 | { 61 | struct flock lock_t; 62 | int fd; 63 | } swFileLock; 64 | ``` 65 | 其中struct flock lock_t为Linux系统的文件锁结构,int fd为被锁控制的文件描述符。(struct flock的详细说明参考http://www.gnu.org/software/libc/manual/html_node/File-Locks.html) 66 | 67 | 创建一个文件锁的函数声明在swoole.h的537行,如下: 68 | ```c 69 | int swFileLock_create(swLock *lock, int fd); 70 | ``` 71 | 其定义就是初始化一个type为SW_FILELOCK的锁并指定对应的操作函数。文件锁的操作函数声明在FileLock.c文件中,其声明如下: 72 | ```c 73 | static int swFileLock_lock_rd(swLock *lock); 74 | static int swFileLock_lock_rw(swLock *lock); 75 | static int swFileLock_unlock(swLock *lock); 76 | static int swFileLock_trylock_rw(swLock *lock); 77 | static int swFileLock_trylock_rd(swLock *lock); 78 | static int swFileLock_free(swLock *lock); 79 | ``` 80 | 其中rd代表读锁,rw代表写锁。 81 | 这里需要介绍一下fcntl函数,其函数原型为: 82 | ```c 83 | int fcntl(int fd, int cmd); 84 | int fcntl(int fd, int cmd, long arg); 85 | int fcntl(int fd, int cmd, struct flock *lock); 86 | ``` 87 | 在这里,该函数仅用于申请和释放文件锁,因此只用到了两个cmd:F_SETLK 和 F_SETLKW。 88 | F_SETLK:在指定的字节范围获取锁(F_RDLCK, F_WRLCK)或释放锁(F_UNLCK)。如果和另一个进程的锁操作发生冲突,返回 -1并将errno设置为EACCES或EAGAIN。 89 | F_SETLKW:行为如同F_SETLK,除了不能获取锁时会睡眠等待外。如果在等待的过程中接收到信号,会即时返回并将errno置为EINTR。 90 | 91 | 回到swFileLock。因为每个函数很短,所以这里直接贴出全部操作函数: 92 | ```c 93 | // 申请文件读锁RDLOCK 94 | static int swFileLock_lock_rd(swLock *lock) 95 | { 96 | lock->object.filelock.lock_t.l_type = F_RDLCK; 97 | return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); 98 | } 99 | // 申请文件写锁 100 | static int swFileLock_lock_rw(swLock *lock) 101 | { 102 | lock->object.filelock.lock_t.l_type = F_WRLCK; 103 | return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); 104 | } 105 | // 释放文件锁 106 | static int swFileLock_unlock(swLock *lock) 107 | { 108 | lock->object.filelock.lock_t.l_type = F_UNLCK; 109 | return fcntl(lock->object.filelock.fd, F_SETLKW, &lock->object.filelock); 110 | } 111 | // 尝试是否能申请写锁 112 | static int swFileLock_trylock_rw(swLock *lock) 113 | { 114 | // lock->object.filelock.lock_t.l_type = F_RDLCK; // 该处为原代码,经作者确认,为bug,下一行是正确的代码 115 | lock->object.filelock.lock_t.l_type = F_WRLCK; 116 | return fcntl(lock->object.filelock.fd, F_SETLK, &lock->object.filelock); 117 | } 118 | // 尝试是否能申请读锁 119 | static int swFileLock_trylock_rd(swLock *lock) 120 | { 121 | // lock->object.filelock.lock_t.l_type = F_WRLCK; // 该处为原代码,经作者确认,为bug,下一行是正确的代码 122 | lock->object.filelock.lock_t.l_type = F_RDLCK; 123 | return fcntl(lock->object.filelock.fd, F_SETLK, &lock->object.filelock); 124 | } 125 | // 调用close函数释放锁 126 | static int swFileLock_free(swLock *lock) 127 | { 128 | return close(lock->object.filelock.fd); 129 | } 130 | ``` 131 | 这里可能有的人会有疑问:明明fcntl的函数要求传入一个flock结构体,而这里却传入了一个swFileLock结构体呢?因为这里flock结构体位于swFileLock结构体头部,swFileLock的首地址就是flock结构体的首地址。 132 | (swoole的文件锁本身并没有复杂的结构,这里需要对flock以及fcntl函数有足够的了解,本文仅作简单说明,详细信息请参考http://www.cnblogs.com/papam/archive/2009/09/02/1559154.html) 133 | 134 | -------------------------------------------------------------------------------- /02.三种MemoryPool(上).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.4-stable 5 | ------------- 6 | Swoole中为了更好的进行内存管理,减少频繁分配释放内存空间造成的损耗和内存碎片,Rango设计并实现了三种不同功能的MemoryPool:FixedPool,RingBuffer和MemoryGlobal。 7 | Rango声明了一个swMemoryPool结构体来表示一个内存池,该结构体在swoole.h头文件中 501-507行声明,结构如下: 8 | ```c 9 | typedef struct _swMemoryPool 10 | { 11 | void *object; 12 | void* (*alloc)(struct _swMemoryPool *pool, uint32_t size); 13 | void (*free)(struct _swMemoryPool *pool, void *ptr); 14 | void (*destroy)(struct _swMemoryPool *pool); 15 | } swMemoryPool; 16 | ``` 17 | (PS:虽然原来也知道结构体中可以通过存放函数指针模拟一个类,但是现在才学到可以通过传入一个指针参数来模拟this指针,使结构体更像一个类) 18 | 这里看到,首先是一个void*型指针,用于标记内存池的首地址。另外三个函数大家都不会陌生,分别用于从内存池中拿到一块内存、释放一块内存、销毁整个内存池。 19 | 20 | 基类介绍完了,下面来看具体的子类实现。 21 | 首先是FixedPool。FixedPool是随机分配内存池(random alloc/free),将一整块内存空间切分成**等大小**的一个个小块,每次分配其中的一个小块作为要使用的内存,这些小块以**链表**的形式存储。 22 | FixedPool的全部定义声明均在src/memory/FixedPool.c文件内。Rango声明了两个结构体swFixedPool_slice和swFixedPool来实现管理内存池。 23 | swFixedPool_slice为内存池中每个小块的结构声明,其定义如下: 24 | ```c 25 | typedef struct _swFixedPool_slice 26 | { 27 | uint8_t lock; 28 | struct _swFixedPool_slice *next; 29 | struct _swFixedPool_slice *pre; 30 | char data[0]; 31 | 32 | } swFixedPool_slice; 33 | ``` 34 | 可以看出,每个slice为一个链表节点(双向链表),unit8_t lock用于标记该节点是否被占用(0代表空闲,1代表已占用),next和pre为该节点的后继、前驱指针,char data[0]为内存空间指针(关于0长度数组的解释,请看[http://blog.csdn.net/liuaigui/article/details/3680404](http://blog.csdn.net/liuaigui/article/details/3680404)) 35 | 36 | swFixedPool为内存池实体,包括了内存池相关信息。定义如下: 37 | ``` 38 | typedef struct _swFixedPool 39 | { 40 | void *memory; // 内存指针,指向一片内存空间 41 | size_t size; // 内存空间大小 42 | 43 | swFixedPool_slice *head; // 链表头部节点 44 | swFixedPool_slice *tail; // 链表尾部节点,两个指针用于快速访问和移动节点 45 | 46 | /** 47 | * total memory size,节点数目 48 | */ 49 | uint32_t slice_num; 50 | 51 | /** 52 | * memory usage ,已经使用的节点 53 | */ 54 | uint32_t slice_use; 55 | 56 | /** 57 | * Fixed slice size 58 | */ 59 | uint32_t slice_size; 节点大小 60 | 61 | /** 62 | * use shared memory 63 | */ 64 | uint8_t shared; 是否共享内存 65 | 66 | } swFixedPool; 67 | ``` 68 | (参数太多就不文字叙述,直接在变量名后以注释格式解释) 69 | 这里先看swFixedPool_new的具体定义。其声明如下: 70 | ```c 71 | /** 72 | * FixedPool, random alloc/free fixed size memory 73 | * Location: swoole.h 512L 74 | */ 75 | swMemoryPool * swFixedPool_new(uint32_t size, uint32_t trunk_size, uint8_t shared); 76 | ``` 77 | 第一个参数size为每一个小块的大小,第二个参数trunk_size为小块的总数,第三个参数shared代表该内存池是否为共享内存,返回的是被创建的内存池指针。下面贴出代码: 78 | ```c 79 | size_t size = slice_size * slice_num; 80 | size_t alloc_size = size + sizeof(swFixedPool) + sizeof(swMemoryPool); 81 | void *memory = (shared == 1) ? sw_shm_malloc(alloc_size) : sw_malloc(alloc_size); 82 | 83 | swFixedPool *object = memory; 84 | memory += sizeof(swFixedPool); 85 | bzero(object, sizeof(swFixedPool)); 86 | 87 | object->shared = shared; 88 | object->slice_num = slice_num; 89 | object->slice_size = slice_size; 90 | object->size = size; 91 | 92 | swMemoryPool *pool = memory; 93 | memory += sizeof(swMemoryPool); 94 | pool->object = object; 95 | pool->alloc = swFixedPool_alloc; // 指定三个操作函数 96 | pool->free = swFixedPool_free; 97 | pool->destroy = swFixedPool_destroy; 98 | 99 | object->memory = memory; 100 | 101 | swFixedPool_init(object); 102 | ``` 103 | 前两行是计算出内存池实际大小:slice大小*slice数量+MemoryPool头部大小+FixedPool头部大小。接下来根据是否是共享内存来判定使用哪种分配内存的方法(其中sw_shm_malloc方法将在上一章补充)。接下来依次填充swFixedPool、swMemoryPool的相关属性,实际内存空间起始地址存放于swFixedPool的memory中。接下来调用swFixedPool_init方法初始化内存池。 104 | 105 | FixedPool同时拥有四个操作函数分别用于初始化、分配空间、释放空间、销毁内存池。定义如下: 106 | ```c 107 | static void swFixedPool_init(swFixedPool *object); 108 | static void* swFixedPool_alloc(swMemoryPool *pool, uint32_t size); 109 | static void swFixedPool_free(swMemoryPool *pool, void *ptr); 110 | static void swFixedPool_destroy(swMemoryPool *pool); 111 | ``` 112 | 首先是swFixedPool_init函数。该函数用于初始化一个内存池结构体,其核心代码如下: 113 | ```c 114 | void *cur = object->memory; 115 | void *max = object->memory + object->size; 116 | do 117 | { 118 | slice = (swFixedPool_slice *) cur; 119 | bzero(slice, sizeof(swFixedPool_slice)); 120 | 121 | if (object->head != NULL) 122 | { 123 | object->head->pre = slice; 124 | slice->next = object->head; 125 | } 126 | else 127 | { 128 | object->tail = slice; 129 | } 130 | 131 | object->head = slice; 132 | cur += (sizeof(swFixedPool_slice) + object->slice_size); 133 | slice->pre = (swFixedPool_slice *) cur; 134 | } while (cur < max); 135 | ``` 136 | 源码解释:从内存空间的首部开始,每次初始化一个slice大小的空间,并插入到链表的头部。至于为什么使用链表这个结构会稍后说明。 137 | 内存分配好了,如何使用呢?这里再看swFixedPool_alloc函数。这里要注意,因为FixedPool的内存块大小是固定的,所以函数中第二个参数size只是为了符合swMemoryPool中的声明,不具有实际作用,可以随便填一个数。其核心代码如下: 138 | ```c 139 | slice = object->head; 140 | if (slice->lock == 0) 141 | { 142 | slice->lock = 1; 143 | /** 144 | * move next slice to head (idle list) 145 | */ 146 | object->head = slice->next; 147 | slice->next->pre = NULL; 148 | 149 | /* 150 | * move this slice to tail (busy list) 151 | */ 152 | object->tail->next = slice; 153 | slice->next = NULL; 154 | slice->pre = object->tail; 155 | object->tail = slice; 156 | 157 | return slice->data; 158 | } 159 | else 160 | { 161 | return NULL; 162 | } 163 | ``` 164 | 源码解释:首先获取内存池链表首部的节点,并判断该节点是否被占用,如果被占用,说明内存池已满,返回null(因为所有被占用的节点都会被放到尾部);如果未被占用,则将该节点的下一个节点移到首部,并将该节点移动到尾部,标记该节点为占用状态,返回该节点的数据域。 165 | 166 | 当一个内存块用完,需要释放内存,则调用swFixedPool_free方法。该函数第二个参数为需要释放的数据域。其核心代码如下: 167 | ```c 168 | slice = ptr - sizeof(swFixedPool_slice); 169 | slice->lock = 0; 170 | 171 | //list head, AB 172 | if (slice->pre == NULL) 173 | { 174 | return; 175 | } 176 | //list tail, DE 177 | if (slice->next == NULL) 178 | { 179 | slice->pre->next = NULL; 180 | } 181 | //middle BCD 182 | else 183 | { 184 | slice->pre->next = slice->next; 185 | slice->next->pre = slice->pre; 186 | } 187 | slice->pre = NULL; 188 | slice->next = object->head; 189 | object->head->pre = slice; 190 | object->head = slice; 191 | ``` 192 | 源码解释:首先通过移动ptr指针获得slice对象,并将占用标记lock置为0。如果该节点为头节点,则直接返回。如果不是头节点,则将该节点移动到链表头部。 193 | swFixedPool_destroy为直接释放整片内存池,在此不再详解。 194 | (PS:目前先放出FixedPool的源码分析,今晚再放出RingBuffer和MmemoyGlobal的分析。) 195 | -------------------------------------------------------------------------------- /06.Pipe管道.md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.4-stable 5 | ------------- 6 | Pipe(管道)用于进程之间的数据交互,Linux系统本身提供了pipe函数用于创建一个半双工通信管道,而在swoole中也利用eventfd和unix sock封装了两种管道,使得进程间的通信更加灵活。 7 | Swoole提供了一个基本结构体swPipe用于操作不同类型的Pipe,其声明在swoole.h文件的**315 – 326** 行,声明如下: 8 | ```c 9 | typedef struct _swPipe 10 | { 11 | void *object; 12 | int blocking; 13 | double timeout; 14 | 15 | int (*read)(struct _swPipe *, void *recv, int length); 16 | int (*write)(struct _swPipe *, void *send, int length); 17 | int (*getFd)(struct _swPipe *, int isWriteFd); 18 | int (*close)(struct _swPipe *); 19 | } swPipe; 20 | ``` 21 | 其中,首部的void *object指向具体的管道结构体(Base,Eventfd,Unsock),int blocking标记该管道是否是阻塞类型,double timeout为阻塞的超时时间,余下四个函数用于对管道进行读写、关闭等操作。 22 | 23 | ###一.基于pipe函数的PipeBase 24 | 前面提到过,Linux本身提供了一个pipe函数用于创建一个管道,swoole基于这个函数进行了一层封装用于简化操作,并命名为PipeBase,其结构体声明在PipeBase.c文件中,声明如下: 25 | ```c 26 | typedef struct _swPipeBase 27 | { 28 | int pipes[2]; 29 | } swPipeBase; 30 | ``` 31 | 其中的 int pipes[2] 用于存放pipe的读端fd和写端fd。 32 | 创建一个PipeBase管道的函数声明在swoole.h文件的**328**行,声明如下: 33 | ```c 34 | int swPipeBase_create(swPipe *p, int blocking); 35 | ``` 36 | 第二个参数标记该管道是否阻塞。该函数具体定义在PipeBase.c文件中,其核心代码如下: 37 | ```c 38 | swPipeBase *object = sw_malloc(sizeof(swPipeBase)); 39 | if (object == NULL) 40 | { 41 | return -1; 42 | } 43 | p->blocking = blocking; 44 | ret = pipe(object->pipes); 45 | if (ret < 0) 46 | { 47 | swWarn("pipe create fail. Error: %s[%d]", strerror(errno), errno); 48 | return -1; 49 | } 50 | else 51 | { 52 | //Nonblock 53 | if (blocking == 0) 54 | { 55 | swSetNonBlock(object->pipes[0]); 56 | swSetNonBlock(object->pipes[1]); 57 | } 58 | else 59 | { 60 | p->timeout = -1; 61 | } 62 | 63 | p->object = object; 64 | p->read = swPipeBase_read; 65 | p->write = swPipeBase_write; 66 | p->getFd = swPipeBase_getFd; 67 | p->close = swPipeBase_close; 68 | } 69 | ``` 70 | 源码解释:创建一个PipeBase结构体,指定管道阻塞类型为blocking参数,并调用pipe函数创建一个linux pipe,获取pipe fd。如果创建管道成功,若管道为非阻塞模式,则调用swSetNonBlock函数(声明于swoole.h文件的**641**行,定义于Base.c文件的**507 – 529**行,该函数的分析在本章末尾)设置两个fd为非阻塞;若管道为阻塞模式,则指定timeout为-1. 71 | 72 | PipeBase有四个操作函数分别用于读、写、获取描述符以及关闭管道。其声明如下: 73 | ```c 74 | static int swPipeBase_read(swPipe *p, void *data, int length); 75 | static int swPipeBase_write(swPipe *p, void *data, int length); 76 | static int swPipeBase_getFd(swPipe *p, int isWriteFd); 77 | static int swPipeBase_close(swPipe *p); 78 | ``` 79 | 1. swPipeBase_read函数核心源码如下: 80 | ```c 81 | if (p->blocking == 1 && p->timeout > 0) 82 | { 83 | if (swSocket_wait(object->pipes[0], p->timeout * 1000, SW_EVENT_READ) < 0) 84 | { 85 | return SW_ERR; 86 | } 87 | } 88 | return read(object->pipes[0], data, length); 89 | ``` 90 | 源码解释:如果该管道为阻塞模式,且指定了timeout时间,则调用swSocket_wait函数(定义于Base.c文件中的236 – 268行,该函数分析在本章末尾)执行等待,若超时,则返回SW_ERR;否则,直接调用read方法读取数据(此时,若管道为非阻塞模式,因已经指定了fd的options,所以read方法不会阻塞;若为阻塞模式,则read函数会一直阻塞到有数据可读) 91 | 2. swPipeBase_write函数直接调用write函数写出数据 92 | 3. swPipeBase_getFd函数根据 isWriteFd 判定返回 写fd 或者 读fd 93 | 4. swPipeBase_close函数调用close函数关闭描述符并释放swPipeBase内存。 94 | ###二.基于eventfd函数的PipeEventfd 95 | Eventfd是Linux提供的一个事件提醒(event notification)功能,eventfd函数创建一个对象用于事件的等待/提醒,可以从内核态发送通知到用户态。一个eventfd创建的对象包括一个无符号的64bit整型计数器,该计数器被内核持有。 96 | eventfd函数的第二个参数用于指定flags,其中EFD_NONBLOCK指定eventfd是否非阻塞,EFD_SEMAPHORE用于指定eventfd的读写效果。 97 | Swoole用一个结构体swPipeEventfd代表一个eventfd类型的管道,其声明如下: 98 | ```c 99 | typedef struct _swPipeEventfd 100 | { 101 | int event_fd; 102 | } swPipeEventfd; 103 | ``` 104 | 其中event_fd就是用eventfd函数获得的描述符。 105 | 创建一个swPipeEventfd的函数声明在swoole.h的**329**行,声明如下: 106 | ```c 107 | int swPipeEventfd_create(swPipe *p, int blocking, int semaphore, int timeout); 108 | ``` 109 | 其中第三个参数semaphore标记该管道是否仅用于提供通知,第四个参数timeout指定阻塞超时的时间。 110 | 该函数具体定义在PipeEventfd.c文件,其核心代码如下: 111 | 112 | ```c 113 | swPipeEventfd *object = sw_malloc(sizeof(swPipeEventfd)); 114 | if (object == NULL) 115 | { 116 | return -1; 117 | } 118 | 119 | flag = EFD_NONBLOCK; 120 | 121 | if (blocking == 1) 122 | { 123 | if (timeout > 0) 124 | { 125 | flag = 0; 126 | p->timeout = -1; 127 | } 128 | else 129 | { 130 | p->timeout = timeout; 131 | } 132 | } 133 | 134 | #ifdef EFD_SEMAPHORE 135 | if (semaphore == 1) 136 | { 137 | flag |= EFD_SEMAPHORE; 138 | } 139 | #endif 140 | 141 | p->blocking = blocking; 142 | efd = eventfd(0, flag); 143 | ``` 144 | 源码解释:创建一个swPipeEventfd对象,默认设置管道为非阻塞的,若参数blocking指定管道为阻塞,则指定timeout的值。如果指定了semaphore参数,则将EFD_SEMAPHORE加入flags中。最后调用eventfd函数获取event_fd. 145 | 146 | 这里需要特别说明EFD_SEMAPHORE参数。如果一个eventfd被指定了该参数,当eventfd的计数器不为0时,对eventfd的read操作会读到一个8byte长度的整数1,然后计数器的值减1;如果没有指定该参数,当eventfd的计数器不为0时,对eventfd的read操作会将计数器的值读出(8 byte的整数),并将计数器置0. 147 | 148 | PipeEventfd同样有四个操作函数分别用于读、写、获取描述符以及关闭管道。其声明如下 149 | ```c 150 | static int swPipeEventfd_read(swPipe *p, void *data, int length); 151 | static int swPipeEventfd_write(swPipe *p, void *data, int length); 152 | static int swPipeEventfd_getFd(swPipe *p, int isWriteFd); 153 | static int swPipeEventfd_close(swPipe *p); 154 | ``` 155 | 1. swPipeEventfd_read核心源码如下: 156 | ```c 157 | if (p->blocking == 1 && p->timeout > 0) 158 | { 159 | if (swSocket_wait(object->event_fd, p->timeout * 1000, SW_EVENT_READ) < 0) 160 | { 161 | return SW_ERR; 162 | } 163 | } 164 | 165 | while (1) 166 | { 167 | ret = read(object->event_fd, data, sizeof(uint64_t)); 168 | if (ret < 0 && errno == EINTR) 169 | { 170 | continue; 171 | } 172 | break; 173 | } 174 | ``` 175 | 源码解释:如果管道为可阻塞且timeout大于0,则调用swSocket_wait函数(定义于Base.c文件中的236 – 268行,该函数分析在本章末尾)执行等待。若超时,则返回SW_ERR;否则,循环调用read函数直到读取到数据。 176 | 2. swPipeEventfd_write函数循环调用write函数直到写入数据成功。 177 | 3. swPipeEventfd_getFd函数返回event_fd 178 | 4. swPipeEventfd_close函数调用close关闭event_fd并释放内存。 179 | ###三.基于unix socket的PipeUnsock 180 | PipeUnsock使用了socketpair函数和AF_UNIX(Unix Socket)来创建一个全双工的“管道”。这其实类似于Linux的pipe函数,不同的是文件描述符fd换成了套接字描述符sockfd,半双工通讯变成了全双工通讯。 181 | swPipeUnsock结构体声明在PipeUnsock.c文件中,其声明如下: 182 | ```c 183 | typedef struct _swPipeUnsock 184 | { 185 | int socks[2]; 186 | } swPipeUnsock; 187 | ``` 188 | 其中socks用于存放两个socket套接字 189 | 创建一个swPipeUnsock的函数声明在swoole.h文件的**330**行,声明如下: 190 | ```c 191 | int swPipeUnsock_create(swPipe *p, int blocking, int protocol); 192 | ``` 193 | 其中第二个参数指定socket使用的协议族(TCP/UDP) 194 | 其核心代码如下: 195 | ```c 196 | p->blocking = blocking; 197 | ret = socketpair(AF_UNIX, protocol, 0, object->socks); 198 | if (ret < 0) 199 | { 200 | swWarn("socketpair() failed. Error: %s [%d]", strerror(errno), errno); 201 | return SW_ERR; 202 | } 203 | else 204 | { 205 | //Nonblock 206 | if (blocking == 0) 207 | { 208 | swSetNonBlock(object->socks[0]); 209 | swSetNonBlock(object->socks[1]); 210 | } 211 | 212 | int sbsize = SwooleG.unixsock_buffer_size; 213 | setsockopt(object->socks[1], SOL_SOCKET, SO_SNDBUF, &sbsize, sizeof(sbsize)); 214 | setsockopt(object->socks[1], SOL_SOCKET, SO_RCVBUF, &sbsize, sizeof(sbsize)); 215 | setsockopt(object->socks[0], SOL_SOCKET, SO_SNDBUF, &sbsize, sizeof(sbsize)); 216 | setsockopt(object->socks[0], SOL_SOCKET, SO_RCVBUF, &sbsize, sizeof(sbsize)); 217 | } 218 | ``` 219 | 源码解释:设置管道阻塞类型,并调用socketpair获取两个套接字,并指定套接字类型为AF_UNIX,如果管道为非阻塞类型,则调用swSetNonBlock函数设置套接字属性。然后指定socket buffer 的大小(SwooleG是一个swServerG结构体,用于存放一些全局变量,该对象声明在Server.c中,在此不作分析),并调用setsockopt函数设置套接字选项。 220 | swPipeUnsock的操作函数和swPipeBase基本一样,在此不再分析。 221 | 222 | 至此,Swoole的Pipe模块已分析完成。 223 | ###附录:Base.c中相关函数的分析 224 | 1. swSetNonBlock 225 | 函数原型:void swSetNonBlock(int sock); (swoole.h 641行) 226 | 核心代码:(Base.c ) 227 | ```c 228 | do 229 | { 230 | opts = fcntl(sock, F_GETFL); 231 | } 232 | while(opts <0 && errno == EINTR); 233 | if (opts < 0) 234 | { 235 | swWarn("fcntl(sock,GETFL) fail"); 236 | } 237 | opts = opts | O_NONBLOCK; 238 | do 239 | { 240 | ret = fcntl(sock, F_SETFL, opts); 241 | } 242 | while(ret <0 && errno == EINTR); 243 | ``` 244 | 源码解释:调用fcntl函数,通过F_GETFL命令获得描述符的状态flags(没有一个比较好的翻译……百度翻译成旗标);将O_NONBLOCK(非阻塞标记)加入flags,并通过fcntl函数的F_SETFL命令设置描述符的状态flags。 245 | 2. swSocket_wait 246 | 函数原型:int swSocket_wait(int fd, int timeout_ms, int events) (swoole.h 648行) 247 | 核心代码:(Base.c 236 – 268行) 248 | ```c 249 | struct pollfd event; 250 | event.fd = fd; 251 | event.events = 0; 252 | 253 | if (events & SW_EVENT_READ) 254 | { 255 | event.events |= POLLIN; 256 | } 257 | if (events & SW_EVENT_WRITE) 258 | { 259 | event.events |= POLLOUT; 260 | } 261 | while (1) 262 | { 263 | int ret = poll(&event, 1, timeout_ms); 264 | if (ret == 0) 265 | { 266 | return SW_ERR; 267 | } 268 | else if (ret < 0 && errno != EINTR) 269 | { 270 | swWarn("poll() failed. Error: %s[%d]", strerror(errno), errno); 271 | return SW_ERR; 272 | } 273 | else 274 | { 275 | return SW_OK; 276 | } 277 | } 278 | ``` 279 | 源码解释:创建 pollfd 结构体,指定监听的fd,并根据events的值指定需要监听的事件(读事件or写事件)。接着在循环中调用poll函数监听,并指定poll函数的超时时间为timeout_ms参数。若成功监听到事件,则返回SW_OK,否则返回SW_ERR。 280 | 281 | 282 | 283 | -------------------------------------------------------------------------------- /05.锁和信号(下).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.4-stable 5 | ------------- 6 | ###二.Mutex互斥锁 7 | 接下来是Mutex(互斥锁)。Swoole的Mutex实现是基于pthread_mutex*方法族实现的,Rango用一个swMutex结构体封装了mutex的两个属性,结构体定义如下: 8 | ```c 9 | //互斥锁 10 | typedef struct _swMutex 11 | { 12 | pthread_mutex_t _lock; // 互斥锁对象 13 | pthread_mutexattr_t attr; // 互斥锁的属性 14 | } swMutex; 15 | ``` 16 | 创建一个互斥锁的函数声明在swoole.h文件的 **536** 行,声明如下: 17 | ```c 18 | int swMutex_create(swLock *lock, int use_in_process); 19 | ``` 20 | 其中lock为锁对象,use_in_process用于标记该锁是否用于进程之间。该函数的具体定义在Mutex.c文件中,其核心代码如下: 21 | 22 | ```c 23 | lock->type = SW_MUTEX; 24 | pthread_mutexattr_init(&lock->object.mutex.attr); 25 | if (use_in_process == 1) 26 | { 27 | pthread_mutexattr_setpshared(&lock->object.mutex.attr, PTHREAD_PROCESS_SHARED); 28 | } 29 | if ((ret = pthread_mutex_init(&lock->object.mutex._lock, &lock->object.mutex.attr)) < 0) 30 | { 31 | return SW_ERR; 32 | } 33 | ``` 34 | 源码解释:设置锁的类型为SW_MUTEX,并调用**pthread_mutexattr_init**函数初始化mutex的属性结构体。如果该进程用于进程之间,则调用**pthread_mutexattr_setpshared**函数设置该锁的共享属性为PTHREAD_PROCESS_SHARED(进程间共享)。接着调用**pthread_mutex_init**函数初始化互斥锁。 35 | 36 | 这里要重点分析三个粗体的函数。这三个函数都属于pthread_mutex*方法族,用于操作mutex以及其attr属性。 37 | 1. pthread_mutexattr_init方法用于初始化一个pthread_mutexattr_t结构体,并默认设置其pshared属性为PTHREAD_PROCESS_PRIVATE(表示可以在进程内使用该互斥锁)。 38 | 2. pthread_mutexattr_setpshared方法用来设置互斥锁变量的作用域。PTHREAD_PROCESS_SHARED表示该互斥锁可被多个进程共享使用(前提是该互斥锁是在共享内存中创建)。PTHREAD_PROCESS_PRIVATE表示该互斥锁仅能被那些由同一个进程创建的线程处理。 39 | 3. pthread_mutex_init方法以动态方式创建互斥锁,并通过参数attr指定互斥锁属性。如果参数attr为空,则默认创建快速互斥锁。 40 | 41 | swMutex的其他操作函数swMutex_lock、swMutex_unlock、swMutex_trylock、free分别对应pthread_mutex_lock、pthread_mutex_unlock、pthread_mutex_trylock、pthread_mutex_destroy,用于加锁、解锁、尝试加锁、销毁swMutex。 42 | 43 | ###三.RWLock读写锁 44 | 读写锁的内容其实与互斥锁相差不大,区别仅在于读写锁有读锁、写锁两个区别,同时底层调用的是pthread_rwlock*系列函数。读写锁swRWLock结构体在swoole.c文件的 **398 – 404** 行声明,如下: 45 | ```c 46 | //读写锁 47 | typedef struct _swRWLock 48 | { 49 | pthread_rwlock_t _lock; // 读写锁对象 50 | pthread_rwlockattr_t attr; // 读写锁属性 51 | 52 | } swRWLock; 53 | ``` 54 | 创建一个读写锁的函数声明在swoole.h文件的 **534** 行,声明如下: 55 | ```c 56 | int swRWLock_create(swLock *lock, int use_in_process); 57 | ``` 58 | 其中lock为锁对象,use_in_process用于标记该锁是否用于进程之间。该函数的具体定义在RWLock.c文件中,其核心代码如下: 59 | ```c 60 | lock->type = SW_RWLOCK; 61 | if(use_in_process == 1) 62 | { 63 | pthread_rwlockattr_setpshared(&lock->object.rwlock.attr, PTHREAD_PROCESS_SHARED); 64 | } 65 | if((ret = pthread_rwlock_init(&lock->object.rwlock._lock, &lock->object.rwlock.attr)) < 0) 66 | { 67 | return SW_ERR; 68 | } 69 | ``` 70 | 源码解释:设置锁类型为SW_RWLOCK,如果该锁用于进程之间,则调用**pthread_rwlockattr_setpshared**函数设置该锁的共享属性为PTHREAD_PROCESS_SHARED(进程间共享)。接着调用**pthread_rwlock_init**初始化该读写锁。 71 | 72 | 这里重点分析两个粗体函数。这两个函数属于pthread_rwlock*方法族,用于操作rwlock以及其attr属性。 73 | 1. pthread_rwlockattr_setpshared用于设置一个pthread_rwlockattr_t的属性,和mutex一样有PTHREAD_PROCESS_SHARED和PTHREAD_PROCESS_PRIVATE两个值。 74 | 2. pthread_rwlock_init用于初始化一个pthread_rwlock_t结构体,创建一个读写锁,并通过attr设置其属性。 75 | (实际上rwlock也有pthread_rwlockattr_init方法,不知道为什么这里没有调用。所有pthread_rwlock*相关的函数请参考http://blog.163.com/software_ark/blog/static/175614594201181665330631/) 76 | 77 | 剩下的就是通过调用pthread_rwlock的相关操作函数如pthread_rwlock_rdlock、pthread_rwlock_wrlock来创建读锁、写锁以及解锁。大家可以直接看RWLock.c文件中的定义,在此为节约篇幅不再叙述。 78 | ###四.SpinLock自旋锁 79 | 首先需要说明一下什么是自旋锁。Spinlock本质上也是一种互斥锁,它和mutex的区别在于Spinlock是通过busy_wait_loop方式来获得锁,不会使线程状态发生切换(用户态->内核态),因此减少了系统调用,执行速度快。但缺点也是有的,Spinlock会一直占用cpu,导致cpu busy飙高,因此要谨慎使用Spinlock。 80 | SpinLock自旋锁的特殊性在于其只有一个pthread_spinlock_t锁对象,没有对应的属性结构体。其swSpinLock结构体在swoole.c文件中 **406 – 412** 行声明,如下: 81 | ```c 82 | //自旋锁 83 | #ifdef HAVE_SPINLOCK 84 | typedef struct _swSpinLock 85 | { 86 | pthread_spinlock_t lock_t; // 自旋锁对象 87 | } swSpinLock; 88 | #endif 89 | ``` 90 | 创建一个自旋锁的函数声明在swoole.h文件的 **534** 行,声明如下: 91 | ```c 92 | int swSpinLock_create(swLock *object, int spin); 93 | ``` 94 | 其中lock为锁对象,spin(为何不继续使用use_in_process……)用于标记该锁是否用于进程之间。该函数的具体定义在SpinLock.c文件中,其核心代码如下: 95 | ```c 96 | lock->type = SW_SPINLOCK; 97 | if ((ret = pthread_spin_init(&lock->object.spinlock.lock_t, use_in_process)) < 0) 98 | { 99 | return -1; 100 | } 101 | ``` 102 | 源码解释:设置锁类型为SW_SPINLOCK,调用pthread_spin_init初始化一个Spinlock对象。 103 | 1. pthread_spin_init用于初始化一个pthread_spinlock_t对象,第二个参数用于指定该spinlock是否可被进程间共享。 104 | 其他操作均为直接调用pthread_spin*方法族的相关函数,具体内容请看SpinLock.c文件。 105 | ###五.原子锁 106 | 首先我说明一下什么是原子操作。所谓原子操作,就是该操作绝不会在执行完毕前被任何其他任务或事件打断,也就说,它的最小的执行单位,不可能有比它更小的执行单位。通常情况下,原子操作都是用于资源计数。 107 | 原子锁就是用来保证操作的原子性。Swoole中自定义了一个原子类型atomic_t,其声明在atomic.h文件中的14行,这个类型在不同位数的操作系统里有不同的长度。同时,swoole使用了gcc提供的__sync_*系列的build-in函数来提供加减和逻辑运算的原子操作。这些函数的声明在atomic.h文件的**16 – 20**行,如下: 108 | ```c 109 | #define sw_atomic_cmp_set(lock, old, set) __sync_bool_compare_and_swap(lock, old, set) 110 | #define sw_atomic_fetch_add(value, add) __sync_fetch_and_add(value, add) 111 | #define sw_atomic_fetch_sub(value, sub) __sync_fetch_and_sub(value, sub) 112 | #define sw_atomic_memory_barrier() __sync_synchronize() 113 | #define sw_atomic_cpu_pause() __asm__ ("pause") 114 | ``` 115 | 这里依次介绍5个函数的作用: 116 | 1. sw_atomic_cmp_set(lock, old, set) 如果lock的值等于old的值,将set的值写入lock。如果相等并写入成功则返回true 117 | 2. sw_atomic_fetch_add(value, add) 将value的值增加add,返回增加前的值 118 | 3. sw_atomic_fetch_sub(value, sub) 将value的值减去sub,返回减去前的值 119 | 4. sw_atomic_memory_barrier() 发出一个full barrier。保证指令执行的顺序合理 120 | 5. sw_atomic_cpu_pause() 这个函数调用的是__asm__(“pause”),通过一番查询,这个调用能让cpu等待一段时间,这个时间由处理器定义。 121 | 122 | Swoole另外声明了一个结构体swAtomicLock来封装原子锁,该结构体在swoole.h文件中 **414 – 419** 行声明,如下: 123 | ```c 124 | //原子锁Lock-Free 125 | typedef struct _swAtomicLock 126 | { 127 | atomic_t lock_t; 128 | uint32_t spin; 129 | } swAtomicLock; 130 | ``` 131 | 其中lock_t为原子锁对象,spin为一个原子锁可以等待的次数(为2的n次幂)。 132 | 原子锁的全部操作函数声明在swoole.h文件的**541 – 544**行,如下: 133 | ```c 134 | int swAtomicLock_create(swLock *object, int spin); 135 | sw_inline int swAtomicLock_lock(swLock *lock); 136 | sw_inline int swAtomicLock_unlock(swLock *lock); 137 | sw_inline int swAtomicLock_trylock(swLock *lock); 138 | ``` 139 | 这里着重介绍swAtomicLock_lock方法,其核心代码如下: 140 | ```c 141 | atomic_t *atomic = &lock->object.atomlock.lock_t; 142 | uint32_t i, n; 143 | while (1) 144 | { 145 | if (*atomic == 0 && sw_atomic_cmp_set(atomic, 0, 1)) 146 | { 147 | return SW_OK; 148 | } 149 | if (SW_CPU_NUM > 1) 150 | { 151 | for (n = 1; n < lock->object.atomlock.spin; n <<= 1) 152 | { 153 | for (i = 0; i < n; i++) 154 | { 155 | sw_atomic_cpu_pause(); 156 | } 157 | 158 | if (*atomic == 0 && sw_atomic_cmp_set(atomic, 0, 1)) 159 | { 160 | return SW_OK; 161 | } 162 | } 163 | } 164 | swYield(); 165 | } 166 | return SW_ERR; 167 | ``` 168 | 源码解释:首先获得atomic_t对象的指针,接着进入一个无限循环。在该循环里,首先判断atomic的值是否为0,并尝试将其赋值为1,如果成功,则直接返回。若前两个判断条件不成立,接着判断CPU数目是否大于1(多核),如果是则进入循环,循环变量为n,每次循环内先执行n次sw_atomic_cpu_pause(),随后再次尝试设置atomic的值为1。每次循环结束后将n的值左移一位(值*2,这就是为何spin的值要是2的幂)。如果CPU的数目为1或者直到内循环结束也没有设置成功atomic,就调用swYield方法交出线程的使用权。 169 | 这里就是在不停的尝试申请atomic锁,如果失败就等待一段时间后再次申请,每次失败后等待的时间会加长。 170 | ###六.信号量 171 | Swoole的Semaphore信号量的实现是基于Linux的semget、semop和semctl函数实现的。结构体swSem用于封装信号量相关的属性,其声明在swoole.h的**421-427**行,如下: 172 | ```c 173 | //信号量 174 | typedef struct _swSem 175 | { 176 | key_t key; // 关键字,通常由ftok()返回 177 | int semid; // 信号量id 178 | int lock_num; // 179 | } swSem; 180 | ``` 181 | 创建一个swSem的函数声明在swoole.h文件的535行,如下: 182 | ```c 183 | int swSem_create(swLock *lock, key_t key, int n); 184 | ``` 185 | 其中lock为信号量对象,key为信号量关键字,n为该信号量拥有的资源数。 186 | 该函数具体定义在Semaphore.c文件内,其核心代码如下: 187 | ```c 188 | lock->type = SW_SEM; 189 | if ((ret = semget(key, n, IPC_CREAT | 0666)) < 0) 190 | { 191 | return SW_ERR; 192 | } 193 | lock->object.sem.semid = ret; 194 | lock->object.sem.lock_num = 0; 195 | ``` 196 | 源码解释:设置锁类型为SW_SEM,并调用**semget**函数创造一个信号量,并返回信号的id。设置信号量id并设置lock_num为0。 197 | swSem的操作函数共有三个:swSem_lock,swSem_unlock,swSem_free。分别对应加锁、解锁和销毁。 198 | swSem_lock的核心代码如下: 199 | ```c 200 | struct sembuf sem; 201 | sem.sem_flg = SEM_UNDO; 202 | sem.sem_num = lock->object.sem.lock_num; 203 | // sem.sem_op = 1; 此行为原始代码,经作者确认为bug 204 | sem.sem_op = -1; 205 | return semop(lock->object.sem.semid, &sem, 1); 206 | ``` 207 | 源码解释:创建一个struct sembuf 结构体,设置sem_flg为SEM_UNFO(程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值),指定操作的信号量为lock_num,设置sem_op为-1(请求资源),最后调用**semop**函数操作信号量。 208 | swSem_unlock的核心代码如下: 209 | ```c 210 | struct sembuf sem; 211 | sem.sem_flg = SEM_UNDO; 212 | sem.sem_num = lock->object.sem.lock_num; 213 | // sem.sem_op = -1; 此行为原始代码,经作者确认为bug 214 | sem.sem_op = 1; 215 | return semop(lock->object.sem.semid, &sem, 1); 216 | ``` 217 | 源码解释:创建一个struct sembuf 结构体,设置sem_flg为SEM_UNFO(程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值),指定操作的信号量为lock_num,设置sem_op为1(释放资源),最后调用**semop**函数操作信号量。 218 | swSem_free函数就是调用**semctl**方法释放信号量。 219 | ###七.Cond条件变量 220 | 条件变量的用处是使线程睡眠等待某种条件出现后唤醒线程,是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个是线程等待“条件成立”而挂起,另一个是线程使“条件成立”并发出信号。为了防止竞争,条件变量通常与一个互斥锁结合在一起。 221 | Swoole中,条件变量的实现使用了pthread_cond*方法族,其封装结构体swCond声明在swoole.c文件的**451 - 461**行,如下: 222 | ```c 223 | //Cond 224 | typedef struct _swCond 225 | { 226 | swLock lock; // 互斥锁 227 | pthread_cond_t cond; // 条件变量对象 228 | 229 | int (*wait)(struct _swCond *object); // 四个操作函数 230 | int (*timewait)(struct _swCond *object,long,long); 231 | int (*notify)(struct _swCond *object); 232 | int (*broadcast)(struct _swCond *object); 233 | } swCond; 234 | ``` 235 | swCond的相关操作函数共六个,声明在swoole.c文件中的**546 – 551**行,如下: 236 | ```c 237 | int swCond_create(swCond *cond); 238 | int swCond_notify(swCond *cond); 239 | int swCond_broadcast(swCond *cond); 240 | int swCond_timewait(swCond *cond, long sec, long nsec); 241 | int swCond_wait(swCond *cond); 242 | void swCond_free(swCond *cond); 243 | ``` 244 | 这六个函数均在Cond.c文件中被定义。 245 | 1. swCond_create核心代码: 246 | ```c 247 | if (pthread_cond_init(&cond->cond, NULL) < 0) 248 | { 249 | swWarn("pthread_cond_init fail. Error: %s [%d]", strerror(errno), errno); 250 | return SW_ERR; 251 | } 252 | if (swMutex_create(&cond->lock, 0) < 0) 253 | { 254 | return SW_ERR; 255 | } 256 | ``` 257 | 源码解释:调用pthread_cond_init创建并初始化一个条件变量,并创建对应的互斥锁。 258 | 2. swCond_notify方法调用pthread_cond_signal方法,向另一个正处于阻塞等待状态的线程发信号,使其脱离阻塞状态。需要注意的是,如果有多个线程正在等待,则根据优先级的高低决定由哪个线程收到信号;若优先级相同,则优先让等待时间最长的线程执行。 259 | 3. swCond_broadcast方法调用pthread_cond_broadcast方法,向所有正处于阻塞等待状态的线程发出信号使其脱离阻塞状态。 260 | 4. swCond_timewait方法调用pthread_cond_timedwait方法,计时等待其他线程发出信号,等待时间由参数long sec,long nsec指定。 261 | 5. swCond_wait方法调用pthread_cond_wait方法,等待其他线程发出信号。 262 | 6. swCond_free方法调用pthread_cond_destroy方法销毁信号变量,并销毁互斥锁。 263 | 264 | 至此,swoole全部的锁和信号已分析完成。 265 | 266 | -------------------------------------------------------------------------------- /14.Server模块详解(下).md: -------------------------------------------------------------------------------- 1 | # Swoole源码学习记录 2 | 3 | --- 4 | **swoole版本:1.7.6-stable** 5 | 6 | 上一章已经分析了如何启动swServer的相关函数。本章将继续分析swServer的相关函数, 7 | ##【Table of Contents】 8 | - [1.swServer函数分析](#1swserver%E5%87%BD%E6%95%B0%E5%88%86%E6%9E%90) 9 | - [swServer_addListener](#swserver_addlistener) 10 | - [swServer_listen](#swserver_listen) 11 | - [swServer_addTimer](#swserver_addtimer) 12 | - [swServer_tcp_send](#swserver_tcp_send) 13 | - [swServer_reload](#swserver_reload) 14 | - [2.Server相关结构体分析](#2server%E7%9B%B8%E5%85%B3%E7%BB%93%E6%9E%84%E4%BD%93%E5%88%86%E6%9E%90) 15 | - [swPackage](#swsackage) 16 | - [swPackage_task](#swsackage_task) 17 | - [swPackage_response](#swsackage_response) 18 | 19 | --- 20 | ###**1.swServer函数分析** 21 | ####**swServer_addListener** 22 | 该函数用于在swServer中添加一个需要监听的host及port。函数原型如下: 23 | ```c 24 | // Server.h 438h 25 | int swServer_addListener(swServer *serv, int type, char *host,int port); 26 | ``` 27 | | 参数 | 说明 | 28 | | -------- | ------ | 29 | | swServer *serv|swServer对象| 30 | | int type |创建的socket类型,见枚举**swSocket_type**| 31 | | char* host |监听地址| 32 | | int port |监听端口| 33 | 函数核心源码: 34 | ```c 35 | // Server.c 900~943h 36 | swListenList_node *listen_host = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swListenList_node)); 37 | 38 | listen_host->type = type; 39 | listen_host->port = port; 40 | listen_host->sock = 0; 41 | listen_host->ssl = 0; 42 | 43 | bzero(listen_host->host, SW_HOST_MAXSIZE); 44 | strncpy(listen_host->host, host, SW_HOST_MAXSIZE); 45 | LL_APPEND(serv->listen_list, listen_host); 46 | 47 | //UDP需要提前创建好 48 | if (type == SW_SOCK_UDP || type == SW_SOCK_UDP6 || type == SW_SOCK_UNIX_DGRAM) 49 | { 50 | int sock = swSocket_listen(type, listen_host->host, port, serv->backlog); 51 | if (sock < 0) 52 | { 53 | return SW_ERR; 54 | } 55 | //设置UDP缓存区尺寸,高并发UDP服务器必须设置 56 | int bufsize = serv->udp_sock_buffer_size; 57 | setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); 58 | setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); 59 | 60 | listen_host->sock = sock; 61 | serv->have_udp_sock = 1; 62 | } 63 | else 64 | { 65 | if (type & SW_SOCK_SSL) 66 | { 67 | type = type & (~SW_SOCK_SSL); 68 | listen_host->type = type; 69 | listen_host->ssl = 1; 70 | } 71 | if (type != SW_SOCK_UNIX_STREAM && port <= 0) 72 | { 73 | swError("listen port must greater than 0."); 74 | return SW_ERR; 75 | } 76 | serv->have_tcp_sock = 1; 77 | } 78 | return SW_OK; 79 | ``` 80 | 源码解释: 81 | 在**SwooleG**的共享内存池中创建一个**swListenList_node**,并设置type等相关参数,并将该node添加进**swServer**的**listen_list**。随后,判定type类型。如果是UDP类型的socket,需要直接调用swSocket_listen进行监听,并根据**swServer**的**udp_sock_buffer_size**设置socket的缓存区大小;如果是TCP类型的socket,只针对两种特别的type类型做判定(SSL类型要设置ssl开关,非Unix Sock类型要保证监听端口大于0)。 82 | 83 | ####**swServer_listen** 84 | 该函数用于开始监听swServer中全部的TCP类型的socket。函数原型如下: 85 | ```c 86 | // Server.h 440h 87 | int swServer_listen(swServer *serv, swReactor *reactor); 88 | ``` 89 | | 参数 | 说明 | 90 | | -------- | ------ | 91 | | swServer *serv|swServer对象| 92 | | swReactor *reactor |Reactor对象,监听实际的Listen事件| 93 | 函数核心源码: 94 | ```c 95 | // Server.c 949-998h 96 | LL_FOREACH(serv->listen_list, listen_host) 97 | { 98 | //UDP 99 | if (listen_host->type == SW_SOCK_UDP || listen_host->type == SW_SOCK_UDP6 || listen_host->type == SW_SOCK_UNIX_DGRAM) 100 | { 101 | continue; 102 | } 103 | 104 | //TCP 105 | sock = swSocket_listen(listen_host->type, listen_host->host, listen_host->port, serv->backlog); 106 | if (sock < 0) 107 | { 108 | LL_DELETE(serv->listen_list, listen_host); 109 | return SW_ERR; 110 | } 111 | 112 | if (reactor!=NULL) 113 | { 114 | reactor->add(reactor, sock, SW_FD_LISTEN); 115 | } 116 | 117 | #ifdef TCP_DEFER_ACCEPT 118 | int sockopt; 119 | if (serv->tcp_defer_accept > 0) 120 | { 121 | sockopt = serv->tcp_defer_accept; 122 | setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &sockopt, sizeof(sockopt)); 123 | } 124 | #endif 125 | listen_host->sock = sock; 126 | //将server socket也放置到connection_list中 127 | serv->connection_list[sock].fd = sock; 128 | serv->connection_list[sock].addr.sin_port = listen_host->port; 129 | //save listen_host object 130 | serv->connection_list[sock].object = listen_host; 131 | } 132 | //将最后一个fd作为minfd和maxfd 133 | if (sock >= 0) 134 | { 135 | swServer_set_minfd(serv, sock); 136 | swServer_set_maxfd(serv, sock); 137 | } 138 | ``` 139 | 源码解释: 140 | 遍历**listen_list**列表,对所有TCP类型的socket,调用**swSocket_listen**函数启动监听,并将socket添加到reactor中。如果设置了**TCP_DEFER_ACCEPT**属性,则设置相应的socket option。最后,将监听的socket加入swServer的**connection_list**,并设置swServer的minfd和maxfd为最后一个待监听的server socket。 141 | 142 | ####**swServer_addTimer** 143 | 该函数用于在swServer中添加一个定时器。函数原型如下: 144 | ```c 145 | // Server.h 443h 146 | int swServer_addTimer(swServer *serv, int interval); 147 | ``` 148 | | 参数 | 说明 | 149 | | -------- | ------ | 150 | | swServer *serv|swServer对象| 151 | | int interval |Timer的时间间隔| 152 | 函数核心源码: 153 | ```c 154 | // Server.c 263-293h 155 | if (serv->onTimer == NULL) 156 | { 157 | swWarn("onTimer is null. Can not use timer."); 158 | return SW_ERR; 159 | } 160 | 161 | //timer no init 162 | if (SwooleG.timer.fd == 0) 163 | { 164 | if (swTimer_create(&SwooleG.timer, interval, SwooleG.use_timer_pipe) < 0) 165 | { 166 | return SW_ERR; 167 | } 168 | 169 | if (swIsMaster()) 170 | { 171 | serv->connection_list[SW_SERVER_TIMER_FD_INDEX].fd = SwooleG.timer.fd; 172 | } 173 | 174 | if (SwooleG.use_timer_pipe) 175 | { 176 | SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_TIMER, swTimer_event_handler); 177 | SwooleG.main_reactor->add(SwooleG.main_reactor, SwooleG.timer.fd, SW_FD_TIMER); 178 | } 179 | 180 | SwooleG.timer.onTimer = swServer_onTimer; 181 | } 182 | return swTimer_add(&SwooleG.timer, interval); 183 | ``` 184 | 源码解释: 185 | 首先判定是否有onTimer回调,如果没有则返回一个Error。随后,如果没有初始化timer计时器,则调用**swTimer_create**函数创建计时器。如果当前进程是master进程,将timer的fd添加到**connection_list**中。如果timer指定使用了pipe管道,则将timer的fd添加到**SwooleG**的**main_reactor**中。最后,调用**swTimer_add**添加timer。 186 | 187 | ####**swServer_tcp_send** 188 | 该函数用于发送TCP数据。函数原型如下: 189 | ```c 190 | // Server.h 443h 191 | int swServer_tcp_send(swServer *serv, int fd, void *data, int length); 192 | ``` 193 | | 参数 | 说明 | 194 | | -------- | ------ | 195 | | swServer *serv|swServer对象| 196 | | int fd |发送的socket描述符| 197 | | void *data |需要发送的数据| 198 | | int length |数据长度| 199 | 函数核心源码: 200 | ```c 201 | // Server.c 788-858h 202 | swSendData _send; 203 | swFactory *factory = &(serv->factory); 204 | 205 | #ifndef SW_WORKER_SEND_CHUNK 206 | /** 207 | * More than the output buffer 208 | */ 209 | if (length >= serv->buffer_output_size) 210 | { 211 | swWarn("More than the output buffer size[%d], please use the sendfile.", serv->buffer_output_size); 212 | return SW_ERR; 213 | } 214 | else 215 | { 216 | _send.info.fd = fd; 217 | _send.info.type = SW_EVENT_TCP; 218 | _send.data = data; 219 | 220 | if (length >= SW_BUFFER_SIZE) 221 | { 222 | _send.length = length; 223 | } 224 | else 225 | { 226 | _send.info.len = length; 227 | _send.length = 0; 228 | } 229 | return factory->finish(factory, &_send); 230 | } 231 | #else 232 | char buffer[SW_BUFFER_SIZE]; 233 | int trunk_num = (length / SW_BUFFER_SIZE) + 1; 234 | int send_n = 0, i, ret; 235 | 236 | swConnection *conn = swServer_connection_get(serv, fd); 237 | if (conn == NULL || conn->active == 0) 238 | { 239 | swWarn("Connection[%d] has been closed.", fd); 240 | return SW_ERR; 241 | } 242 | 243 | for (i = 0; i < trunk_num; i++) 244 | { 245 | //last chunk 246 | if (i == (trunk_num - 1)) 247 | { 248 | send_n = length % SW_BUFFER_SIZE; 249 | if (send_n == 0) 250 | break; 251 | } 252 | else 253 | { 254 | send_n = SW_BUFFER_SIZE; 255 | } 256 | memcpy(buffer, data + SW_BUFFER_SIZE * i, send_n); 257 | _send.info.len = send_n; 258 | ret = factory->finish(factory, &_send); 259 | 260 | #ifdef SW_WORKER_SENDTO_YIELD 261 | if ((i % SW_WORKER_SENDTO_YIELD) == (SW_WORKER_SENDTO_YIELD - 1)) 262 | { 263 | swYield(); 264 | } 265 | #endif 266 | } 267 | return ret; 268 | #endif 269 | ``` 270 | 源码解释: 271 | 如果没有定义**SW_WORKER_SEND_CHUNK**宏,执行如下操作: 272 | 如果数据长度大于swServer的输出缓存大小,则报错。否则,设置swSendData的相关属性,调用swServer中的factory的finish函数将数据发出。 273 | 如果定义了**SW_WORKER_SEND_CHUNK**宏,执行如下操作: 274 | 首先根据数据长度length计算出需要多少个trunk。随后,获取fd对应的**swConnecton**,如果找不到connection或者connection已经关闭,则报错。随后,将数据划分为一个个trunk的长度,放进swSendData中后调用swServer中的factory的finish函数将数据发出。 275 | 276 | 277 | ####**swServer_reload** 278 | 该函数用于通知Manager进程重启全部的Worker进程。函数原型如下: 279 | ```c 280 | // Server.h 443h 281 | int swServer_reload(swServer *serv); 282 | ``` 283 | | 参数 | 说明 | 284 | | -------- | ------ | 285 | | swServer *serv|swServer对象| 286 | 函数核心源码: 287 | ```c 288 | // Server.c 1009-1017h 289 | int manager_pid = swServer_get_manager_pid(serv); 290 | if (manager_pid > 0) 291 | { 292 | return kill(manager_pid, SIGUSR1); 293 | } 294 | return SW_ERR; 295 | ``` 296 | 源码解释: 297 | 通过kill函数向Manager进程发送SIGUSR1信号,该信号的行为是杀死并重启全部的Worker。 298 | 299 | 剩下的swServer的操作函数较为简单,在此不再贴出源码进行分析。 300 | 301 | ###**2.Server相关结构体分析** 302 | 下面分析一些声明在Server.h中的结构体。 303 | 304 | ####**swPackage** 305 | 该结构体已被swEventData替代。 306 | 307 | ####**swPackage_task** 308 | 声明: 309 | ```c 310 | // Server.h 420-424h 311 | typedef struct 312 | { 313 | int length; 314 | char tmpfile[sizeof(SW_TASK_TMP_FILE)]; 315 | } swPackage_task; 316 | ``` 317 | | 成员 | 说明 | 318 | | -------- | ------ | 319 | | int length|数据长度| 320 | | char tmpfile[sizeof(SW_TASK_TMP_FILE)]|临时文件的文件名| 321 | 说明: 322 | **swPackage_task**用于封装内容较大的task包(超过8K),tmpfile指向一个由**mkstemp**函数(如果开启了**HAVE_MKOSTEMP**选项,则为**mkostemp**函数)创造的临时文件,所有的数据会被暂时存放在这个文件里。 323 | 324 | ####**swPackage_response** 325 | 声明: 326 | ```c 327 | // Server.h 426-430h 328 | typedef struct 329 | { 330 | int length; 331 | int worker_id; 332 | } swPackage_response; 333 | ``` 334 | | 成员 | 说明 | 335 | | -------- | ------ | 336 | | int length|数据长度| 337 | | int worker_id|用于接收该应答的Worker ID| 338 | 说明: 339 | swPackage_response结构体用于Factory回应Reactor,主要作用是通知Reactor响应数据的实际长度以及是由哪个worker处理的,然后会根据是否为Big Response决定是否从worker的共享内存中读取数据。(参考**ReactorThread**的**swReactorThread_onPipeReceive**函数以及**FactoryProcess**的**swFactoryProcess_finish**函数) 340 | -------------------------------------------------------------------------------- /08.Reactor模块-epoll.md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.5-stable 5 | ------------- 6 | Reactor模块可以说是Swoole中最核心的模块之一,正是这些reactor模型为swoole提供了异步操作的基础。Swoole中根据不同的内核函数,提供了四种Reactor封装,ReactorEpoll,ReactorKqueue,ReactorPoll和ReactorSelect。同时,Swoole通过结构体swReactor封装了对于reactor的操作函数和基本属性。本章,我将分析swReactor以及四种Reactor模型中的ReactorEpoll,并回顾一下epoll的相关知识。 7 | 8 | 一.swReactor 9 | swReactor结构体声明在swoole.h的**698 – 721**行,其声明如下: 10 | ```c 11 | struct swReactor_s 12 | { 13 | void *object; 14 | void *ptr; //reserve 15 | uint32_t event_num; 16 | uint32_t max_event_num; 17 | uint16_t id; //Reactor ID 18 | uint16_t flag; //flag 19 | char running; 20 | 21 | swReactor_handle handle[SW_MAX_FDTYPE]; //默认事件 22 | swReactor_handle write_handle[SW_MAX_FDTYPE]; //扩展事件1(一般为写事件) 23 | swReactor_handle error_handle[SW_MAX_FDTYPE]; //扩展事件2(一般为错误事件,如socket关闭) 24 | 25 | int (*add)(swReactor *, int fd, int fdtype); 26 | int (*set)(swReactor *, int fd, int fdtype); 27 | int (*del)(swReactor *, int fd); 28 | int (*wait)(swReactor *, struct timeval *); 29 | void (*free)(swReactor *); 30 | int (*setHandle)(swReactor *, int fdtype, swReactor_handle); 31 | 32 | void (*onTimeout)(swReactor *); //发生超时时 33 | void (*onFinish)(swReactor *); //完成一次轮询 34 | }; 35 | typedef struct swReactor_s swReactor; // swoole.h 354行 36 | typedef int (*swReactor_handle)(swReactor *reactor, swEvent*event); // swoole.h 355行 37 | ``` 38 | 属性分析:object存放实际reactor模型的内存地址,reserve , event_num存放现有的事件数目,max_event_num存放允许持有的最大事件数目,id用于存放对应reactor的id,flag为标记位,running用于标记该reactor是否正在运行,接下来三个数组用于存放需要监听的事件的响应回调函数,余下的都是对应reactor的操作函数,这些函数将在具体的Reactor模型中被实现并赋值。 39 | 对应Reactor的部分通用的操作函数声明在swoole.h文件中**827 – 873**行。首先是一个枚举类型SW_EVENTS,用于指定对应的事件id,声明如下: 40 | ```c 41 | enum SW_EVENTS 42 | { 43 | SW_EVENT_DEAULT = 256, 44 | SW_EVENT_READ = 1u << 9, 45 | SW_EVENT_WRITE = 1u << 10, 46 | SW_EVENT_ERROR = 1u << 11, 47 | }; 48 | ``` 49 | SW_EVENTS中指定了四种事件类型:DEFAULT代表默认事件,READ和WRITE分别代表读写事件,ERROR代表错误。 50 | 接着是四个操作事件类型的函数: 51 | ```c 52 | // 过滤fdtype中的读、写、错误事件标记 53 | static sw_inline int swReactor_fdtype(int fdtype) 54 | { 55 | return fdtype & (~SW_EVENT_READ) & (~SW_EVENT_WRITE) & (~SW_EVENT_ERROR); 56 | } 57 | // 判定是否为读事件和其他swFd_type类型的监听 58 | static sw_inline int swReactor_event_read(int fdtype) 59 | { 60 | return (fdtype < SW_EVENT_DEAULT) || (fdtype & SW_EVENT_READ); 61 | } 62 | // 判定是否为事件监听 63 | static sw_inline int swReactor_event_write(int fdtype) 64 | { 65 | return fdtype & SW_EVENT_WRITE; 66 | } 67 | // 判定是否为错误事件监听 68 | static sw_inline int swReactor_event_error(int fdtype) 69 | { 70 | return fdtype & SW_EVENT_ERROR; 71 | } 72 | ``` 73 | 接着是四个个通用操作函数,函数声明如下: 74 | ```c 75 | int swReactor_auto(swReactor *reactor, int max_event); 76 | int swReactor_receive(swReactor *reactor, swEvent *event); 77 | int swReactor_setHandle(swReactor *, int, swReactor_handle); 78 | swReactor_handle swReactor_getHandle(swReactor *reactor, int event_type, int fdtype); 79 | ``` 80 | 这四个函数分别用于自动创建可用类型的reactor模型、从reactor接收到的swEvent中读取数据、设置Reactor的回调函数、获得Reactor的回调函数。函数的具体声明在ReactorBase.c文件中。 81 | 这里补充说明swEvent结构体,其声明在swoole.h文件的**311 – 317** 行,声明如下: 82 | ```c 83 | typedef struct _swEvent 84 | { 85 | int fd; // 描述符 86 | int16_t from_id; // 来自哪个reactor 87 | uint8_t type; // 描述符类型 88 | void *object; // 数据域 89 | } swEvent; 90 | ``` 91 | 在ReactorBase.c文件中,具体实现了Reactor的四个通用操作函数。第一个函数是swReactor_auto,其核心代码如下: 92 | int swReactor_auto(swReactor *reactor, int max_event) // 第二个参数设置允许监听的最大事件数 93 | ```c 94 | int ret; 95 | #ifdef HAVE_EPOLL 96 | ret = swReactorEpoll_create(reactor, max_event); 97 | #elif defined(HAVE_KQUEUE) 98 | ret = swReactorKqueue_create(reactor, max_event); 99 | #elif defined(SW_MAINREACTOR_USE_POLL) 100 | ret = swReactorPoll_create(reactor, max_event); 101 | #else 102 | ret = swReactorSelect_create(SwooleG.main_reactor) 103 | #endif 104 | return ret; 105 | ``` 106 | 源码解释:根据环境编译中定义的参数决定使用哪种类型的reactor模型,主线程默认使用poll模型。 107 | 108 | 这里需要提前介绍一个枚举类型swFd_type。枚举类型swFd_type指定了描述符fd的一些特殊类型,这些类型主要用于reactor直接辨识某个fd类型的回调函数(同类型的fd共用一个回调函数)。该枚举类型声明在swoole.h文件中的**165 – 179** 行,如下: 109 | ```c 110 | enum swFd_type 111 | { 112 | SW_FD_TCP = 0, //tcp socket 113 | SW_FD_LISTEN = 1, //server socket 114 | SW_FD_CLOSE = 2, //socket closed 115 | SW_FD_ERROR = 3, //socket error 116 | SW_FD_UDP = 4, //udp socket 117 | SW_FD_PIPE = 5, //pipe 118 | SW_FD_WRITE = 7, //fd can write 119 | SW_FD_TIMER = 8, //timer fd 120 | SW_FD_AIO = 9, //linux native aio 121 | SW_FD_SEND_TO_CLIENT = 10, //sendto client 122 | SW_FD_SIGNAL = 11, //signalfd 123 | SW_FD_DNS_RESOLVER = 12, //dns resolver 124 | }; 125 | ``` 126 | swReactor_getHandle函数和swReactor_setHandle函数分别用于获取和设置相应的回调函数。swReactor_getHandle函数的核心代码如下: 127 | ```c 128 | if (event_type == SW_EVENT_WRITE) 129 | { 130 | //默认可写回调函数SW_FD_WRITE 131 | return (reactor->write_handle[fdtype] != NULL) ? reactor->write_handle[fdtype] : reactor->handle[SW_FD_WRITE]; 132 | } 133 | if (event_type == SW_EVENT_ERROR) 134 | { 135 | //默认关闭回调函数SW_FD_CLOSE 136 | return (reactor->error_handle[fdtype] != NULL) ? reactor->error_handle[fdtype] : reactor->handle[SW_FD_CLOSE]; 137 | } 138 | return reactor->handle[fdtype]; 139 | ``` 140 | 源码解释:首先判定事件类型是否为写事件,如果是,判定参数fdtype指定的回调是否存在,如果不存在,默认返回SW_FD_WRITE回调,否则返回fdtype对应的回调;然后判定事件类型是否为异常事件,如果是,判定参数fdtype指定的回调是否存在,如果不存在,默认返回SW_FD_CLOSE回调,否则返回fdtype对应的回调。最后,如果事件类型为其他类型,则直接返回fdtype对应的回调。 141 | swReactor_setHandle函数的核心代码如下: 142 | ```c 143 | int fdtype = swReactor_fdtype(_fdtype); 144 | if (fdtype >= SW_MAX_FDTYPE) 145 | { 146 | swWarn("fdtype > SW_MAX_FDTYPE[%d]", SW_MAX_FDTYPE); 147 | return SW_ERR; 148 | } 149 | else 150 | { 151 | if (swReactor_event_read(_fdtype)) 152 | { 153 | reactor->handle[fdtype] = handle; 154 | } 155 | else if (swReactor_event_write(_fdtype)) 156 | { 157 | reactor->write_handle[fdtype] = handle; 158 | } 159 | else if (swReactor_event_error(_fdtype)) 160 | { 161 | reactor->error_handle[fdtype] = handle; 162 | } 163 | else 164 | { 165 | swWarn("unknow fdtype"); 166 | return SW_ERR; 167 | } 168 | } 169 | ``` 170 | 源码解释:调用swReactor_fdtype函数去掉_fdtype参数中的SW_EVENTS类型变量,获取原始的swFd_type类型变量fdtype。如果fdtype超过了swoole规定的范围,则返回SW_ERR;否则,使用swReactor_event_*系列函数判定_fdtype的实际类型,根据不同的类型将回调函数存入reactor中对应的回调函数数组中。 171 | swReactor_receive函数只是简单调用swRead方法从event的fd中读取了数据,不再赘述。 172 | 173 | 二.ReactorEpoll 174 | 首先回顾一下epoll的相关知识(在群里很多用PHP做开发的小伙伴似乎根本不了解什么是epoll什么是异步I/O……)epoll是Linux内核提供的一个多路复用I/O模型,它提供和poll函数一样的功能:监控多个文件描述符是否处于I/O就绪状态(可读、可写)。这就是异步最核心的表现:程序不是主动等待一个描述符可以操作,而是当描述符可操作时由系统提醒程序可以操作了,程序在被提醒前可以去做其他的事情(这里的程序、描述符、系统可以更换为其他东西) 175 | Linux提供了三个主要的系统调用:**epoll_create**,**epoll_ctl**,**epoll_wait**。 176 | epoll_create用于创建一个epoll实例并返回这个实例的文件描述符。epoll_ctl用于将一个需要监控的文件描述符在epoll中注册对应的监听事件,该函数也可用于更改一个已注册描述符的监听事件。epoll_wait函数用于等待监听的描述符的I/O事件,如果所有描述符都没有就绪,该函数会阻塞直到有至少一个描述符进入就绪状态。(该段描述翻译自 Linux 命令:man epoll ) 177 | 上周参与腾讯面试时就被问到了这样的问题:请说明一下epoll函数的水平触发(Level-triggered)和边缘触发(edge-triggered)两种模式的区别。结果我逗比的没答出来……在此重新复习一下这个知识……水平触发和边缘触发是epoll的两种模式,它们的区别在于:水平触发模式下,当一个fd就绪之后,如果没有对该fd进行操作,则系统会继续发出就绪通知直到该fd被操作;边缘触发模式下,当一个fd就绪后,系统仅会发出一次就绪通知。 178 | (相关链接:http://baike.baidu.com/view/1385104.htm http://yaocoder.blog.51cto.com/2668309/888374 ) 179 | 180 | Swoole中,所有swReactorEpoll的相关定义都在ReactorEpoll.c中实现。首先说明两个epoll相关的宏:EPOLLRDHUP和EPOLLONESHOT。 181 | EPOLLRDHUP代表的意义是对端断开连接,这个宏是用于弥补epoll在处理对端断开连接时可能会出现的一处Bug。 182 | EPOLLONESHOT用于标记epoll对于每个socket仅监听一次事件,如果需要再次监听这个socket,需要再次将该socket加入epoll的监听队列中。 183 | 184 | ReactorEpoll首先声明了一个结构体swFd用于封装一个描述符类型,其声明如下: 185 | ```c 186 | #pragma pack(4) 187 | typedef struct _swFd 188 | { 189 | uint32_t fd; 190 | uint32_t fdtype; 191 | } swFd; 192 | #pragma pack() 193 | ``` 194 | 其中, #pragma pack(4)的含义是指定结构体内的成员变量按照4字节对齐(关于字节对齐请参考http://baike.baidu.com/view/2317161.htm 195 | http://www.cppblog.com/tauruser/archive/2007/02/28/19049.html ) 196 | 该结构体存放两个变量,一个变量为文件描述符,另一个变量为描述符类型。 197 | 同样的,Swoole也封装了一个结构体swReactorEpoll用于存放epoll的描述符以及监听的事件列表。该结构体的声明如下: 198 | ```c 199 | struct swReactorEpoll_s 200 | { 201 | int epfd; 202 | struct epoll_event *events; 203 | }; 204 | ``` 205 | 创建一个ReactorEpoll的函数声明在swoole.h文件中的**852**行,其声明如下: 206 | ```c 207 | int swReactorPoll_create(swReactor *reactor, int max_event_num); 208 | ``` 209 | 该函数的核心源码如下: 210 | ```c 211 | swReactorEpoll *reactor_object = sw_malloc(sizeof(swReactorEpoll)); 212 | if (reactor_object == NULL) 213 | { 214 | swWarn("malloc[0] failed."); 215 | return SW_ERR; 216 | } 217 | bzero(reactor_object, sizeof(swReactorEpoll)); 218 | reactor->object = reactor_object; 219 | reactor->max_event_num = max_event_num; 220 | 221 | reactor_object->events = sw_calloc(max_event_num, sizeof(struct epoll_event)); 222 | 223 | if (reactor_object->events == NULL) 224 | { 225 | swWarn("malloc[1] failed."); 226 | return SW_ERR; 227 | } 228 | //epoll create 229 | reactor_object->epfd = epoll_create(512); 230 | if (reactor_object->epfd < 0) 231 | { 232 | swWarn("epoll_create failed. Error: %s[%d]", strerror(errno), errno); 233 | return SW_ERR; 234 | } 235 | ``` 236 | 源码解释:申请一个swReactorEpoll结构体并初始化。设置reactor的object和max_event_num参数。调用epoll_create函数创建一个epoll实例,参数512指定最大的监听fd数量。 237 | swReactorEpoll共有5个操作函数,其声明如下: 238 | ```c 239 | static int swReactorEpoll_add(swReactor *reactor, int fd, int fdtype); 240 | static int swReactorEpoll_set(swReactor *reactor, int fd, int fdtype); 241 | static int swReactorEpoll_del(swReactor *reactor, int fd); 242 | static int swReactorEpoll_wait(swReactor *reactor, struct timeval *timeo); 243 | static void swReactorEpoll_free(swReactor *reactor); 244 | ``` 245 | 这5个函数基于epoll函数家族以及close函数实现,用于对epoll实例的添加fd、设置fd监听事件、移除fd、等待fd事件以及释放epoll实例。同时Swoole还声明了一个内联函数swReactorEpoll_event_set用于将自定义的SW_EVENTS类型转变为标准的epoll事件类型(EPOLLET、EPOLLIN、EPOLLOUT、EPOLLRDHUP)。下面将一一分析这些函数。 246 | 1. swReactorEpoll_add 247 | 核心源码: 248 | ```c 249 | swReactorEpoll *object = reactor->object; 250 | struct epoll_event e; 251 | swFd fd_; 252 | int ret; 253 | bzero(&e, sizeof(struct epoll_event)); 254 | 255 | fd_.fd = fd; 256 | fd_.fdtype = swReactor_fdtype(fdtype); 257 | e.events = swReactorEpoll_event_set(fdtype); 258 | 259 | memcpy(&(e.data.u64), &fd_, sizeof(fd_)); 260 | ret = epoll_ctl(object->epfd, EPOLL_CTL_ADD, fd, &e); 261 | if (ret < 0) 262 | { 263 | swWarn("add event failed. Error: %s[%d]", strerror(errno), errno); 264 | return SW_ERR; 265 | } 266 | swTraceLog(SW_TRACE_EVENT, "add event[reactor_id=%d|fd=%d]", reactor->id, fd); 267 | reactor->event_num++; 268 | ``` 269 | 源码解释:获取reactor中的swReactorEpoll结构体,创建一个epoll_event结构体e和一个swFd结构体fd_,初始化fd_参数并将该对象存放到epoll_event的data域中的u64变量中。调用epoll_ctl添加对该fd的监听,并将reactor的event_num计数加一。 270 | 2. swReactorEpoll_set 271 | 核心代码与swReactorEpoll_add基本一致,唯一不同在于epoll_ctl函数的第二个参数由EPOLL_CTL_ADD变成EPOLL_CTL_MOD,代表设置fd的监听事件(而不是新增)。 272 | 3. swReactorEpoll_del 273 | 核心源码: 274 | ```c 275 | swReactorEpoll *object = reactor->object; 276 | int ret; 277 | 278 | if (fd <= 0) 279 | { 280 | return SW_ERR; 281 | } 282 | 283 | ret = epoll_ctl(object->epfd, EPOLL_CTL_DEL, fd, NULL); 284 | if (ret < 0) 285 | { 286 | swWarn("epoll remove fd[=%d] failed. Error: %s[%d]", fd, strerror(errno), errno); 287 | return SW_ERR; 288 | } 289 | //close时会自动从epoll事件中移除 290 | //swoole中未使用dup 291 | ret = close(fd); 292 | if (ret >= 0) 293 | { 294 | (reactor->event_num <= 0) ? reactor->event_num = 0 : reactor->event_num--; 295 | } 296 | ``` 297 | 源码解释:获取reactor中的swReactorEpoll结构体,创建一个epoll_event结构体e,设置e的data域的fd变量为指定需要删除的fd。调用epoll_ctl并指定操作位EPOLL_CTL_DEL用监听队列中移除对应的监听,并close对应的fd。如果移除成功,更改reactor的event_num计数。 298 | 4. swReactorEpoll_wait 299 | 由于该函数较长且比较重要,在此将分段分析该函数。 300 | ``` 301 | swEvent ev; 302 | swReactorEpoll *object = reactor->object; 303 | swReactor_handle handle; 304 | int i, n, ret, usec; 305 | 306 | int reactor_id = reactor->id; 307 | int epoll_fd = object->epfd; 308 | int max_event_num = reactor->max_event_num; 309 | struct epoll_event *events = object->events; 310 | 311 | if (timeo == NULL) 312 | { 313 | usec = SW_MAX_UINT; 314 | } 315 | else 316 | { 317 | usec = timeo->tv_sec * 1000 + timeo->tv_usec / 1000; 318 | } 319 | ``` 320 | 源码解释:该段源码声明了所需使用的全部临时变量。ev是相应事件数据的封装,是回调函数handle的第二个参数。object为swReactorEpoll结构体变量。n为每一次epoll_wait响应后返回的当前处于就绪状态的fd的数量,usec为epoll_wait的timeout超时时间,由swReactorEpoll_wait函数的第二个参数struct timeval *timeo指定。接下来的几个参数,reactor_id为swReactor的标记,epoll_fd为epoll实例的描述符,max_event_num为允许监听的最大事件数量,events用于存放epoll函数发现的处于就绪状态的事件。 321 | ```c 322 | while (SwooleG.running > 0) 323 | ``` 324 | 源码解释:这是一个核心循环,之所以单独提出来是因为SwooleG变量非常重要。该变量声明在Server.c文件中的**53**行,并在swoole.h的**1081**行中通过extern关键字修饰使之可以被其他关联文件访问。该结构体中主要存放了整个swoole运行中需要的一些全局变量,在这里使用running变量用于标记swoole主循环是否正在执行。 325 | ```c 326 | n = epoll_wait(epoll_fd, events, max_event_num, usec); 327 | if (n < 0) 328 | { 329 | if (swReactor_error(reactor) < 0) 330 | { 331 | swWarn("[Reactor#%d] epoll_wait failed. Error: %s[%d]", reactor_id, strerror(errno), errno); 332 | return SW_ERR; 333 | } 334 | else 335 | { 336 | continue; 337 | } 338 | } 339 | else if (n == 0) 340 | { 341 | if (reactor->onTimeout != NULL) 342 | { 343 | reactor->onTimeout(reactor); 344 | } 345 | continue; 346 | } 347 | ``` 348 | 源码解释:调用epoll_wait函数获取已经处于就绪状态的fd的集合,该集合存放在events结构体数组中,其数目为返回值n。如果没有任何描述符处于就绪状态,该函数会阻塞直到有描述符就绪。如果在usec毫秒后仍没有描述符就绪,则返回0。 349 | ```c 350 | for (i = 0; i < n; i++) 351 | { 352 | ev.fd = events[i].data.u64; 353 | ev.from_id = reactor_id; 354 | ev.type = events[i].data.u64 >> 32; 355 | 356 | //read 357 | if (events[i].events & EPOLLIN) 358 | { 359 | //read 360 | handle = swReactor_getHandle(reactor, SW_EVENT_READ, ev.type); 361 | ret = handle(reactor, &ev); 362 | if (ret < 0) 363 | { 364 | swWarn("[Reactor#%d] epoll [EPOLLIN] handle failed. fd=%d. Error: %s[%d]", reactor_id, ev.fd, strerror(errno), errno); 365 | } 366 | } 367 | //write, ev.fd == 0, connection is closed. 368 | if ((events[i].events & EPOLLOUT) && ev.fd > 0) 369 | { 370 | handle = swReactor_getHandle(reactor, SW_EVENT_WRITE, ev.type); 371 | ret = handle(reactor, &ev); 372 | if (ret < 0) 373 | { 374 | swWarn("[Reactor#%d] epoll [EPOLLOUT] handle failed. fd=%d. Error: %s[%d]", reactor_id, ev.fd, strerror(errno), errno); 375 | } 376 | } 377 | //error 378 | #ifndef NO_EPOLLRDHUP 379 | if ((events[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) && ev.fd > 0) 380 | #else 381 | if ((events[i].events & (EPOLLERR | EPOLLHUP)) && ev.fd > 0) 382 | #endif 383 | { 384 | handle = swReactor_getHandle(reactor, SW_EVENT_ERROR, ev.type); 385 | ret = handle(reactor, &ev); 386 | if (ret < 0) 387 | { 388 | swWarn("[Reactor#%d] epoll [EPOLLERR] handle failed. fd=%d. Error: %s[%d]", reactor_id, ev.fd, strerror(errno), errno); 389 | } 390 | } 391 | } 392 | ``` 393 | 源码解释:这是核心的事件处理逻辑了。循环遍历n个待处理事件,前三行设置swEvent的对应参数(这里需要注意,data域中的u64变量是一个uint64_t类型,该变量被写入了一个swFd结构体,低位32位存放的是uint32_t类型的fd,高位32位存放的是uint32_t类型的fdtype,该fdtype的取值为枚举类型swFd_type),然后根据events的不同类型进入不同的处理逻辑,如下: 394 | a. EPOLLIN 读事件,根据swEvent中的type类型(fdtype)获取对应的读操作的回调函数,通过该回调将swEvent类型发出。 395 | b. EPOLLOUT 写事件,根据swEvent中的type类型(fdtype)获取对应的写操作的回调函数,通过该回调将swEvent类型发出。 396 | c. 异常事件,根据swEvent中的type类型(fdtype)获取对应的异常操作的回调函数,通过该回调将swEvent类型发出。 397 | 5. swReactorEpoll_free 398 | 调用close函数关闭epoll实例的描述符,并释放申请的内存空间 399 | 400 | 至此,swReactorEpoll分析已经结束。下一章将分析剩下的三种类型poll,select和kqueue。 401 | -------------------------------------------------------------------------------- /09.Factory模块(上).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.5-stable 5 | ------------- 6 | Factory这个命名让我一度认为这是一个工厂模型……这个工厂实际上并不负责生产实例,而是根据类型的不同执行两项任务:Factory实现的功能是一个任务中心,一个task请求进入Factory,会进过dispatch分配、onTask处理、onFinish交付结果一系列流程;FactoryProcess用于管理manager和worker进程,也有对单独的writer线程的管理。 7 | (PS:Swoole源码中有FactoryThread模块,该模块是一个多线程模型,根据开发者Rango韩少的解释,因为PHP不支持多线程,所以无法使用这个模块,因此该模块被废弃了。而实际上,FactoryThread比FactoryProcess要更简洁……) 8 | 9 | Factory模块的相关声明在Server.h头文件中。首先是一个在Factory模块中被用到的结构体swDispatchData,该结构体声明在Server.h的**145 – 149** 行,声明如下: 10 | ```c 11 | typedef struct 12 | { 13 | long target_worker_id; 14 | swEventData data; 15 | } swDispatchData; 16 | ``` 17 | swDispatchData存放了一个目标worker进程的id和一条数据,该结构体用于传递数据给task进程进行处理。 18 | Swoole中用swFactory结构体封装了Factory模块的相关操作,其声明在Server.h文件中的**151 – 168** 行,其声明如下: 19 | ```c 20 | struct _swFactory 21 | { 22 | void *object; 23 | void *ptr; //server object 24 | int last_from_id; 25 | 26 | swReactor *reactor; //reserve for reactor 27 | 28 | int (*start)(struct _swFactory *); 29 | int (*shutdown)(struct _swFactory *); 30 | int (*dispatch)(struct _swFactory *, swDispatchData *); 31 | int (*finish)(struct _swFactory *, swSendData *); 32 | int (*notify)(struct _swFactory *, swDataHead *); //send a event notify 33 | int (*end)(struct _swFactory *, swDataHead *); 34 | 35 | int (*onTask)(struct _swFactory *, swEventData *task); //worker function.get a task,goto to work 36 | int (*onFinish)(struct _swFactory *, swSendData *result); //factory worker finish.callback 37 | }; 38 | ``` 39 | 其中object用于存放一个具体的Factory类型(FactoryProcess or FactoryThread),last_from_id存放了最近一个通过该Factory发送消息的reactor的id。 40 | swFactory的全部操作函数都声明在Server.h的**176 – 183**行,声明如下: 41 | ```c 42 | int swFactory_create(swFactory *factory); 43 | int swFactory_start(swFactory *factory); 44 | int swFactory_shutdown(swFactory *factory); 45 | int swFactory_dispatch(swFactory *factory, swDispatchData *req); 46 | int swFactory_finish(swFactory *factory, swSendData *_send); 47 | int swFactory_notify(swFactory *factory, swDataHead *event); 48 | int swFactory_end(swFactory *factory, swDataHead *cev); 49 | int swFactory_check_callback(swFactory *factory); 50 | ``` 51 | 这些函数在Factory.c中被定义。其中,start和shutdown函数简单返回SW_OK,create函数仅将传入的factory中的各个函数赋值(其实onTask和onFinish还是外部赋值……),callback函数仅仅检查onTask和onFinish两个函数指针是否为空,剩下的函数都是根据传入的参数调用对应的PHP回调函数(dispatch调用onTask,notify调用onClose和onConnect,end调用onClose并调用swServer_connection_close函数(详细分析见附录)关闭对应的connect连接) 52 | 这里需要分析一下swFactory_finish函数。swFactory_finish是一个通道,它的作用是将task运行结束后的数据发送给对应的Reactor。其核心源码如下: 53 | ```c 54 | //unix dgram 55 | if (resp->info.type == SW_EVENT_UNIX_DGRAM) 56 | { 57 | socklen_t len; 58 | struct sockaddr_un addr_un; 59 | int from_sock = resp->info.from_fd; 60 | 61 | addr_un.sun_family = AF_UNIX; 62 | memcpy(addr_un.sun_path, resp->sun_path, resp->sun_path_len); 63 | len = sizeof(addr_un); 64 | ret = swSendto(from_sock, resp->data, resp->info.len, 0, (struct sockaddr *) &addr_un, len); 65 | goto finish; 66 | } 67 | //UDP pacakge 68 | else if (resp->info.type == SW_EVENT_UDP || resp->info.type == SW_EVENT_UDP6) 69 | { 70 | ret = swServer_udp_send(serv, resp); 71 | goto finish; 72 | } 73 | else 74 | { 75 | resp->length = resp->info.len; 76 | ret = swReactorThread_send(resp); 77 | } 78 | ``` 79 | 源码解释:这里首先判定swDataHead* resp应答中的type类型,如果type类型是Unix sock的数据包类型,则调用swSendto函数(详细分析见附录)发送数据到指定的Reactor;如果type类型是UDP,则调用swServer_udp_send(详细分析见附录)发送数据;否则,调用swReactorThread_send函数发送数据。 80 | 81 | FactoryProcess模块 82 | FactoryProcess模块是Swoole的进程管理模块,是Swoole另一个核心组件。通过该模块,Swoole能有效的调度和管理Master进程和多个Worker进程。 83 | FactoryProcess的结构体swFactoryProcess声明在Server.h文件的**170 – 174**行,声明如下: 84 | ```c 85 | typedef struct _swFactoryProcess 86 | { 87 | swPipe *pipes; 88 | int writer_pti; //current writer id 89 | } swFactoryProcess; 90 | ``` 91 | 第一个变量pipes存放用于进程间通信的管道,从后续实现分析这个Pipe仅用于接收其他进程发来的消息;第二个参数定义了当前writer线程的id,这个writer线程用于进程发送数据。 92 | swFactoryProcess的四个公共操作函数声明在Server.h文件中的**185 – 188**行,其声明如下: 93 | ```c 94 | int swFactoryProcess_create(swFactory *factory, int writer_num, int worker_num); 95 | int swFactoryProcess_start(swFactory *factory); 96 | int swFactoryProcess_shutdown(swFactory *factory); 97 | int swFactoryProcess_end(swFactory *factory, swDataHead *event); 98 | ``` 99 | 这四个函数都在FactoryProcess.c文件中具体定义。其中,swFactoryProcess_create函数用于创建一个swFactoryProcess实例,其核心代码如下: 100 | ```c 101 | swFactoryProcess *object; 102 | swServer *serv = SwooleG.serv; 103 | object = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swFactoryProcess)); 104 | if (object == NULL) 105 | { 106 | swWarn("[Master] malloc[object] failed"); 107 | return SW_ERR; 108 | } 109 | serv->writer_threads = SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->reactor_num * sizeof(swWorkerThread)); 110 | if (serv->writer_threads == NULL) 111 | { 112 | swWarn("[Master] malloc[object->writers] failed"); 113 | return SW_ERR; 114 | } 115 | object->writer_pti = 0; 116 | ``` 117 | 源码解释:从全局变量SwooleG的内存池中申请一个swFactoryProcess的内存,并同样从这个内存池中申请和reactor_num等量的writer线程,并初始化swFactoryProcess的writer_pti为0. 118 | swFactoryProcess_shutdown函数不是关闭一个swFactoryProcess,而是关闭SwooleG全局变量中的swServer实例。其核心源码如下: 119 | ```c 120 | swServer *serv = SwooleG.serv; 121 | int i; 122 | //kill manager process 123 | kill(SwooleGS->manager_pid, SIGTERM); 124 | //kill all child process 125 | for (i = 0; i < serv->worker_num; i++) 126 | { 127 | swTrace("[Main]kill worker processor"); 128 | kill(serv->workers[i].pid, SIGTERM); 129 | } 130 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 131 | { 132 | serv->read_queue.free(&serv->read_queue); 133 | serv->write_queue.free(&serv->write_queue); 134 | } 135 | //close pipes 136 | return SW_OK; 137 | ``` 138 | 源码解释:杀死manager进程,遍历swServer的worker列表并分别杀死每一个worker进程。如果使用了消息队列模式,则还需将read队列和write队列释放。 139 | swFactoryProcess_start函数用于启动一个swFactoryProcess实例。这里需要注意,在该函数中涉及到一些没有分析过的函数和结构体,这些对象的实际分析将于后面的章节进行,在此我只简单说明这些函数的作用。swFactoryProcess_start函数核心源码如下: 140 | ```c 141 | if (swFactory_check_callback(factory) < 0) 142 | { 143 | swWarn("swFactory_check_callback failed"); 144 | return SW_ERR; 145 | } 146 | 147 | int i; 148 | swServer *serv = factory->ptr; 149 | swWorker *worker; 150 | 151 | for (i = 0; i < serv->worker_num; i++) 152 | { 153 | worker = swServer_get_worker(serv, i); 154 | if (swWorker_create(worker) < 0) 155 | { 156 | return SW_ERR; 157 | } 158 | } 159 | 160 | //必须先启动manager进程组,否则会带线程fork 161 | if (swFactoryProcess_manager_start(factory) < 0) 162 | { 163 | swWarn("swFactoryProcess_manager_start fail"); 164 | return SW_ERR; 165 | } 166 | 167 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 168 | { 169 | swQueueMsg_set_blocking(&serv->read_queue, 0); 170 | //tcp & message queue require writer pthread 171 | if (serv->have_tcp_sock == 1) 172 | { 173 | int ret = swFactoryProcess_writer_start(factory); 174 | if (ret < 0) 175 | { 176 | return SW_ERR; 177 | } 178 | } 179 | } 180 | 181 | //主进程需要设置为直写模式 182 | factory->finish = swFactory_finish; 183 | ``` 184 | 源码解释:首先判断swFactory是否设置了相应的PHP回调函数(onTask,onFinish)。然后创建worker_num个worker对象,随后调用swFactoryProcess_manager_start函数启动manager进程和对应的worker子进程。如果swServer设置了消息队列属性,则设置swServer的read队列为非阻塞,并启动writer线程。最后设置manager主进程的finish函数。 185 | 186 | swFactoryProcess_end函数用于关闭一个swFactoryProcess实例。其核心代码入下: 187 | ```c 188 | swServer *serv = factory->ptr; 189 | swSendData _send; 190 | 191 | bzero(&_send, sizeof(_send)); 192 | 193 | _send.info.fd = event->fd; 194 | /** 195 | * length == 0, close the connection 196 | */ 197 | _send.info.len = 0; 198 | 199 | /** 200 | * passive or initiative 201 | */ 202 | _send.info.type = event->type; 203 | 204 | swConnection *conn = swServer_connection_get(serv, _send.info.fd); 205 | if (conn == NULL || conn->active == 0) 206 | { 207 | swWarn("can not close. Connection[%d] not found.", _send.info.fd); 208 | return SW_ERR; 209 | } 210 | if (serv->onClose != NULL) 211 | { 212 | serv->onClose(serv, event->fd, conn->from_id); 213 | } 214 | return swFactoryProcess_finish(factory, &_send); 215 | ``` 216 | 源码解释:获取swServer对象,创建需要发送的swSendData并设置fd、长度(长度为0代表关闭连接)和类型。并获取swServer中fd对应的connection连接。随后调用onClose回调函数(如果有),并调用swFactoryProcess_finish函数发送数据。 217 | 218 | 除了以上四个共有的操作函数外,swFactoryProcess还拥有三个不同的操作函数,声明如下: 219 | ```c 220 | static int swFactoryProcess_notify(swFactory *factory, swDataHead *event); 221 | static int swFactoryProcess_dispatch(swFactory *factory, swDispatchData *buf); 222 | static int swFactoryProcess_finish(swFactory *factory, swSendData *data); 223 | ``` 224 | 这三个函数是核心的进程通信函数,notify函数用于主进程通知worker进程,dispatch用于reactor向worker进程投递请求,finish用于worker将执行后的数据发送给client。 225 | 一个个来看,首先是swFactoryProcess_notify函数,其核心代码如下: 226 | ```c 227 | int swFactoryProcess_notify(swFactory *factory, swDataHead *ev) 228 | { 229 | memcpy(&sw_notify_data._send, ev, sizeof(swDataHead)); 230 | sw_notify_data._send.len = 0; 231 | return factory->dispatch(factory, (swDispatchData *) &sw_notify_data); 232 | } 233 | ``` 234 | 源码解释:通过factory的dispatch函数将notify_data发送给worker进程。 235 | 其中sw_notify_data的声明如下: 236 | ```c 237 | static __thread struct 238 | { 239 | long target_worker_id; 240 | swDataHead _send; 241 | } sw_notify_data; 242 | ``` 243 | 这个结构体是用来匹配swDispatchData结构体的,并没有特殊的意义。 244 | swFactoryDespatch函数的调用来自于ReactorThread或者ReactorProcess(根据Rango的说法,所有线程相关的模块都已经停用了……),其核心源码如下: 245 | ```c 246 | uint32_t schedule_key; 247 | uint32_t send_len = sizeof(task->data.info) + task->data.info.len; 248 | uint16_t target_worker_id; 249 | swServer *serv = SwooleG.serv; 250 | 251 | if (task->target_worker_id < 0) 252 | { 253 | //udp use remote port 254 | if (task->data.info.type == SW_EVENT_UDP || task->data.info.type == SW_EVENT_UDP6 255 | || task->data.info.type == SW_EVENT_UNIX_DGRAM) 256 | { 257 | schedule_key = task->data.info.from_id; 258 | } 259 | else 260 | { 261 | schedule_key = task->data.info.fd; 262 | } 263 | 264 | #ifndef SW_USE_RINGBUFFER 265 | if (SwooleTG.factory_lock_target) 266 | { 267 | if (SwooleTG.factory_target_worker < 0) 268 | { 269 | target_worker_id = swServer_worker_schedule(serv, schedule_key); 270 | SwooleTG.factory_target_worker = target_worker_id; 271 | } 272 | else 273 | { 274 | target_worker_id = SwooleTG.factory_target_worker; 275 | } 276 | } 277 | else 278 | #endif 279 | { 280 | target_worker_id = swServer_worker_schedule(serv, schedule_key); 281 | } 282 | } 283 | else 284 | { 285 | target_worker_id = task->target_worker_id; 286 | } 287 | 288 | if (SwooleTG.type == SW_THREAD_REACTOR) 289 | { 290 | return swReactorThread_send2worker((void *) &(task->data), send_len, target_worker_id); 291 | } 292 | else 293 | { 294 | swTrace("dispatch to worker#%d", target_worker_id); 295 | return swServer_send2worker_blocking(serv, (void *) &(task->data), send_len, target_worker_id); 296 | } 297 | ``` 298 | 源码解释:首先判定task是否已经指定了worker,如果task的target_worker_id小于0(代表没有指定),则需要为其分配worker进程。首先根据task的data域中info的type字段判断task是属于哪个fd,这个fd用于在随后的调用中判断如何分配worker进程。之后,判断是否指定使用RingBuffer,判断SwooleTG(Swoole Thread Global,线程相关全局变量,以__thread关键字修饰,每个线程拥有自己独立的一份,线程之间互不影响)是否指定了当前线程所有的worker_id,如果没有指定,调用swServer_worker_schedule函数分配worker;否则直接使用指定的worker。如果以上条件都未能满足,则直接使用swServer_worker_schedule函数分配worker。如果task已经指定了worker,则默认使用该worker。随后判断SwooleTG的type字段,如果为SW_THREAD_REACTOR(线程Reacotr模式),则调用swReactorThread_send2worker函数(非阻塞)发送task请求;否则(type为SW_PROCESS_REACTOR模式),调用swServer_send2worker_blocking函数(阻塞)发送请求。 299 | 300 | 这里解释一下这两个函数的阻塞和非阻塞的原因。swReactorThread_send2worker调用write函数如果失败,会将数据存放进pipe的缓存中,等待下一次发送;而swServer_send2worker_blocking调用write失败后,会重新发送或调用swSocket_wait等待一段时间后再次发送。 301 | 302 | 接下来是swFactoryProcess_finish函数,该函数用于将一个resp应答发送给task的发起者。由于该函数较长,在此分段进行分析:其源码如下: 303 | ```c 304 | //unix dgram 305 | if (resp->info.type == SW_EVENT_UNIX_DGRAM) 306 | { 307 | socklen_t len; 308 | struct sockaddr_un addr_un; 309 | int from_sock = resp->info.from_fd; 310 | 311 | addr_un.sun_family = AF_UNIX; 312 | memcpy(addr_un.sun_path, resp->sun_path, resp->sun_path_len); 313 | len = sizeof(addr_un); 314 | ret = swSendto(from_sock, resp->data, resp->info.len, 0, (struct sockaddr *) &addr_un, len); 315 | goto finish; 316 | } 317 | ``` 318 | 源码解释:如果判定resp的类型为unixsock的报文模式,则构建相应sockaddr_un结构体并调用swSendto函数发送resp(swSendto函数分析见附录)。发送完成后,goto到finish标签继续运行。 319 | ```c 320 | //UDP pacakge 321 | else if (resp->info.type == SW_EVENT_UDP || resp->info.type == SW_EVENT_UDP6) 322 | { 323 | ret = swServer_udp_send(serv, resp); 324 | goto finish; 325 | } 326 | ``` 327 | 源码解释:如果是UDP模式,调用swServer_udp_send函数发送数据并goto到finish标签。 328 | ```c 329 | struct 330 | { 331 | long pti; 332 | swEventData _send; 333 | } sdata; 334 | 335 | //for message queue 336 | sdata.pti = (SwooleWG.id % serv->writer_num) + 1; 337 | 338 | swConnection *conn = swServer_connection_get(serv, fd); 339 | if (conn == NULL || conn->active == 0) 340 | { 341 | swWarn("connection[%d] not found.", fd); 342 | return SW_ERR; 343 | } 344 | 345 | sdata._send.info.fd = fd; 346 | sdata._send.info.type = resp->info.type; 347 | swWorker *worker = swServer_get_worker(serv, SwooleWG.id); 348 | ``` 349 | 源码解释:构建了一个临时结构体用于匹配swQueue_data结构体,并指定对应的writer线程id。随后获取fd对应的connection连接和SwooleWG(Worker进程的全局变量)中存放的当前进程对应的worker_id。 350 | ```c 351 | if (resp->length > 0) 352 | { 353 | int64_t wait_reactor; 354 | 355 | /** 356 | * Storage is in use right now, wait notify. 357 | */ 358 | if (worker->store.lock == 1) 359 | { 360 | worker->notify->read(worker->notify, &wait_reactor, sizeof(wait_reactor)); 361 | } 362 | swPackage_response response; 363 | 364 | response.length = resp->length; 365 | response.worker_id = SwooleWG.id; 366 | 367 | //swWarn("BigPackage, length=%d|worker_id=%d", response.length, response.worker_id); 368 | 369 | sdata._send.info.from_fd = SW_RESPONSE_BIG; 370 | sdata._send.info.len = sizeof(response); 371 | 372 | memcpy(sdata._send.data, &response, sizeof(response)); 373 | 374 | /** 375 | * Lock the worker storage 376 | */ 377 | worker->store.lock = 1; 378 | memcpy(worker->store.ptr, resp->data, resp->length); 379 | } 380 | ``` 381 | 源码解释:如果resp的长度大于0,表示这是个较大的应答包(完全不理解为什么大于0就代表big response了……),需要使用共享内存。首先判定worker的store是否已经被占用,如果被占用,则通过notify管道等待唤醒。当store可用后,用swPackage_response结构体封装数据信息并代替原有的data域,设置from_id为SW_RESPONSE_BIG,并将data复制到worker的store共享内存中并上锁。 382 | ```c 383 | #if SW_REACTOR_SCHEDULE == 2 384 | sdata._send.info.from_id = fd % serv->reactor_num; 385 | #else 386 | sdata._send.info.from_id = conn->from_id; 387 | #endif 388 | ``` 389 | 源码解释:根据Reactor的Schedule模型确定from_id, 2代表的是取模。 390 | ```c 391 | for (count = 0; count < SW_WORKER_SENDTO_COUNT; count++) 392 | { 393 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 394 | { 395 | ret = serv->write_queue.in(&serv->write_queue, (swQueue_data *) &sdata, sendn); 396 | } 397 | else 398 | { 399 | int master_pipe = swWorker_get_write_pipe(serv, fd); 400 | //swWarn("send to reactor. fd=%d|pipe=%d|reactor_id=%d|reactor_pipe_num=%d", fd, master_pipe, conn->from_id, serv->reactor_pipe_num); 401 | ret = write(master_pipe, &sdata._send, sendn); 402 | 403 | #ifdef SW_WORKER_WAIT_PIPE 404 | if (ret < 0 && errno == EAGAIN) 405 | { 406 | /** 407 | * Wait pipe can be written. 408 | */ 409 | if (swSocket_wait(master_pipe, SW_WORKER_WAIT_TIMEOUT, SW_EVENT_WRITE) == SW_OK) 410 | { 411 | continue; 412 | } 413 | else 414 | { 415 | goto finish; 416 | } 417 | } 418 | #endif 419 | } 420 | //swTraceLog("wt_queue->in: fd=%d|from_id=%d|data=%s|ret=%d|errno=%d", sdata._send.info.fd, sdata._send.info.from_id, sdata._send.data, ret, errno); 421 | if (ret >= 0) 422 | { 423 | break; 424 | } 425 | else if (errno == EINTR) 426 | { 427 | continue; 428 | } 429 | else if (errno == EAGAIN) 430 | { 431 | swYield(); 432 | } 433 | else 434 | { 435 | break; 436 | } 437 | } 438 | ``` 439 | 源码解释:循环写出数据直到写出成功,最大重写次数为SW_WORKER_SENDTO_COUNT(32)次。每次写出时,先判断是否为消息队列模式,如果是,将数据写入消息队列;如果不是,获取worker的write_pipe,并通过该管道将数据发送给master进程。 440 | 441 | 对于FactoryProcess的基本分析到这里就结束了,下一章将分析FactoryProcess.c文件中剩下的操作函数。 442 | 443 | -------------------------------------------------------------------------------- /11.Worker,Connection.md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | 4 | ------------- 5 | 6 | ##Swoole版本:1.7.5-stable 7 | 8 | ------------- 9 | 本章将分析Swoole中的三个比较重要的模块,Worker,ReactorProcess和Connection。其中Worker和ReactorProcess其实是对前面三章的一个补充,在前面的章节中为了分析结果的流畅性没有针对这些模块做特定分析,在此做出补充。 10 | 11 | ###Worker模块 12 | 首先是Worker模块。Worker在Swoole中为核心工作进程的封装,包括用于处理核心逻辑的worker和用于处理任务的task_worker。在Swoole中使用了结构体swWorker来封装worker进程的相关属性,其声明在swoole.h文件中的**727 – 787**行,其声明如下: 13 | ```c 14 | struct _swWorker 15 | { 16 | /** 17 | * worker process 18 | */ 19 | pid_t pid; 20 | 21 | /** 22 | * worker thread 23 | */ 24 | pthread_t tid; 25 | 26 | swProcessPool *pool; 27 | 28 | swMemoryPool *pool_output; 29 | 30 | swQueue *queue; 31 | 32 | /** 33 | * redirect stdout to pipe_master 34 | */ 35 | uint8_t redirect_stdout; 36 | 37 | /** 38 | * worker status, IDLE or BUSY 39 | */ 40 | uint8_t status; 41 | 42 | uint8_t ipc_mode; 43 | 44 | /** 45 | * redirect stdin to pipe_worker 46 | */ 47 | uint8_t redirect_stdin; 48 | 49 | /** 50 | * worker id 51 | */ 52 | uint32_t id; 53 | 54 | /** 55 | * eventfd, process notify 56 | */ 57 | swPipe *notify; 58 | 59 | /** 60 | * share memory store 61 | */ 62 | struct 63 | { 64 | uint8_t lock; 65 | void *ptr; 66 | } store; 67 | 68 | int pipe_master; 69 | int pipe_worker; 70 | int pipe; 71 | int reactor_id; 72 | void *ptr; 73 | void *ptr2; 74 | }; 75 | ``` 76 | 标有注释的变量就不说明了,大家一看就明白。剩下的几个,pool是个进程池,用于分配task_worker,pool_output用于存放task_worker执行结束后的结果,queue是消息队列,ipc_mode是进程间的通讯模式,pipe_master和pipe_worker是管道的fd,分别用于写消息到master进程和从master读取消息,pipe当然就是管道的id,reactor_id是该worker归属的reactor的标志。(ptr和ptr2待补充,实在没找到这俩变量在哪用的) 77 | 78 | 这里做一点补充,一个Worker中有两个管道,一个管道用于和master进程通信,一个管道用于和Reactor通信。而实际上如果指定了消息队列模式,则通信方式都是通过读写swQueue队列来实现的。这几点在之前的分析中已经有说明了,再此补充说明一下。 79 | 80 | swWorker有四个操作函数,这些函数声明在Server.h文件中的**545 – 548**行,其声明如下: 81 | ```c 82 | int swWorker_create(swWorker *worker); 83 | void swWorker_free(swWorker *worker); 84 | void swWorker_signal_init(void); 85 | void swWorker_signal_handler(int signo); 86 | ``` 87 | 这四个函数声明在Worker.c中,其中swWorker_free只是释放了worker的store内存并且关闭了notify管道(不接收reactor消息),swWorker_signal_init指定了对应信号的回调函数,swWorker_signal_handler规定了对应信号的操作(基本——没内容,看一看就好……),这里只贴出swWorker_create的源码: 88 | ```c 89 | void *store = sw_shm_malloc(SwooleG.serv->buffer_output_size); 90 | if (store == NULL) 91 | { 92 | swWarn("malloc for worker->store failed."); 93 | return SW_ERR; 94 | } 95 | 96 | swPipe *worker_notify = sw_malloc(sizeof(swPipe)); 97 | if (worker_notify == NULL) 98 | { 99 | swWarn("malloc for worker->notify failed."); 100 | sw_shm_free(store); 101 | return SW_ERR; 102 | } 103 | 104 | /** 105 | * Create notify pipe 106 | */ 107 | if (swPipeNotify_auto(worker_notify, 1, 0)) 108 | { 109 | sw_shm_free(store); 110 | sw_free(worker_notify); 111 | return SW_ERR; 112 | } 113 | ``` 114 | 源码解释:创建共享内存store,分配通信管道notify的内存,并调用swPipeNotify_auto创建实际管道(根据内核版本决定使用eventfd管道还是Base管道)。 115 | 116 | 117 | ###Connection模块 118 | Connection模块应该是少有的有自己独立的头文件和C文件的模块了…… 119 | Connection用于存储一个实际的连接(C-to-S),用来存放一些相关的变量,比如连接时长、上一次响应时间之类的,当然另一个好处就在于可以给这个连接加一个SSL控制然后就能实现收发数据的安全加密解密了对不对(好像很厉害的样子)…… 120 | 首先是swConnection结构体,该结构体封装了所有Connection的属性变量,其声明在Connection.c文件中,声明如下: 121 | ```c 122 | typedef struct _swConnection 123 | { 124 | /** 125 | * is active 126 | * system fd must be 0. en: timerfd, signalfd, listen socket 127 | */ 128 | uint8_t active; 129 | 130 | /** 131 | * file descript 132 | */ 133 | int fd; 134 | 135 | /** 136 | * ReactorThread id 137 | */ 138 | uint16_t from_id; 139 | 140 | /** 141 | * from which socket fd 142 | */ 143 | uint16_t from_fd; 144 | 145 | /** 146 | * socket address 147 | */ 148 | struct sockaddr_in addr; 149 | 150 | /** 151 | * link any thing 152 | */ 153 | void *object; 154 | 155 | /** 156 | * input buffer 157 | */ 158 | swBuffer *in_buffer; 159 | 160 | /** 161 | * output buffer 162 | */ 163 | swBuffer *out_buffer; 164 | 165 | /** 166 | * connect time(seconds) 167 | */ 168 | time_t connect_time; 169 | 170 | /** 171 | * received time with last data 172 | */ 173 | time_t last_time; 174 | 175 | #ifdef SW_USE_OPENSSL 176 | SSL *ssl; 177 | uint32_t ssl_state; 178 | #endif 179 | 180 | } swConnection; 181 | ``` 182 | 基本上每一个变量都有注释我就不多废话了……不过这里说明一下,swConnection中的fd变量存放的是该连接所对应的描述符,而那个from_fd吧……经过一系列的排查我最终发现……from_fd是Server创建的监听fd。 183 | (大概说一下排查过程,首先找到创建Connection的函数,因为知道Connection大概会在Server处创建,所以在Server.h中找到swServer_connection_new,然后看每个变量的具体赋值,然后发现from_fd是参数中的swDataHead赋予的,于是回追到调用swServer_connection_new的函数swServer_master_onAccept,然后就发现fd来自于accept的返回值,是一个新创建的socket连接,而from_fd是server创建的用于监听的socket fd) 184 | 185 | Connection的操作函数分布在两个地方,new和close函数声明在Server.h的**476 – 477** 行,而其他操作函数都在Connection.h文件中声明(收发数据、操作缓存)。 186 | 首先是在Server.h中用于新建和关闭连接的函数,其声明如下: 187 | ```c 188 | swConnection* swServer_connection_new(swServer *serv, swDataHead *ev); 189 | int swServer_connection_close(swServer *serv, int fd, int notify); 190 | ``` 191 | 这两个函数的具体定义在Server.c文件中,swServer_connection_new函数定义在文件最末尾,其核心代码如下: 192 | ```c 193 | int conn_fd = ev->fd; 194 | swConnection* connection = NULL; 195 | 196 | SwooleStats->accept_count++; 197 | sw_atomic_fetch_add(&SwooleStats->connection_num, 1); 198 | 199 | if (conn_fd > swServer_get_maxfd(serv)) 200 | { 201 | swServer_set_maxfd(serv, conn_fd); 202 | #ifdef SW_CONNECTION_LIST_EXPAND 203 | //新的fd超过了最大fd 204 | //需要扩容 205 | if (conn_fd == serv->connection_list_capacity - 1) 206 | { 207 | void *new_ptr = sw_shm_realloc(serv->connection_list, sizeof(swConnection)*(serv->connection_list_capacity + SW_CONNECTION_LIST_EXPAND)); 208 | if (new_ptr == NULL) 209 | { 210 | swWarn("connection_list realloc fail"); 211 | return SW_ERR; 212 | } 213 | else 214 | { 215 | serv->connection_list_capacity += SW_CONNECTION_LIST_EXPAND; 216 | serv->connection_list = (swConnection *)new_ptr; 217 | } 218 | } 219 | #endif 220 | } 221 | 222 | connection = &(serv->connection_list[conn_fd]); 223 | 224 | connection->fd = conn_fd; 225 | connection->from_id = ev->from_id; 226 | connection->from_fd = ev->from_fd; 227 | connection->connect_time = SwooleGS->now; 228 | connection->last_time = SwooleGS->now; 229 | connection->active = 1; //使此连接激活,必须在最后,保证线程安全 230 | ``` 231 | 源码解释:首先将accept_count和connection_num两个计数器加1。如果新的conn_fd超过了serv中连接列表的最大容量,就将连接列表扩容(注意到这里调用的是sw_shm_realloc,也就是connection_list的内存是可共享的)。随后将新的conn_fd加入连接列表中,然后给connection的属性赋值。主要的属性有:连接描述符fd,来自于哪个reactor、来自于哪个监听fd、连接的时间和上一次响应时间、连接的active状态。 232 | 233 | 然后是swServer_connection_close函数,该函数也定义在Server.c的末尾,让我没想到的是一个close函数也有这么长,以至于我不得不分段进行分析…… 234 | ```c 235 | conn->active = 0; 236 | /** 237 | * Close count 238 | */ 239 | sw_atomic_fetch_add(&SwooleStats->close_count, 1); 240 | sw_atomic_fetch_sub(&SwooleStats->connection_num, 1); 241 | 242 | int reactor_id = conn->from_id; 243 | 244 | reactor = &(serv->reactor_threads[reactor_id].reactor); 245 | ``` 246 | 源码解释:首先将conn设置为关闭状态,然后修改两个计数器close_count和connection_num.随后根据conn中的reactor_id从serv的reactor列表中获取对应的reactor。 247 | ```c 248 | //释放缓存区占用的内存 249 | if (serv->open_eof_check) 250 | { 251 | if (conn->in_buffer) 252 | { 253 | swBuffer_free(conn->in_buffer); 254 | conn->in_buffer = NULL; 255 | } 256 | } 257 | else if (serv->open_length_check) 258 | { 259 | if (conn->object) 260 | { 261 | swString_free(conn->object); 262 | } 263 | } 264 | else if (serv->open_http_protocol) 265 | { 266 | if (conn->object) 267 | { 268 | swHttpRequest *request = (swHttpRequest *) conn->object; 269 | if (request->state > 0 && request->buffer) 270 | { 271 | swTrace("ConnectionClose. free buffer=%p, request=%p\n", request->buffer, request); 272 | swString_free(request->buffer); 273 | bzero(request, sizeof(swHttpRequest)); 274 | } 275 | } 276 | } 277 | if (conn->out_buffer != NULL) 278 | { 279 | swBuffer_free(conn->out_buffer); 280 | conn->out_buffer = NULL; 281 | } 282 | 283 | if (conn->in_buffer != NULL) 284 | { 285 | swBuffer_free(conn->in_buffer); 286 | conn->in_buffer = NULL; 287 | } 288 | ``` 289 | 源码解释:这里用于释放conn开启的缓存区。如果开启了eof_check,则需要将conn的输入缓存释放(这里放着没有检测到eof的数据);如果打开了length_check,则将object指向的对象释放;如果使用了http协议,则释放object指向的swHttpRequest对象。最后,释放out_buffer和in_buffer。 290 | ```c 291 | //通知到worker进程 292 | if (serv->onClose != NULL && notify == 1) 293 | { 294 | //通知worker进程 295 | notify_ev.from_id = reactor_id; 296 | notify_ev.fd = fd; 297 | notify_ev.type = SW_EVENT_CLOSE; 298 | SwooleG.factory->notify(SwooleG.factory, ¬ify_ev); 299 | } 300 | ``` 301 | 源码解释:如果设置了onClose回调并且指定需要通知,则调用factory的notify方法发送通知到worker进程。(所以发现onClose回调异常的童鞋可以通过这里查找bug) 302 | ```c 303 | #ifdef SW_USE_OPENSSL 304 | if (conn->ssl) 305 | { 306 | swSSL_close(conn); 307 | } 308 | #endif 309 | 310 | /** 311 | * reset maxfd, for connection_list 312 | */ 313 | if (fd == swServer_get_maxfd(serv)) 314 | { 315 | SwooleG.lock.lock(&SwooleG.lock); 316 | int find_max_fd = fd - 1; 317 | swTrace("set_maxfd=%d|close_fd=%d\n", find_max_fd, fd); 318 | /** 319 | * Find the new max_fd 320 | */ 321 | for (; serv->connection_list[find_max_fd].active == 0 && find_max_fd > swServer_get_minfd(serv); find_max_fd--); 322 | swServer_set_maxfd(serv, find_max_fd); 323 | SwooleG.lock.unlock(&SwooleG.lock); 324 | } 325 | 326 | //关闭此连接,必须放在最前面,以保证线程安全 327 | return reactor->del(reactor, fd); 328 | ``` 329 | 源码解释:如果使用了SSL功能,则需要从SSL中移除该SSL。如果关闭的fd是maxfd,则需要重新设置serv中的连接列表,操作步骤为先锁住全局变量SwooleG,然后从末尾循环便利connection_list直到找到当前最大的fd,最后解锁。最后一步,从reactor中移除监听的fd(谁能给我解释一下这句注释啥意思,很急,在线等) 330 | 331 | 在Connection.h中一共声明了10个操作函数,其中三个内联函数。据我目测,实际上原来应该没有**swConnection_recv**和**swConnection_send**这两个函数,应该是有了SSL特性后专门添加上的,因为这俩函数就只是判断了一下有没有开启SSL特性,如果开启了并且conn设置了ssl,则调用ssl的安全读写方法。**swConnection_error**函数也不具体分析了,只是根据errcode的值返回对应的SW_*。 332 | 333 | 另外7个操作函数声明如下: 334 | ```c 335 | int swConnection_send_blocking(int fd, void *data, int length, int timeout); 336 | int swConnection_buffer_send(swConnection *conn); 337 | 338 | swString* swConnection_get_string_buffer(swConnection *conn); 339 | void swConnection_clear_string_buffer(swConnection *conn); 340 | volatile swBuffer_trunk* swConnection_get_out_buffer(swConnection *conn, uint32_t type); 341 | volatile swBuffer_trunk* swConnection_get_in_buffer(swConnection *conn); 342 | int swConnection_sendfile(swConnection *conn, char *filename); 343 | ``` 344 | 可以看到基本上都是用于发送数据、文件以及操作缓存区的函数,下面一个个上分析。 345 | 346 | 首先是swConnection_send_blocking函数,该函数是一个阻塞式的发送函数,其源码如下: 347 | ```c 348 | while (writen > 0) 349 | { 350 | if (swSocket_wait(fd, timeout, SW_EVENT_WRITE) < 0) 351 | { 352 | return SW_ERR; 353 | } 354 | else 355 | { 356 | n = send(fd, data, writen, MSG_NOSIGNAL | MSG_DONTWAIT); 357 | if (n < 0) 358 | { 359 | swWarn("send() failed. Error: %s[%d]", strerror(errno), errno); 360 | return SW_ERR; 361 | } 362 | else 363 | { 364 | writen -= n; 365 | continue; 366 | } 367 | } 368 | } 369 | ``` 370 | 源码解释:首先调用swSocket_wait函数(调用poll函数监听socket直到socket满足监听条件)监听到fd可写,随后调用send函数发送数据;循环写入直到所有数据都写入fd。 371 | 372 | 这里我补充说明一下swoole的Buffer.c,不作详细分析,只说明其功能。Buffer是个链表式结构,每个链表节点是一个trunk,trunk用于存放具体的数据。 373 | 374 | 下面分析swConnection_buffer_send函数,这个函数是将conn中的输出缓存区中的数据发出,核心源码如下: 375 | ```c 376 | swBuffer *buffer = conn->out_buffer; 377 | swBuffer_trunk *trunk = swBuffer_get_trunk(buffer); 378 | sendn = trunk->length - trunk->offset; 379 | 380 | if (sendn == 0) 381 | { 382 | swBuffer_pop_trunk(buffer, trunk); 383 | return SW_CONTINUE; 384 | } 385 | ret = swConnection_send(conn, trunk->store.ptr + trunk->offset, sendn, 0); 386 | //printf("BufferOut: reactor=%d|sendn=%d|ret=%d|trunk->offset=%d|trunk_len=%d\n", reactor->id, sendn, ret, trunk->offset, trunk->length); 387 | if (ret < 0) 388 | { 389 | switch (swConnection_error(errno)) 390 | { 391 | case SW_ERROR: 392 | swWarn("send to fd[%d] failed. Error: %s[%d]", conn->fd, strerror(errno), errno); 393 | return SW_OK; 394 | case SW_CLOSE: 395 | return SW_CLOSE; 396 | case SW_WAIT: 397 | return SW_WAIT; 398 | default: 399 | return SW_CONTINUE; 400 | } 401 | } 402 | //trunk full send 403 | else if (ret == sendn || sendn == 0) 404 | { 405 | swBuffer_pop_trunk(buffer, trunk); 406 | } 407 | else 408 | { 409 | trunk->offset += ret; 410 | } 411 | ``` 412 | 源码解释:首先从conn中的输出缓存中拿到首部的trunk,并计算该trunk还有多少数据需要发送。如果已经发送完了,弹出该trunk,继续发送下一个trunk。如果没有发送完,调用swConnection_send方法发送剩余数据。随后从trunk中移除已经发送的数据,并再次判定是否发送结束。 413 | 414 | swConnection中有个void* object变量,这个指针可以用来指向一些奇奇怪怪的东西,比如一个swString对象,于是就有了两个操作函数**swConnection_get_string_buffer**和**swConnection_clear_string_buffer**,这俩函数不贴代码了,一个new一个free而已。 415 | 416 | 接着分析两个操作buffer的函数。首先是swConnection_get_in_buffer,该函数的作用是创建一个新的trunk加入到in_buffer中,并返回这个trunk的指针,其核心源码如下: 417 | ```c 418 | if (conn->in_buffer == NULL) 419 | { 420 | buffer = swBuffer_new(SW_BUFFER_SIZE); 421 | //buffer create failed 422 | if (buffer == NULL) 423 | { 424 | return NULL; 425 | } 426 | //new trunk 427 | trunk = swBuffer_new_trunk(buffer, SW_TRUNK_DATA, buffer->trunk_size); 428 | if (trunk == NULL) 429 | { 430 | sw_free(buffer); 431 | return NULL; 432 | } 433 | conn->in_buffer = buffer; 434 | } 435 | else 436 | { 437 | buffer = conn->in_buffer; 438 | trunk = buffer->tail; 439 | if (trunk == NULL || trunk->length == buffer->trunk_size) 440 | { 441 | trunk = swBuffer_new_trunk(buffer, SW_TRUNK_DATA, buffer->trunk_size); 442 | } 443 | } 444 | ``` 445 | 源码解释:如果in_buffe为空,则创建一个swBuffer,并在这个buffer中创建一个新的trunk;否则,直接在in_buffer中创建trunk并添加到buffer末尾。 446 | 447 | swConnection_get_out_buffer用于从out_buffer中拿到一个trunk。核心源码如下: 448 | ```c 449 | if (conn->out_buffer == NULL) 450 | { 451 | conn->out_buffer = swBuffer_new(SW_BUFFER_SIZE); 452 | if (conn->out_buffer == NULL) 453 | { 454 | return NULL; 455 | } 456 | } 457 | if (type == SW_TRUNK_SENDFILE) 458 | { 459 | trunk = swBuffer_new_trunk(conn->out_buffer, SW_TRUNK_SENDFILE, 0); 460 | } 461 | else 462 | { 463 | trunk = swBuffer_get_trunk(conn->out_buffer); 464 | if (trunk == NULL) 465 | { 466 | trunk = swBuffer_new_trunk(conn->out_buffer, SW_TRUNK_DATA, conn->out_buffer->trunk_size); 467 | } 468 | } 469 | ``` 470 | 源码解释:如果out_buffer为空,尝试创建,如果创建失败,返回NULL。然后判定type类型,如果为SENDFILEl类型,则直接创建type类型的trunk;如果不是,则尝试从buffer中获取首部的trunk,如果该trunk为空,则创建新的trunk。 471 | 472 | 这里重点分析一下swConnection_sendfile函数,该函数用于在out_buffer中添加一个用于sendfile的trunk。其核心源码如下: 473 | ```c 474 | if (conn->out_buffer == NULL) 475 | { 476 | conn->out_buffer = swBuffer_new(SW_BUFFER_SIZE); 477 | if (conn->out_buffer == NULL) 478 | { 479 | return SW_ERR; 480 | } 481 | } 482 | 483 | swBuffer_trunk *trunk = swBuffer_new_trunk(conn->out_buffer, SW_TRUNK_SENDFILE, 0); 484 | if (trunk == NULL) 485 | { 486 | swWarn("get out_buffer trunk failed."); 487 | return SW_ERR; 488 | } 489 | swTask_sendfile *task = sw_malloc(sizeof(swTask_sendfile)); 490 | if (task == NULL) 491 | { 492 | swWarn("malloc for swTask_sendfile failed."); 493 | //TODO: 回收这里的内存 494 | return SW_ERR; 495 | } 496 | bzero(task, sizeof(swTask_sendfile)); 497 | 498 | task->filename = strdup(filename); 499 | int file_fd = open(filename, O_RDONLY); 500 | if (file_fd < 0) 501 | { 502 | swWarn("open file[%s] failed. Error: %s[%d]", task->filename, strerror(errno), errno); 503 | return SW_ERR; 504 | } 505 | struct stat file_stat; 506 | if (fstat(file_fd, &file_stat) < 0) 507 | { 508 | swWarn("swoole_async_readfile: fstat failed. Error: %s[%d]", strerror(errno), errno); 509 | return SW_ERR; 510 | } 511 | 512 | task->filesize = file_stat.st_size; 513 | task->fd = file_fd; 514 | trunk->store.ptr = (void *)task; 515 | ``` 516 | 源码解释:先判定out_buffer是否存在,如果不存在则创建,随后在out_buffer中创建一个类型为sendfile的trunk。创建一个sendfile的task(swTask_sendfile),指定文件名以及文件的描述符fd,通过fstat函数获取文件的大小。随后将task添加到trunk的存储空间中。 517 | 518 | 以上就是Connection.c模块的相关分析。 519 | (原计划分析ReactorProcess模块,但是发现这里需要和ProcessPool一起分析,因此单独开一章。) 520 | 521 | 522 | -------------------------------------------------------------------------------- /13.Server模块详解(上).md: -------------------------------------------------------------------------------- 1 | 2 | 终于可以正式进入Server.c模块了…… 3 | 在之前的分析中,可以看到很多相关模块的声明都已经写在了Server.h中,就是因为这些模块构成了Server的核心部分。而Server本身,则是一个最上层的对象,它包括了核心的Reactor和Factory模块,存放了消息队列的key值,控制着全部的Connection,所有PHP层面的回调函数也在这里指定;同时,Server存放了大量的属性值,这些值决定了整个Swoole的详细特征。 4 | 5 | 直接上源码。首先是swServer结构体,这个结构体定义了一个swoole_server对象所拥有的全部成员。其声明在Server.h文件的197 - 406 行。因为该结构体太长,因此我将分段贴出其成员并分析。 6 | ```c 7 | /** 8 | * tcp socket listen backlog 9 | */ 10 | uint16_t backlog; 11 | /** 12 | * reactor thread/process num 13 | */ 14 | uint16_t reactor_num; 15 | uint16_t writer_num; 16 | /** 17 | * worker process num 18 | */ 19 | uint16_t worker_num; 20 | 21 | /** 22 | * The number of pipe per reactor maintenance 23 | */ 24 | uint16_t reactor_pipe_num; 25 | 26 | uint8_t factory_mode; 27 | 28 | /** 29 | * run as a daemon process 30 | */ 31 | uint8_t daemonize; 32 | 33 | /** 34 | * package dispatch mode 35 | */ 36 | uint8_t dispatch_mode; //分配模式,1平均分配,2按FD取摸固定分配,3,使用抢占式队列(IPC消息队列)分配 37 | 38 | /** 39 | * 1: unix socket, 2: message queue, 3: memory channel 40 | */ 41 | uint8_t ipc_mode; 42 | ``` 43 | backlog参数为socket监听时listen函数的参数,表示listen可以保持的最大连通数。reactor_num指定了reactor进程的数量,worker_num指定了worker进程的数量。reactor_pipe_num指定了每个reactor所拥有的管道数量。factory_mode指定了Swoole_server运行于哪种运行模式下(Base模式、单进程单线程、多进程、多线程)。daemonize指定是否作为守护进程运行(也就是后台运行)。dispatch_mode指定了收到消息时worker进程的分配模式,ipc_mode则指定了进程间的通信模式。 44 | 45 | ```c 46 | int worker_uid; 47 | int worker_groupid; 48 | 49 | /** 50 | * max connection num 51 | */ 52 | uint32_t max_connection; 53 | 54 | /** 55 | * worker process max request 56 | */ 57 | uint32_t max_request; 58 | /** 59 | * task worker process max request 60 | */ 61 | uint32_t task_max_request; 62 | 63 | int timeout_sec; 64 | int timeout_usec; 65 | 66 | int sock_client_buffer_size; //client的socket缓存区设置 67 | int sock_server_buffer_size; //server的socket缓存区设置 68 | 69 | char log_file[SW_LOG_FILENAME]; //日志文件 70 | 71 | int signal_fd; 72 | int event_fd; 73 | 74 | int ringbuffer_size; 75 | ``` 76 | worker_uid和worker_groupid是两个无用变量,估计是历史遗留……max_connection指定允许的最大连接数,max_request指定了每个worker进程能处理的最大请求数,task_max_request指定了每个task worker进程能处理的最大任务请求。timeout_sec和timeout_usec定义了超时时间。signal_fd和event_fd同样是废弃变量。ringbuffer_size指定了RingBuffer的大小。 77 | ```c 78 | uint16_t reactor_round_i; //轮询调度 79 | uint16_t reactor_next_i; //平均算法调度 80 | uint16_t reactor_schedule_count; 81 | 82 | uint16_t worker_round_id; 83 | 84 | int udp_sock_buffer_size; //UDP临时包数量,超过数量未处理将会被丢弃 85 | 86 | /** 87 | * reactor ringbuffer memory pool size 88 | */ 89 | size_t reactor_ringbuffer_size; 90 | 91 | /** 92 | * have udp listen socket 93 | */ 94 | uint8_t have_udp_sock; 95 | 96 | /** 97 | * have tcp listen socket 98 | */ 99 | uint8_t have_tcp_sock; 100 | 101 | /** 102 | * oepn cpu affinity setting 103 | */ 104 | uint8_t open_cpu_affinity; 105 | ``` 106 | 前三个属性用于Reactor的调度算法,而实际上reactor只使用了轮询调度,因此后两个属性都没有用到……worker_round_id用于worker轮询调度,reactor_ringbuffer_size指定了reactor中的RingBuffer内存池的大小。have_udp_sock和have_tcp_sock用于标记当前Server是否有udp和tcp的监听。open_cpu_affinity标记是否打开了cpu亲和性设置。 107 | 108 | ```c 109 | /** 110 | * open tcp_defer_accept option 111 | */ 112 | uint8_t tcp_defer_accept; //TCP_DEFER_ACCEPT 113 | 114 | /* tcp keepalive */ 115 | uint8_t open_tcp_keepalive; //开启keepalive 116 | uint16_t tcp_keepidle; //如该连接在规定时间内没有任何数据往来,则进行探测 117 | uint16_t tcp_keepinterval; //探测时发包的时间间隔 118 | uint16_t tcp_keepcount; //探测尝试的次数 119 | 120 | /* heartbeat check time*/ 121 | uint16_t heartbeat_idle_time; //心跳存活时间 122 | uint16_t heartbeat_check_interval; //心跳定时侦测时间, 必需小于heartbeat_idle_time 123 | 124 | /** 125 | * 来自客户端的心跳侦测包 126 | */ 127 | char heartbeat_ping[SW_HEARTBEAT_PING_LEN]; 128 | uint8_t heartbeat_ping_length; 129 | 130 | /** 131 | * 服务器端对心跳包的响应 132 | */ 133 | char heartbeat_pong[SW_HEARTBEAT_PING_LEN]; 134 | uint8_t heartbeat_pong_length; 135 | ``` 136 | tcp_defer_accept指定是否打开TCP_DEFER_ACCEPT选项,open_tcp_keepalive用于指定是否打开TCP_KEEPALIVE探测,接下来三个属性则用于设置对应的socket选项,heartbeat_idle_time和heartbeat_check_interval用于控制心跳检测。 137 | 这里做一些简单说明。TCP_DEFER_ACCEPT选项的意义在于,如果某个客户端发起连接后并没有发送数据,则服务器会先暂时挂起这个连接,不Accept,不建立IO通道,但是保留端口号;当客户端发送数据过来后,服务器会直接Accept这个端口号,建立IO。 138 | tcp_keepalive是tcp内部的一个连接保持机制,而heartbeat是应用层控制的连接检测。 139 | 140 | ```c 141 | /* one package: eof check */ 142 | uint8_t open_eof_check; //检测数据EOF 143 | uint8_t package_eof_len; //数据缓存结束符长度 144 | //int data_buffer_max_num; //数据缓存最大个数(超过此数值的连接会被当作坏连接,将清除缓存&关闭连接) 145 | //uint8_t max_trunk_num; //每个请求最大允许创建的trunk数 146 | char package_eof[SW_DATA_EOF_MAXLEN]; //数据缓存结束符 147 | 148 | /** 149 | * built-in http protocol 150 | */ 151 | uint8_t open_http_protocol; 152 | uint32_t http_max_post_size; 153 | uint32_t http_max_websocket_size; 154 | 155 | /* one package: length check */ 156 | uint8_t open_length_check; //开启协议长度检测 157 | 158 | char package_length_type; //length field type 159 | uint8_t package_length_size; 160 | uint16_t package_length_offset; //第几个字节开始表示长度 161 | uint16_t package_body_offset; //第几个字节开始计算长度 162 | uint32_t package_max_length; 163 | ``` 164 | 这一组变量控制着swoole不同的分包机制。第一组为eof检测,第二组为内建的http协议,第三组为包长检测。这里都有中文注释,我就不再赘述。 165 | 166 | ```c 167 | /** 168 | * Use data key as factory->dispatch() param 169 | */ 170 | uint8_t open_dispatch_key; 171 | uint8_t dispatch_key_size; 172 | uint16_t dispatch_key_offset; 173 | uint16_t dispatch_key_type; 174 | 175 | /* buffer output/input setting*/ 176 | uint32_t buffer_output_size; 177 | uint32_t buffer_input_size; 178 | 179 | #ifdef SW_USE_OPENSSL 180 | uint8_t open_ssl; 181 | char *ssl_cert_file; 182 | char *ssl_key_file; 183 | #endif 184 | ``` 185 | open_dispatch_key指定是否在dispatch时给数据加上key值,后面三个属性用于控制key的相关特性。buffer_output_size和buffer_input_size指定了输入输出缓存区的大小。最后一组参数用于设置SSL特性,指定了对应的私钥和证书。 186 | 187 | ```c 188 | void *ptr2; 189 | 190 | swReactor reactor; 191 | swFactory factory; 192 | 193 | swListenList_node *listen_list; 194 | 195 | swReactorThread *reactor_threads; 196 | swWorkerThread *writer_threads; 197 | 198 | swWorker *workers; 199 | 200 | swQueue read_queue; 201 | swQueue write_queue; 202 | 203 | swConnection *connection_list; //连接列表 204 | int connection_list_capacity; //超过此容量,会自动扩容 205 | 206 | /** 207 | * message queue key 208 | */ 209 | uint64_t message_queue_key; 210 | 211 | swReactor *reactor_ptr; //Main Reactor 212 | swFactory *factory_ptr; //Factory 213 | ``` 214 | 第一个ptr2变量存放了一个zval指针,存放的是一个server在PHP中的this指针(个人理解和猜测,未验证)。接下来的几个属性我想大家应该都不陌生,在之前的分析中也多次提到了这些参数。需要说明的是,最后的两个指针是遗留变量,没有实际用处。 215 | 216 | 最后还有N个回调函数指针,我就不贴代码了,我想大家都看得懂他们是干嘛的…… 217 | 218 | 然后swServer大概有……17个相关操作函数,这些函数声明在Server.h文件的435 - 449行,如下: 219 | ```c 220 | int swServer_onFinish(swFactory *factory, swSendData *resp); 221 | int swServer_onFinish2(swFactory *factory, swSendData *resp); 222 | 223 | void swServer_init(swServer *serv); 224 | void swServer_signal_init(void); 225 | int swServer_start(swServer *serv); 226 | int swServer_addListener(swServer *serv, int type, char *host,int port); 227 | int swServer_create(swServer *serv); 228 | int swServer_listen(swServer *serv, swReactor *reactor); 229 | int swServer_free(swServer *serv); 230 | int swServer_shutdown(swServer *serv); 231 | int swServer_addTimer(swServer *serv, int interval); 232 | int swServer_reload(swServer *serv); 233 | int swServer_udp_send(swServer *serv, swSendData *resp); 234 | int swServer_tcp_send(swServer *serv, int fd, void *data, int length); 235 | int swServer_reactor_add(swServer *serv, int fd, int sock_type); //no use 236 | int swServer_reactor_del(swServer *serv, int fd, int reacot_id); //no use 237 | int swServer_get_manager_pid(swServer *serv); 238 | ``` 239 | 然后我尽量按照调用的先后顺序来分析这些函数 240 | 241 | 首先是swServer_init函数,这个函数主要是设置swServer的相关属性,并通过swoole_init函数设置相关的全局变量,基本都是基础的赋值。这里需要重点分析**swServerG**结构体,该结构体声明在swoole.h的1052 - 1104行,其声明如下: 242 | ```c 243 | typedef struct 244 | { 245 | swTimer timer; // 全局定时器 246 | 247 | int running; // 是否正在运行 248 | int error; // 错误码errno 249 | int process_type; // 标记当前进程的类型(manager or worker) 250 | int signal_alarm; //for timer with message queue 251 | int signal_fd; // 没有找到赋值语句,应该是已经废弃 252 | int log_fd; // 日志文件的描述符 253 | 254 | uint8_t use_timerfd; // 是否使用Linux提供的timerfd功能 255 | uint8_t use_signalfd; // 是否使用signalfd功能 256 | /** 257 | * Timer used pipe 258 | */ 259 | uint8_t use_timer_pipe; // timer是否使用管道 260 | uint8_t task_ipc_mode; // timer的通知模式 261 | 262 | /** 263 | * task worker process num 264 | */ 265 | uint16_t task_worker_num; // task worker进程的数量 266 | 267 | uint16_t cpu_num; // cpu的核数 268 | 269 | uint32_t pagesize; // 当前进程的分页大小 270 | uint32_t max_sockets; // 允许的最大socket fd 271 | 272 | /** 273 | * Unix socket default buffer size 274 | */ 275 | uint32_t unixsock_buffer_size; 276 | 277 | swServer *serv; // 指向swServer 278 | swFactory *factory; // 指向swServer里的factory 279 | swLock lock; 280 | 281 | swProcessPool task_workers; // task_workers的进程池 282 | swProcessPool *event_workers;// 用于BASE模式 283 | 284 | swMemoryPool *memory_pool; // 内存池 285 | swReactor *main_reactor; // 全局主reactor,在主进程中只用于接收TCP连接,在worker进程中还需要额外处理管道的事件 286 | 287 | /** 288 | * for swoole_server->taskwait 289 | */ 290 | swPipe *task_notify; 291 | swEventData *task_result; 292 | 293 | pthread_t heartbeat_pidt; // 心跳线程 294 | 295 | } swServerG; 296 | ``` 297 | 该结构体用于存放本地全局变量,这些变量会在任何地方被使用和访问。我在每个属性后加上了注释,标明了每个属性的功能。 298 | 299 | 接下来是swServer_create函数,该函数的实际功能其实是……根据factory_mode创建对应的Factory。该函数定义在Server.c的671 - 678行,如下: 300 | ```c 301 | if (serv->package_eof_len > sizeof(serv->package_eof)) 302 | { 303 | serv->package_eof_len = sizeof(serv->package_eof); 304 | } 305 | 306 | //初始化日志 307 | if (serv->log_file[0] != 0) 308 | { 309 | swLog_init(serv->log_file); 310 | } 311 | 312 | //保存指针到全局变量中去 313 | //TODO 未来全部使用此方式访问swServer/swFactory对象 314 | SwooleG.serv = serv; 315 | SwooleG.factory = &serv->factory; 316 | 317 | //单进程单线程模式 318 | if (serv->factory_mode == SW_MODE_SINGLE) 319 | { 320 | return swReactorProcess_create(serv); 321 | } 322 | else 323 | { 324 | return swReactorThread_create(serv); 325 | } 326 | ``` 327 | 源码解释:如果eof的长度超过了最大长度(8字节),则设置长度为8字节。随后初始化日志,并设置全局变量SwooleG中的serv和factory,接着根据factory_mode调用具体的函数。(swReactorThread_create参考12章) 328 | 329 | 接着是swServer_start函数,该函数用于启动一个swServer。其定义在Server.c文件的456 - 620行,因为函数较长,再此做分段分析。 330 | 331 | ```c 332 | swFactory *factory = &serv->factory; 333 | int ret; 334 | 335 | ret = swServer_start_check(serv); 336 | if (ret < 0) 337 | { 338 | return SW_ERR; 339 | } 340 | 341 | #if SW_WORKER_IPC_MODE == 2 342 | serv->ipc_mode = SW_IPC_MSGQUEUE; 343 | #endif 344 | 345 | if (serv->message_queue_key == 0) 346 | { 347 | char path_buf[128]; 348 | char *path_ptr = getcwd(path_buf, 128); 349 | serv->message_queue_key = ftok(path_ptr, 1) + getpid(); 350 | } 351 | 352 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 353 | { 354 | SwooleG.use_timerfd = 0; 355 | SwooleG.use_signalfd = 0; 356 | SwooleG.use_timer_pipe = 0; 357 | } 358 | 359 | #ifdef SW_USE_OPENSSL 360 | if (serv->open_ssl) 361 | { 362 | if (swSSL_init(serv->ssl_cert_file, serv->ssl_key_file) < 0) 363 | { 364 | return SW_ERR; 365 | } 366 | } 367 | #endif 368 | ``` 369 | 源码解释:首先通过**swServer_start_check**函数检测相关属性是否设置正确。随后设置ipc_mode,如果消息队列key值为0,则生成一个key。如果使用了消息队列,则关闭timerfd,signalfd和timer管道。如果开启了SSL选项,则调用**swSSL_init**初始化SSL设置。 370 | 371 | ```c 372 | //run as daemon 373 | if (serv->daemonize > 0) 374 | { 375 | /** 376 | * redirect STDOUT to log file 377 | */ 378 | if (SwooleG.log_fd > STDOUT_FILENO) 379 | { 380 | if (dup2(SwooleG.log_fd, STDOUT_FILENO) < 0) 381 | { 382 | swWarn("dup2() failed. Error: %s[%d]", strerror(errno), errno); 383 | } 384 | } 385 | /** 386 | * redirect STDOUT_FILENO/STDERR_FILENO to /dev/null 387 | */ 388 | else 389 | { 390 | int null_fd = open("/dev/null", O_WRONLY); 391 | if (null_fd > 0) 392 | { 393 | if (dup2(null_fd, STDOUT_FILENO) < 0) 394 | { 395 | swWarn("dup2(STDOUT_FILENO) failed. Error: %s[%d]", strerror(errno), errno); 396 | } 397 | if (dup2(null_fd, STDERR_FILENO) < 0) 398 | { 399 | swWarn("dup2(STDERR_FILENO) failed. Error: %s[%d]", strerror(errno), errno); 400 | } 401 | } 402 | else 403 | { 404 | swWarn("open(/dev/null) failed. Error: %s[%d]", strerror(errno), errno); 405 | } 406 | } 407 | 408 | if (daemon(0, 1) < 0) 409 | { 410 | return SW_ERR; 411 | } 412 | } 413 | ``` 414 | 源码解释:如果设置为守护进程,如果设置了日志文件,则将输出定向到日志文件中,否则将输入定向到/dev/null中(抹去输出) 415 | 416 | ```c 417 | //master pid 418 | SwooleGS->master_pid = getpid(); 419 | SwooleGS->start = 1; 420 | SwooleGS->now = SwooleStats->start_time = time(NULL); 421 | 422 | serv->reactor_pipe_num = serv->worker_num / serv->reactor_num; 423 | 424 | //设置factory回调函数 425 | serv->factory.ptr = serv; 426 | serv->factory.onTask = serv->onReceive; 427 | 428 | if (serv->have_udp_sock == 1 && serv->factory_mode != SW_MODE_PROCESS) 429 | { 430 | serv->factory.onFinish = swServer_onFinish2; 431 | } 432 | else 433 | { 434 | serv->factory.onFinish = swServer_onFinish; 435 | } 436 | 437 | serv->workers = SwooleG.memory_pool->alloc(SwooleG.memory_pool, serv->worker_num * sizeof(swWorker)); 438 | if (serv->workers == NULL) 439 | { 440 | swWarn("[Master] malloc[object->workers] failed"); 441 | return SW_ERR; 442 | } 443 | 444 | /* 445 | * For swoole_server->taskwait, create notify pipe and result shared memory. 446 | */ 447 | if (SwooleG.task_worker_num > 0 && serv->worker_num > 0) 448 | { 449 | int i; 450 | SwooleG.task_result = sw_shm_calloc(serv->worker_num, sizeof(swEventData)); 451 | SwooleG.task_notify = sw_calloc(serv->worker_num, sizeof(swPipe)); 452 | for (i = 0; i < serv->worker_num; i++) 453 | { 454 | if (swPipeNotify_auto(&SwooleG.task_notify[i], 1, 0)) 455 | { 456 | return SW_ERR; 457 | } 458 | } 459 | } 460 | ``` 461 | 源码解释:设置master进程id为当前进程id,设置start标记并储存启动时间。接下来设置reactor的管道数量,设置factory的onTask回调。如果有UDP监听并且没有使用多进程模式,则设置factory的onFinish回调为swServer_onFinish2回调(当前的swoole模式下基本没用);否则,使用swServer_onFinish回调。接着,在全局内存池中分配worker结构体数组所需要的内存。最后,如果task_worker_num大于0,则在全局变量中为task_result分配共享内存,并创建task_notify管道数组。 462 | 463 | ```c 464 | //factory start 465 | if (factory->start(factory) < 0) 466 | { 467 | return SW_ERR; 468 | } 469 | //Signal Init 470 | swServer_signal_init(); 471 | 472 | //标识为主进程 473 | SwooleG.process_type = SW_PROCESS_MASTER; 474 | 475 | //启动心跳检测 476 | if (serv->heartbeat_check_interval >= 1 && serv->heartbeat_check_interval <= serv->heartbeat_idle_time) 477 | { 478 | swTrace("hb timer start, time: %d live time:%d", serv->heartbeat_check_interval, serv->heartbeat_idle_time); 479 | swServer_heartbeat_start(serv); 480 | } 481 | 482 | if (serv->factory_mode == SW_MODE_SINGLE) 483 | { 484 | ret = swReactorProcess_start(serv); 485 | } 486 | else 487 | { 488 | ret = swServer_start_proxy(serv); 489 | } 490 | 491 | if (ret < 0) 492 | { 493 | SwooleGS->start = 0; 494 | } 495 | 496 | //server stop 497 | if (serv->onShutdown != NULL) 498 | { 499 | serv->onShutdown(serv); 500 | } 501 | swServer_free(serv); 502 | return SW_OK; 503 | ``` 504 | 源码解释:调用factory的start函数启动factory,调用**swServer_signal_init**函数初始化信号回调,并设置当前进程类型为master类型。如果设置了心跳检测,则通过**swServer_heartbeat_start**函数启动心跳检测。如果使用单进程模式,则直接调用**swReactorProcess_start**函数启动。否则,调用**swServer_start_proxy**函数启动Server。Server关闭后,调用onShutdown回调,并释放内存。 505 | 506 | 那么这里需要分析swServer_start_proxy函数。该函数定义了一个proxy模式,在单独的n个线程(进程)中接受维持TCP连接。该函数定义在Server.c文件的405 - 454行,如下: 507 | ```c 508 | int ret; 509 | swReactor *main_reactor = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swReactor)); 510 | 511 | #ifdef SW_MAINREACTOR_USE_POLL 512 | ret = swReactorPoll_create(main_reactor, 10); 513 | #else 514 | ret = swReactorSelect_create(main_reactor); 515 | #endif 516 | 517 | if (ret < 0) 518 | { 519 | swWarn("Reactor create failed"); 520 | return SW_ERR; 521 | } 522 | ret = swReactorThread_start(serv, main_reactor); 523 | if (ret < 0) 524 | { 525 | swWarn("ReactorThread start failed"); 526 | return SW_ERR; 527 | } 528 | SwooleG.main_reactor = main_reactor; 529 | main_reactor->id = serv->reactor_num; //设为一个特别的ID 530 | main_reactor->ptr = serv; 531 | main_reactor->setHandle(main_reactor, SW_FD_LISTEN, swServer_master_onAccept); 532 | 533 | main_reactor->onFinish = swServer_master_onReactorFinish; 534 | main_reactor->onTimeout = swServer_master_onReactorTimeout; 535 | 536 | #ifdef HAVE_SIGNALFD 537 | if (SwooleG.use_signalfd) 538 | { 539 | swSignalfd_setup(main_reactor); 540 | } 541 | #endif 542 | //SW_START_SLEEP; 543 | if (serv->onStart != NULL) 544 | { 545 | serv->onStart(serv); 546 | } 547 | struct timeval tmo; 548 | tmo.tv_sec = SW_MAINREACTOR_TIMEO; 549 | tmo.tv_usec = 0; 550 | 551 | //先更新一次时间 552 | swServer_update_time(); 553 | 554 | return main_reactor->wait(main_reactor, &tmo); 555 | ``` 556 | 源码解释:在全局内存池中创建一个主reactor,调用swReactorThread_start函数启动这个reactor,并设置全局变量SwooleG中的main_reactor为该reactor。接着设置reactor的相关属性和回调函数,设置该reactor监听LISTEN事件并设置回调为**swServer_master_onAccept**。如果serv设置了onStart回调,调用它。随后,调用swServer_update_time函数更新当前时间,然后直接进入reactor的**wait**函数开始监听连接。 557 | 558 | 至此,一个完整的swServer启动流程的相关函数以及分析完成。下一章将分析余下的函数以及相关的回调函数。 559 | 560 | 561 | -------------------------------------------------------------------------------- /12.ReactorThread模块.md: -------------------------------------------------------------------------------- 1 | ####**Swoole版本:1.7.5-stable** 2 | ------------- 3 | 4 | ###**ReactorThread** 5 | 这一章将分析Swoole的ReactorThread模块。虽然叫Thread,但是实际上使用的是swFactoryProcess也就是多进程模式。但是,在ReactorThread中,所有的事件监听是在线程中运行的(Rango只是简单提到了PHP不支持多线程安全,具体原因还有待请教……),比如在UDP模式下,是针对每一个监听的host开辟一个线程运行reactor,在TCP模式下,则是开启指定的reactor_num个线程用于运行reactor。 6 | 7 | 那么OK,先上swReactorThread结构体。该结构体封装的其实是一个运行着Reactor的线程Thread的相关信息,其声明在Server.h文件的**104 – 112**行,如下: 8 | ```c 9 | typedef struct _swReactorThread 10 | { 11 | pthread_t thread_id; 12 | swReactor reactor; 13 | swUdpFd *udp_addrs; 14 | swMemoryPool *buffer_input; 15 | swArray *buffer_pipe; 16 | int c_udp_fd; 17 | } swReactorThread; 18 | ``` 19 | 其中thread_id为ReactorThread的id,udp_addrs和c_udp_fd专门用于处理udp请求,buffer_input为RingBuffer,用于开启了RingBuffer选项的处理,buffer_pipe用于存放来自管道的数据 20 | 21 | 22 | 另一个结构体用来封装需要传递给Thread的参数,其声明在swoole.h的**575 - 579**行,如下: 23 | ```c 24 | typedef struct _swThreadParam 25 | { 26 | void *object; 27 | int pti; 28 | } swThreadParam; 29 | ``` 30 | 第一个void*指针指向了参数内容的地址,第二个参数标记线程的id(不是pid) 31 | 32 | ReactorThread在Server.h中一共有……嗯……12个函数……声明在Server.h的**555 - 568**行。 33 | 这里分下类,其中3个函数用于创建、启动、释放,归为**操作类函数**,7个函数用于回调操作,归为**回调函数**,剩下2个用于发送数据,归为**发送函数**。 34 | 35 | ---- 36 | 首先看3个操作类函数,这三个函数的声明如下: 37 | ```c 38 | int swReactorThread_create(swServer *serv); 39 | int swReactorThread_start(swServer *serv, swReactor *main_reactor_ptr); 40 | void swReactorThread_free(swServer *serv); 41 | ``` 42 | 首先是**swReactorThread_create**函数,该函数实质上并不是创建一个ReactorThread,而是初始化swServer中相关变量,并创建相应的Factory。下面上核心源码: 43 | ```c 44 | serv->reactor_threads = SwooleG.memory_pool->alloc(SwooleG.memory_pool, (serv->reactor_num * sizeof(swReactorThread))); 45 | if (serv->reactor_threads == NULL) 46 | { 47 | swError("calloc[reactor_threads] fail.alloc_size=%d", (int )(serv->reactor_num * sizeof(swReactorThread))); 48 | return SW_ERR; 49 | } 50 | 51 | #ifdef SW_USE_RINGBUFFER 52 | int i; 53 | for (i = 0; i < serv->reactor_num; i++) 54 | { 55 | serv->reactor_threads[i].buffer_input = swRingBuffer_new(SwooleG.serv->buffer_input_size, 1); 56 | if (!serv->reactor_threads[i].buffer_input) 57 | { 58 | return SW_ERR; 59 | } 60 | } 61 | #endif 62 | 63 | serv->connection_list = sw_shm_calloc(serv->max_connection, sizeof(swConnection)); 64 | if (serv->connection_list == NULL) 65 | { 66 | swError("calloc[1] failed"); 67 | return SW_ERR; 68 | } 69 | ``` 70 | 源码解释:初始化运行reactor的线程池,如果指定使用了RingBuffer,则将reactor_threads里的输入缓存区的类型设置为RingBuffer。随后,在共享内存中初始化connectoin_list连接列表的内存空间。 71 | ```c 72 | //create factry object 73 | if (serv->factory_mode == SW_MODE_THREAD) 74 | { 75 | if (serv->writer_num < 1) 76 | { 77 | swError("Fatal Error: serv->writer_num < 1"); 78 | return SW_ERR; 79 | } 80 | ret = swFactoryThread_create(&(serv->factory), serv->writer_num); 81 | } 82 | else if (serv->factory_mode == SW_MODE_PROCESS) 83 | { 84 | if (serv->writer_num < 1 || serv->worker_num < 1) 85 | { 86 | swError("Fatal Error: serv->writer_num < 1 or serv->worker_num < 1"); 87 | return SW_ERR; 88 | } 89 | ret = swFactoryProcess_create(&(serv->factory), serv->writer_num, serv->worker_num); 90 | } 91 | else 92 | { 93 | ret = swFactory_create(&(serv->factory)); 94 | } 95 | ``` 96 | 源码解释:判断swServer的factory_mode。如果为**SW_MODE_THREAD**(线程模式),则创建FactoryThread;如果为**SW_MODE_PROCESS**(进程模式),则创建FactoryProcess;否则,为**SW_MODE_BASE**(基础模式),创建Factory。 97 | 98 | 创建完后,就需要启动了。**swReactorThread_start**函数倒是真的用于启动ReactorThread了……核心源码如下: 99 | ```c 100 | if (serv->have_udp_sock == 1) 101 | { 102 | if (swUDPThread_start(serv) < 0) 103 | { 104 | swError("udp thread start failed."); 105 | return SW_ERR; 106 | } 107 | } 108 | 109 | //listen TCP 110 | if (serv->have_tcp_sock == 1) 111 | { 112 | //listen server socket 113 | ret = swServer_listen(serv, main_reactor_ptr); 114 | if (ret < 0) 115 | { 116 | return SW_ERR; 117 | } 118 | //create reactor thread 119 | for (i = 0; i < serv->reactor_num; i++) 120 | { 121 | thread = &(serv->reactor_threads[i]); 122 | param = SwooleG.memory_pool->alloc(SwooleG.memory_pool, sizeof(swThreadParam)); 123 | if (param == NULL) 124 | { 125 | swError("malloc failed"); 126 | return SW_ERR; 127 | } 128 | 129 | param->object = serv; 130 | param->pti = i; 131 | 132 | if (pthread_create(&pidt, NULL, (void * (*)(void *)) swReactorThread_loop_tcp, (void *) param) < 0) 133 | { 134 | swError("pthread_create[tcp_reactor] failed. Error: %s[%d]", strerror(errno), errno); 135 | } 136 | thread->thread_id = pidt; 137 | } 138 | } 139 | 140 | //timer 141 | if (SwooleG.timer.fd > 0) 142 | { 143 | main_reactor_ptr->add(main_reactor_ptr, SwooleG.timer.fd, SW_FD_TIMER); 144 | } 145 | ``` 146 | 源码解释:如果swServer需要监听UDP,则调用**swUDPThread_start**函数启动UDP监听线程;如果swServer需要监听TCP,首先调用**swServer_listen**函数在main_reactor中注册accept监听,然后创建**reactor_num**个reactor运行线程用于监听TCP连接的其他事件(读、写)。最后,如果使用了Timer,则将Timer的fd监听加入到main_reactor中。 147 | 148 | **swReactorThread_free**函数用于释放全部的正在运行的线程以及相关资源,核心源码如下: 149 | ```c 150 | if (serv->have_tcp_sock == 1) 151 | { 152 | //create reactor thread 153 | for (i = 0; i < serv->reactor_num; i++) 154 | { 155 | thread = &(serv->reactor_threads[i]); 156 | if (pthread_join(thread->thread_id, NULL)) 157 | { 158 | swWarn("pthread_join() failed. Error: %s[%d]", strerror(errno), errno); 159 | } 160 | 161 | for (j = 0; j < serv->worker_num; j++) 162 | { 163 | swWorker *worker = swServer_get_worker(serv, i); 164 | swBuffer *buffer = *(swBuffer **) swArray_fetch(thread->buffer_pipe, worker->pipe_master); 165 | swBuffer_free(buffer); 166 | } 167 | swArray_free(thread->buffer_pipe); 168 | 169 | #ifdef SW_USE_RINGBUFFER 170 | thread->buffer_input->destroy(thread->buffer_input); 171 | #endif 172 | } 173 | } 174 | 175 | if (serv->have_udp_sock == 1) 176 | { 177 | swListenList_node *listen_host; 178 | LL_FOREACH(serv->listen_list, listen_host) 179 | { 180 | shutdown(listen_host->sock, SHUT_RDWR); 181 | if (listen_host->type == SW_SOCK_UDP || listen_host->type == SW_SOCK_UDP6 || listen_host->type == SW_SOCK_UNIX_DGRAM) 182 | { 183 | if (pthread_join(listen_host->thread_id, NULL)) 184 | { 185 | swWarn("pthread_join() failed. Error: %s[%d]", strerror(errno), errno); 186 | } 187 | } 188 | } 189 | } 190 | ``` 191 | 源码解释:如果使用了TCP,则遍历全部的reactor_thread,并调用pthread_join函数结束线程,并释放线程中用于管道通信的缓存区。如果使用了RingBuffer,还需要释放buffer_input输入缓存。如果使用了UDP,则首先遍历监听列表,使用shutdown终止连接,然后调用pthread_join函数结束线程。 192 | 193 | ---- 194 | 接着先看发送函数。一共两个发送函数,一个用于发送数据到client客户端或者输出buffer,一个用于发送数据到worker进程。两个函数的声明如下: 195 | ```c 196 | int swReactorThread_send(swSendData *_send); 197 | int swReactorThread_send2worker(void *data, int len, uint16_t target_worker_id); 198 | ``` 199 | 200 | **swReactorThread_send**函数用于发送数据到客户端,也就是通过swConnection发送数据,其核心源码如下: 201 | ```c 202 | volatile swBuffer_trunk *trunk; 203 | swConnection *conn = swServer_connection_get(serv, fd); 204 | 205 | if (conn == NULL || conn->active == 0) 206 | { 207 | swWarn("Connection[fd=%d] is not exists.", fd); 208 | return SW_ERR; 209 | } 210 | 211 | #if SW_REACTOR_SCHEDULE == 2 212 | reactor_id = fd % serv->reactor_num; 213 | #else 214 | reactor_id = conn->from_id; 215 | #endif 216 | 217 | swTraceLog(SW_TRACE_EVENT, "send-data. fd=%d|reactor_id=%d", fd, reactor_id); 218 | swReactor *reactor = &(serv->reactor_threads[reactor_id].reactor); 219 | ``` 220 | 源码解释:如果不是直传,则需要现将数据放入缓存。首先创建connection的out_buffer输出缓存,如果发送数据长度为0,则指定缓存的trunk类型为**SW_TRUNK_CLOSE**(关闭连接),如果发送数据的类型为sendfile,则调用**swConnection_sendfile**函数,否则调用**swBuffer_append**函数将发送数据加入缓存中。最后,在reactor中设置fd为可写状态。 221 | 222 | swReactorThread_send2worker函数在此不再贴源码分析。基本思路就是消息队列模式就扔队列不是消息队列模式就扔缓存或者直接扔管道……应该都看得懂了。 223 | 224 | 这里直接上数量最多的回调函数的分析。这几个回调式用于处理接收数据的,从名字上大家基本能看出,Swoole提供的一些特性比如包长检测、eof检测还有UDP的报文接收都是通过这些不同的回调来实现的。 225 | 226 | 首先来看**swReactorThread_onReceive_no_buffer**函数,这个是最基本的接收函数,没有缓存,没有检测,收到多少数据就发给worker多少数据。下面上核心源码: 227 | ```c 228 | #ifdef SW_USE_EPOLLET 229 | n = swRead(event->fd, task.data.data, SW_BUFFER_SIZE); 230 | #else 231 | //非ET模式会持续通知 232 | n = swConnection_recv(conn, task.data.data, SW_BUFFER_SIZE, 0); 233 | #endif 234 | 235 | if (n < 0) 236 | { 237 | switch (swConnection_error(errno)) 238 | { 239 | case SW_ERROR: 240 | swWarn("recv from connection[fd=%d] failed. Error: %s[%d]", event->fd, strerror(errno), errno); 241 | return SW_OK; 242 | case SW_CLOSE: 243 | goto close_fd; 244 | default: 245 | return SW_OK; 246 | } 247 | } 248 | //需要检测errno来区分是EAGAIN还是ECONNRESET 249 | else if (n == 0) 250 | { 251 | close_fd: 252 | swTrace("Close Event.FD=%d|From=%d|errno=%d", event->fd, event->from_id, errno); 253 | swServer_connection_close(serv, event->fd, 1); 254 | /** 255 | * skip EPOLLERR 256 | */ 257 | event->fd = 0; 258 | return SW_OK; 259 | } 260 | ``` 261 | 源码解释:如果使用epoll的ET模式,则调用**swRead**函数直接从fd中读取数据;否则,在非ET模式下,调用**swConnection_recv**函数接收数据。如果接收数据失败,则根据errno执行对应的操作,如果接收数据为0,需要关闭连接,调用**swServer_connection_close**函数关闭fd。 262 | ```c 263 | conn->last_time = SwooleGS->now; 264 | 265 | //heartbeat ping package 266 | if (serv->heartbeat_ping_length == n) 267 | { 268 | if (serv->heartbeat_pong_length > 0) 269 | { 270 | send(event->fd, serv->heartbeat_pong, serv->heartbeat_pong_length, 0); 271 | } 272 | return SW_OK; 273 | } 274 | 275 | task.data.info.fd = event->fd; 276 | task.data.info.from_id = event->from_id; 277 | task.data.info.len = n; 278 | 279 | #ifdef SW_USE_RINGBUFFER 280 | 281 | uint16_t target_worker_id = swServer_worker_schedule(serv, conn->fd); 282 | swPackage package; 283 | 284 | package.length = task.data.info.len; 285 | package.data = swReactorThread_alloc(&serv->reactor_threads[SwooleTG.id], package.length); 286 | task.data.info.type = SW_EVENT_PACKAGE; 287 | 288 | memcpy(package.data, task.data.data, task.data.info.len); 289 | task.data.info.len = sizeof(package); 290 | task.target_worker_id = target_worker_id; 291 | memcpy(task.data.data, &package, sizeof(package)); 292 | 293 | #else 294 | task.data.info.type = SW_EVENT_TCP; 295 | task.target_worker_id = -1; 296 | #endif 297 | 298 | //dispatch to worker process 299 | ret = factory->dispatch(factory, &task); 300 | 301 | #ifdef SW_USE_EPOLLET 302 | //缓存区还有数据没读完,继续读,EPOLL的ET模式 303 | if (sw_errno == EAGAIN) 304 | { 305 | swWarn("sw_errno == EAGAIN"); 306 | ret = swReactorThread_onReceive_no_buffer(reactor, event); 307 | } 308 | #endif 309 | ``` 310 | 源码解释:首先更新最近收包的时间。随后,检测是否是心跳包,如果接收长度等于心跳包的长度并且指定了发送心跳回应,则发送心跳包并返回。如果不是心跳包,则设置接收数据的fd、reactor_id以及长度。如果指定使用了RingBuffer,则需要将数据封装到swPackage中然后放进ReactorThread的input_buffer中。随后调用factory的**dispatch**方法将数据投递到对应的worker中。最后,如果是LT模式,并且缓存区的数据还没读完,则继续调用**swReactorThread_onReceive_no_buffer**函数读取数据。 311 | 312 | 接下来是**swReactorThread_onReceive_buffer_check_length**函数,该函数用于接收开启了包长检测的数据包。包长检测是Swoole用于支持固定包头+包体的自定义协议的特性,当然有不少小伙伴不理解怎么使用这个特性……下面上核心源码: 313 | ```c 314 | //new package 315 | if (conn->object == NULL) 316 | { 317 | do_parse_package: 318 | do 319 | { 320 | package_total_length = swReactorThread_get_package_length(serv, (void *)tmp_ptr, (uint32_t) tmp_n); 321 | 322 | //Invalid package, close connection 323 | if (package_total_length < 0) 324 | { 325 | goto close_fd; 326 | } 327 | //no package_length 328 | else if(package_total_length == 0) 329 | { 330 | char recv_buf_again[SW_BUFFER_SIZE]; 331 | memcpy(recv_buf_again, (void *) tmp_ptr, (uint32_t) tmp_n); 332 | do 333 | { 334 | //前tmp_n个字节存放不完整包头 335 | n = recv(event->fd, (void *)recv_buf_again + tmp_n, SW_BUFFER_SIZE, 0); 336 | try_count ++; 337 | 338 | //连续5次尝试补齐包头,认定为恶意请求 339 | if (try_count > 5) 340 | { 341 | swWarn("No package head. Close connection."); 342 | goto close_fd; 343 | } 344 | } 345 | while(n < 0 && errno == EINTR); 346 | 347 | if (n == 0) 348 | { 349 | goto close_fd; 350 | } 351 | tmp_ptr = recv_buf_again; 352 | tmp_n = tmp_n + n; 353 | 354 | goto do_parse_package; 355 | } 356 | //complete package 357 | if (package_total_length <= tmp_n) 358 | { 359 | tmp_package.size = package_total_length; 360 | tmp_package.length = package_total_length; 361 | tmp_package.str = (void *) tmp_ptr; 362 | 363 | //swoole_dump_bin(buffer.str, 's', buffer.length); 364 | swReactorThread_send_string_buffer(swServer_get_thread(serv, SwooleTG.id), conn, &tmp_package); 365 | 366 | tmp_ptr += package_total_length; 367 | tmp_n -= package_total_length; 368 | continue; 369 | } 370 | //wait more data 371 | else 372 | { 373 | if (package_total_length >= serv->package_max_length) 374 | { 375 | swWarn("Package length more than the maximum size[%d], Close connection.", serv->package_max_length); 376 | goto close_fd; 377 | } 378 | package = swString_new(package_total_length); 379 | if (package == NULL) 380 | { 381 | return SW_ERR; 382 | } 383 | memcpy(package->str, (void *)tmp_ptr, (uint32_t) tmp_n); 384 | package->length += tmp_n; 385 | conn->object = (void *) package; 386 | break; 387 | } 388 | } 389 | while(tmp_n > 0); 390 | return SW_OK; 391 | } 392 | ``` 393 | 源码解释:如果connection连接中没有缓存数据,则判定为新的数据包,进入接收循环。在接收循环中,首先从数据缓存中尝试获取包体的长度(这个长度存在包头中),如果长度小于0,说明这个数据包不合法,丢弃并关闭连接;如果长度等于0,说明包头还没接收完整,继续接收数据,如果连续5次补全包头失败,则认定为恶意请求,关闭连接;如果长度大于0并且已经接收的数据长度超过或等于包体长度,则说明已经收到一个完整的数据包,通过**swReactorThread_send_string_buffer**函数将数据包放入缓存;如果已接收数据长度小于包体长度,则将不完整的数据包放入connection的object域,等待下一批数据。 394 | ```c 395 | else 396 | { 397 | package = conn->object; 398 | //swTraceLog(40, "wait_data, size=%d, length=%d", buffer->size, buffer->length); 399 | 400 | /** 401 | * Also on the require_n byte data is complete. 402 | */ 403 | int require_n = package->size - package->length; 404 | 405 | /** 406 | * Data is not complete, continue to wait 407 | */ 408 | if (require_n > n) 409 | { 410 | memcpy(package->str + package->length, recv_buf, n); 411 | package->length += n; 412 | return SW_OK; 413 | } 414 | else 415 | { 416 | memcpy(package->str + package->length, recv_buf, require_n); 417 | package->length += require_n; 418 | swReactorThread_send_string_buffer(swServer_get_thread(serv, SwooleTG.id), conn, package); 419 | swString_free((swString *) package); 420 | conn->object = NULL; 421 | 422 | /** 423 | * Still have the data, to parse. 424 | */ 425 | if (n - require_n > 0) 426 | { 427 | tmp_n = n - require_n; 428 | tmp_ptr = recv_buf + require_n; 429 | goto do_parse_package; 430 | } 431 | } 432 | } 433 | ``` 434 | 源码解释:如果connecton的object已经存有数据,则先判断这个数据包还剩下多少个字节未接受,并用当前接收的数据补全数据包,如果数据不够,则继续等待下一批数据;如果数据够,则补全数据包并将数据包发送到缓存中,并清空connection的object域。如果在补全数据包后仍有剩余数据,则开始下一次数据包的解析。 435 | 436 | 接下来分析**swReactorThread_onReceive_buffer_check_eof**函数。这个函数用于检测用户定义的数据包的分割符,用于解决TCP长连接发送数据的粘包问题,保证onReceive回调每次拿到的是一个完整的数据包。下面是核心源码: 437 | ```c 438 | //读满buffer了,可能还有数据 439 | if ((buffer->trunk_size - trunk->length) == n) 440 | { 441 | recv_again = SW_TRUE; 442 | } 443 | 444 | trunk->length += n; 445 | buffer->length += n; 446 | 447 | //over max length, will discard 448 | //TODO write to tmp file. 449 | if (buffer->length > serv->package_max_length) 450 | { 451 | swWarn("Package is too big. package_length=%d", buffer->length); 452 | goto close_fd; 453 | } 454 | 455 | // printf("buffer[len=%d][n=%d]-----------------\n", trunk->length, n); 456 | //((char *)trunk->data)[trunk->length] = 0; //for printf 457 | // printf("buffer-----------------: %s|fd=%d|len=%d\n", (char *) trunk->data, event->fd, trunk->length); 458 | 459 | //EOF_Check 460 | isEOF = memcmp(trunk->store.ptr + trunk->length - serv->package_eof_len, serv->package_eof, serv->package_eof_len); 461 | // printf("buffer ok. EOF=%s|Len=%d|RecvEOF=%s|isEOF=%d\n", serv->package_eof, serv->package_eof_len, (char *)trunk->data + trunk->length - serv->package_eof_len, isEOF); 462 | 463 | //received EOF, will send package to worker 464 | if (isEOF == 0) 465 | { 466 | swReactorThread_send_in_buffer(swServer_get_thread(serv, SwooleTG.id), conn); 467 | return SW_OK; 468 | } 469 | else if (recv_again) 470 | { 471 | trunk = swBuffer_new_trunk(buffer, SW_TRUNK_DATA, buffer->trunk_size); 472 | if (trunk) 473 | { 474 | goto recv_data; 475 | } 476 | } 477 | ``` 478 | 源码解释:这里使用了connection的in_buffer输入缓存。首先判定trunk的剩余空间,如果trunk已经满了,此时可能还有数据,则需要新开一个trunk接收数据,因此设定recv_again标签为true。随后判定已经接收的数据长度是否超过了最大包长,如果超过,则丢弃数据包(这里可以看到TODO标签,Rango将在后期把超过包长的数据写入tmp文件中)。随后判定EOF,将数据末尾的长度为eof_len的字符串和指定的eof字符串对比,如果相等,则将数据包发送到缓存区,如果不相等,而recv_again标签为true,则新开trunk用于接收数据。 479 | 480 | 这里需要说明,Rango只是简单判定了数据包末尾是否为eof,而不是在已经接收到的字符串中去匹配eof,因此并不能很准确的根据eof来拆分数据包。所以各位如果希望能准确解决粘包问题,还是使用固定包头+包体这种协议格式较好。 481 | 482 | 剩下的几个回调函数都较为简单,有兴趣的读者可以根据之前的分析自己尝试解读一下这几个函数。 483 | 484 | 至此,ReactorThread模块已全部解析完成。可以看出,ReactorThread模块主要在处理连接接收到的数据,并把这些数据投递到对应的缓存中交由Worker去处理。因此可以理出一个基本的结构: 485 | Reactor响应fd请求->ReactorThread接收数据并放入缓存->ReactorFactory将数据从缓存取出发送给Worker->Worker处理数据。 -------------------------------------------------------------------------------- /15.Timer模块分析.md: -------------------------------------------------------------------------------- 1 | # Timer模块分析 2 | --- 3 | #Table of Contents 4 | - [1.Timer]() 5 | - [1.1.swTimer_interval_node结构体](#11swtimer_interval_node) 6 | - [1.2.swTimer_node结构体](#12swtimer_node) 7 | - [1.3.swTimer结构体](#13swtimer) 8 | - [1.4.swTimer公共操作函数](#14swtimer%E5%85%AC%E5%85%B1%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0) 9 | - [1.4.1.swTimer_init](#141swTimer_init) 10 | - [1.4.2.swTimer_signal_handler](#142swTimer_signal_handler) 11 | - [1.4.3.swTimer_event_handler](#143swTimer_event_handler) 12 | - [1.4.4.其他函数](#144%E5%85%B6%E4%BB%96%E5%87%BD%E6%95%B0) 13 | - [1.5.Timer私有操作函数](#15timer%E7%A7%81%E6%9C%89%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0) 14 | - [1.5.1.swTimer_signal_set](#151swTimer_signal_set) 15 | - [1.5.2.swTimer_timerfd_set](#152swTimer_timerfd_set) 16 | - [1.5.3.swTimer_del](#153swTimer_del) 17 | - [1.5.4.swTimer_free](#154swTimer_free) 18 | - [1.5.5.swTimer_add](#155swTimer_add) 19 | - [1.5.6.swTimer_set](#156swTimer_set) 20 | - [1.5.7.swTimer_addtimeout](#157swTimer_addtimeout) 21 | - [1.5.8.swTimer_select](#158swTimer_select) 22 | - [2.EventTimer](#2eventtimer) 23 | - [2.1.EventTimer原理](#21eventtimer%E5%8E%9F%E7%90%86) 24 | - [2.2.EventTimer私有操作函数](22eventtimer%E7%A7%81%E6%9C%89%E6%93%8D%E4%BD%9C%E5%87%BD%E6%95%B0) 25 | - [2.2.1.swEventTimer_add](#221swEventTimer_add) 26 | - [2.2.1.swEventTimer_del](#221swEventTimer_del) 27 | - [2.2.1.swEventTimer_select](#221swEventTimer_select) 28 | - [2.2.1.swEventTimer_free](#221swEventTimer_free) 29 | 30 | --- 31 | **swoole版本:1.7.7-stable** 32 | **Github地址:[点此查看](https://github.com/LinkedDestiny/swoole-src-analysis/blob/master/15.Timer%E6%A8%A1%E5%9D%97%E5%88%86%E6%9E%90.md)** 33 | 34 | --- 35 | ##**1.Timer** 36 | ###**1.1.swTimer_interval_node** 37 | 声明: 38 | ```c 39 | // swoole.h 1045-1050h 40 | typedef struct _swTimer_interval_node 41 | { 42 | struct _swTimerList_node *next, *prev; 43 | struct timeval lasttime; 44 | uint32_t interval; 45 | } swTimer_interval_node; 46 | ``` 47 | 48 | | 成员 | 说明 | 49 | | -------- | ------ | 50 | | next,prev | 链表的后继、前驱指针 | 51 | | struct timeval lasttime | 持续时间 | 52 | | uint32_t interval | 间隔时间 | 53 | 54 | 说明:
55 | **swTimer_interval_node**结构体是一个链表节点,存放一个固定间隔的定时器,其中lasttime为当前定时器从上一次执行到现在经过的时间,interval存放了定时器间隔。
56 | 该结构体用于swoole原本的timer相关操作。
57 | 58 | ###**1.2.swTimer_node** 59 | 声明: 60 | ```c 61 | // swoole.h 1052-1058h 62 | typedef struct _swTimer_node 63 | { 64 | struct _swTimer_node *next, *prev; 65 | void *data; 66 | uint32_t exec_msec; 67 | uint32_t interval; 68 | } swTimer_node; 69 | ``` 70 | 71 | | 成员 | 说明 | 72 | | -------- | ------ | 73 | | next,prev | 链表的后继、前驱指针 | 74 | | void *data | 数据域,存放额外的变量 | 75 | | uint32_t exec_msec | 定时器应当执行的时间 | 76 | | uint32_t interval | 间隔时间(无用,应废弃) | 77 | 78 | 说明:
79 | **swTimer_node**结构体是一个链表节点,存放一个需要在指定时间执行的定时器,其中exec_msec为当前定时器需要执行的指定时间,interval存放了定时器间隔。
80 | 该结构体用于swoole的after函数操作。
81 | 82 | ###**1.3.swTimer** 83 | 声明:
84 | ```c 85 | // swoole.h 1060-1081h 86 | typedef struct _swTimer 87 | { 88 | swTimer_node *root; 89 | /*--------------timerfd & signal timer--------------*/ 90 | swHashMap *list; 91 | int num; 92 | int interval; 93 | int use_pipe; 94 | int lasttime; 95 | int fd; 96 | swPipe pipe; 97 | /*-----------------for EventTimer-------------------*/ 98 | struct timeval basetime; 99 | /*--------------------------------------------------*/ 100 | int (*add)(struct _swTimer *timer, int _msec, int _interval, void *data); 101 | int (*del)(struct _swTimer *timer, int _interval_ms); 102 | int (*select)(struct _swTimer *timer); 103 | void (*free)(struct _swTimer *timer); 104 | /*-----------------event callback-------------------*/ 105 | void (*onTimer)(struct _swTimer *timer, int interval_msec); 106 | void (*onTimeout)(struct _swTimer *timer, void *data); 107 | } swTimer; 108 | ``` 109 | 110 | | 成员 | 说明 | 111 | | -------- | ------ | 112 | | swTimer_node *root | after的链表根节点 | 113 | | swHashMap *list | timer的链表根节点 | 114 | | int num | 当前定时器的数量 | 115 | | int interval | 定时器的基础响应间隔 | 116 | | int use_pipe | 是否使用管道通信 | 117 | | int lasttime | 持续时间(已废弃) | 118 | | int fd | 管道的写fd | 119 | | swPipe pipe | 管道 | 120 | | struct timeval basetime | 管道 | 121 | 122 | 说明:
123 | **swTimer**结构体定时器的实体对象,用于存储、管理和执行众多定时任务,包括timer和after两种不同类型的定时任务。
124 | 125 | ###**1.4.swTimer公共操作函数** 126 | ####**1.4.1.swTimer_init** 127 | 声明: 128 | ```c 129 | // swoole.h 1083 130 | int swTimer_init(int interval_ms, int no_pipe); 131 | ``` 132 | 功能:初始化一个swTimer对象
133 | 核心源码:
134 | ```c 135 | // timer.c 38-94h 136 | swTimer *timer = &SwooleG.timer; 137 | timer->interval = interval; 138 | timer->lasttime = interval; 139 | 140 | #ifndef HAVE_TIMERFD 141 | SwooleG.use_timerfd = 0; 142 | #endif 143 | 144 | timer->list = swHashMap_new(SW_HASHMAP_INIT_BUCKET_N, free); 145 | if (!timer->list) 146 | { 147 | return SW_ERR; 148 | } 149 | 150 | if (SwooleG.use_timerfd) 151 | { 152 | if (swTimer_timerfd_set(timer, interval) < 0) 153 | { 154 | return SW_ERR; 155 | } 156 | timer->use_pipe = 0; 157 | } 158 | else 159 | { 160 | if (use_pipe) 161 | { 162 | if (swPipeNotify_auto(&timer->pipe, 0, 0) < 0) 163 | { 164 | return SW_ERR; 165 | } 166 | timer->fd = timer->pipe.getFd(&timer->pipe, 0); 167 | timer->use_pipe = 1; 168 | } 169 | else 170 | { 171 | timer->fd = 1; 172 | timer->use_pipe = 0; 173 | } 174 | 175 | if (swTimer_signal_set(timer, interval) < 0) 176 | { 177 | return SW_ERR; 178 | } 179 | swSignal_add(SIGALRM, swTimer_signal_handler); 180 | } 181 | 182 | if (timer->fd > 1) 183 | { 184 | SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_TIMER, swTimer_event_handler); 185 | SwooleG.main_reactor->add(SwooleG.main_reactor, SwooleG.timer.fd, SW_FD_TIMER); 186 | } 187 | 188 | timer->add = swTimer_add; 189 | timer->del = swTimer_del; 190 | timer->select = swTimer_select; 191 | timer->free = swTimer_free; 192 | ``` 193 | 源码解释:
194 | 获取**SwooleG**中的timer对象,设置timer响应间隔和lasttime参数。如果没有定义**HAVE_TIMERFD**,则设置不使用timerfd。随后,使用HashMap初始化timer链表list。
195 | 如果使用了timerfd,调用[swTimer_timerfd_set](#152swTimer_timerfd_set)函数设置timer的基础响应间隔,并设置不使用管道。
196 | 如果不使用timerfd而使用signalfd,则先判定是否需要管道,如果需要,则创建管道并获取管道的写fd。随后,调用[swTimer_signal_set](#151swTimer_signal_set)函数设置Linux系统提供的精确定时器,并通过swSignal_add添加对SIGALRM信号的处理回调函数[swTimer_signal_handler](#142swTimer_signal_handler)。
197 | 接着,将管道写fd加入main_reactor的监听中,并设置回调函数[swTimer_event_handler](#143swTimer_event_handler)。
198 | 最后设置swTimer的四个回调操作函数。
199 | 200 | ####**1.4.2.swTimer_signal_handler** 201 | 声明: 202 | ```c 203 | // swoole.h 1085 204 | void swTimer_signal_handler(int sig); 205 | ``` 206 | 功能:SIGALRM信号的回调处理函数
207 | 核心源码:
208 | ```c 209 | // timer.c 338-344h 210 | SwooleG.signal_alarm = 1; 211 | uint64_t flag = 1; 212 | 213 | if (SwooleG.timer.use_pipe) 214 | { 215 | SwooleG.timer.pipe.write(&SwooleG.timer.pipe, &flag, sizeof(flag)); 216 | } 217 | ``` 218 | 源码解释:
219 | 设置**SwooleG**的signal_alarm标记为true,如果设定使用了管道,则通过管道发送一个flag通知Timer。 220 | 221 | ####**1.4.3.swTimer_event_handler** 222 | 声明: 223 | ```c 224 | // swoole.h 1086 225 | int swTimer_event_handler(swReactor *reactor, swEvent *event); 226 | ``` 227 | 功能:timer的事件处理回调函数
228 | 核心源码:
229 | ```c 230 | // timer.c 323-334h 231 | uint64_t exp; 232 | swTimer *timer = &SwooleG.timer; 233 | 234 | if (read(timer->fd, &exp, sizeof(uint64_t)) < 0) 235 | { 236 | return SW_ERR; 237 | } 238 | SwooleG.signal_alarm = 0; 239 | return swTimer_select(timer); 240 | ``` 241 | 源码解释:
242 | 尝试从管道中读取数据,如果读取成功,则重置**SwooleG**的signal_alarm标记,并调用[swTimer_select](#158swTimer_select)来处理定时任务; 243 | 244 | ####**1.4.4.其他函数** 245 | **swTimer_node_insert**,**swTimer_node_print**,**swTimer_node_delete**,**swTimer_node_destory**四个函数是链表操作函数,不再详细分析。 246 | 247 | ###**1.5.Timer私有操作函数** 248 | ####**1.5.1.swTimer_signal_set** 249 | 声明: 250 | ```c 251 | // timer.c 24h 252 | static int swTimer_signal_set(swTimer *timer, int interval); 253 | ``` 254 | 功能:调用系统settimer函数启动定时器
255 | 核心源码:
256 | ```c 257 | struct itimerval timer_set; 258 | int sec = interval / 1000; 259 | int msec = (((float) interval / 1000) - sec) * 1000; 260 | 261 | struct timeval now; 262 | if (gettimeofday(&now, NULL) < 0) 263 | { 264 | swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno); 265 | return SW_ERR; 266 | } 267 | 268 | memset(&timer_set, 0, sizeof(timer_set)); 269 | timer_set.it_interval.tv_sec = sec; 270 | timer_set.it_interval.tv_usec = msec * 1000; 271 | 272 | timer_set.it_value.tv_sec = sec; 273 | timer_set.it_value.tv_usec = timer_set.it_interval.tv_usec; 274 | 275 | if (timer_set.it_value.tv_usec > 1e6) 276 | { 277 | timer_set.it_value.tv_usec = timer_set.it_value.tv_usec - 1e6; 278 | timer_set.it_value.tv_sec += 1; 279 | } 280 | 281 | if (setitimer(ITIMER_REAL, &timer_set, NULL) < 0) 282 | { 283 | swWarn("setitimer() failed. Error: %s[%d]", strerror(errno), errno); 284 | return SW_ERR; 285 | } 286 | ``` 287 | 源码解释:
288 | 首先将interval拆分成秒和毫秒,并将两个值添加进timer_set,随后调用[setitimer](http://linux.die.net/man/2/setitimer)函数设置系统定时器。 289 | 290 | ####**1.5.2.swTimer_timerfd_set** 291 | 声明: 292 | ```c 293 | // timer.c 25h 294 | static int swTimer_timerfd_set(swTimer *timer, int interval); 295 | ``` 296 | 功能:调用timerfd相关函数启动timerfd定时器
297 | 核心源码:
298 | ```c 299 | // timer.c 100h 300 | if (timer->fd == 0) 301 | { 302 | timer->fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC); 303 | if (timer->fd < 0) 304 | { 305 | swWarn("timerfd_create() failed. Error: %s[%d]", strerror(errno), errno); 306 | return SW_ERR; 307 | } 308 | } 309 | 310 | timer_set.it_interval.tv_sec = sec; 311 | timer_set.it_interval.tv_nsec = msec * 1000 * 1000; 312 | 313 | timer_set.it_value.tv_sec = now.tv_sec + sec; 314 | timer_set.it_value.tv_nsec = (now.tv_usec * 1000) + timer_set.it_interval.tv_nsec; 315 | 316 | if (timer_set.it_value.tv_nsec > 1e9) 317 | { 318 | timer_set.it_value.tv_nsec = timer_set.it_value.tv_nsec - 1e9; 319 | timer_set.it_value.tv_sec += 1; 320 | } 321 | 322 | if (timerfd_settime(timer->fd, TFD_TIMER_ABSTIME, &timer_set, NULL) == -1) 323 | { 324 | swWarn("timerfd_settime() failed. Error: %s[%d]", strerror(errno), errno); 325 | return SW_ERR; 326 | } 327 | ``` 328 | 源码解释:
329 | 调用**timerfd_create**函数创建一个timerfd,并将返回的fd赋值给timer.fd;随后设置timer_set的值,并调用**timerfd_settime**函数设置定时器相关属性。 330 | 331 | ####**1.5.3.swTimer_del** 332 | 声明: 333 | ```c 334 | // timer.c 26h 335 | static int swTimer_del(swTimer *timer, int ms); 336 | ``` 337 | 功能:从timer的列表中移除一个指定定时器
338 | 核心源码:
339 | ```c 340 | swHashMap_del_int(timer->list, ms); 341 | return SW_OK; 342 | ``` 343 | 源码解释:
344 | 从timer的list中移除ms对应的定时器 345 | 346 | ####**1.5.4.swTimer_free** 347 | 声明: 348 | ```c 349 | // timer.c 27h 350 | static void swTimer_free(swTimer *timer); 351 | ``` 352 | 功能:释放timer的内存
353 | 核心源码:
354 | ```c 355 | swHashMap_free(timer->list); 356 | if (timer->use_pipe) 357 | { 358 | timer->pipe.close(&timer->pipe); 359 | } 360 | else if (close(timer->fd) < 0) 361 | { 362 | swSysError("close(%d) failed.", timer->fd); 363 | } 364 | if (timer->root) 365 | { 366 | swTimer_node_destory(&timer->root); 367 | } 368 | ``` 369 | 源码解释:
370 | 释放list,关闭管道,释放root指向的链表 371 | 372 | ####**1.5.5.swTimer_add** 373 | 声明: 374 | ```c 375 | // timer.c 28h 376 | static int swTimer_add(swTimer *timer, int msec, int interval, void *data); 377 | ``` 378 | 功能:向timer中添加一个定时器
379 | 核心源码:
380 | ```c 381 | if (interval == 0) 382 | { 383 | return swTimer_addtimeout(timer, msec, data); 384 | } 385 | swTimer_interval_node *node = sw_malloc(sizeof(swTimer_interval_node)); 386 | if (node == NULL) 387 | { 388 | swWarn("malloc failed."); 389 | return SW_ERR; 390 | } 391 | 392 | bzero(node, sizeof(swTimer_interval_node)); 393 | node->interval = msec; 394 | if (gettimeofday(&node->lasttime, NULL) < 0) 395 | { 396 | swSysError("gettimeofday() failed."); 397 | return SW_ERR; 398 | } 399 | 400 | if (msec < timer->interval) 401 | { 402 | int new_interval = swoole_common_divisor(msec, timer->interval); 403 | timer->interval = new_interval; 404 | swTimer_set(timer, new_interval); 405 | } 406 | swHashMap_add_int(timer->list, msec, node, NULL); 407 | timer->num++; 408 | ``` 409 | 源码解释:
410 | 如果interval为0,说明这个定时器是个timeout类型定时器,调用[swTimer_addtimeout](#157swTimer_addtimeout)函数。
411 | 否则,创建一个**swTimer_interval_node**结构体,设置其相关属性,并根据interval计算timer的基础响应间隔,并调用[swTimer_set](#156swTimer_set)函数设置新的定时间隔。
412 | 最后,将新的定时任务节点添加进timer的list,并将定时器数量增加1。 413 | 414 | ####**1.5.6.swTimer_set** 415 | 声明: 416 | ```c 417 | // timer.c 29h 418 | static int swTimer_set(swTimer *timer, int new_interval); 419 | ``` 420 | 功能:设置timer的定时器响应间隔
421 | 核心源码:
422 | ```c 423 | if (SwooleG.use_timerfd) 424 | { 425 | return swTimer_timerfd_set(timer, new_interval); 426 | } 427 | else 428 | { 429 | return swTimer_signal_set(timer, new_interval); 430 | } 431 | ``` 432 | 源码解释:
433 | 如果使用了timerfd,调用[swTimer_timerfd_set](#152swTimer_timerfd_set);否则,调用[swTimer_signal_set](#151swTimer_signal_set); 434 | 435 | ####**1.5.7.swTimer_addtimeout** 436 | 声明: 437 | ```c 438 | // timer.c 30h 439 | int swTimer_addtimeout(swTimer *timer, int timeout_ms, void *data); 440 | ``` 441 | 功能:从timer的列表中移除一个指定定时器
442 | 核心源码:
443 | ```c 444 | int new_interval = swoole_common_divisor(timeout_ms, timer->interval); 445 | if (new_interval < timer->interval) 446 | { 447 | swTimer_set(timer, new_interval); 448 | timer->interval = new_interval; 449 | } 450 | 451 | struct timeval now; 452 | if (gettimeofday(&now, NULL) < 0) 453 | { 454 | swWarn("gettimeofday() failed. Error: %s[%d]", strerror(errno), errno); 455 | return SW_ERR; 456 | } 457 | 458 | uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000; 459 | swTimer_node *node = sw_malloc(sizeof(swTimer_node)); 460 | if (node == NULL) 461 | { 462 | swWarn("malloc(%d) failed. Error: %s[%d]", (int ) sizeof(swTimer_node), strerror(errno), errno); 463 | return SW_ERR; 464 | } 465 | 466 | bzero(node, sizeof(swTimer_node)); 467 | node->data = data; 468 | node->exec_msec = now_ms + timeout_ms; 469 | swTimer_node_insert(&timer->root, node); 470 | ``` 471 | 源码解释:
472 | 首先计算timer定时器最小时间间隔,并设置新的定时器基础响应间隔。随后创建新的swTimer_node节点,并设置其属性值,然后调用**swTimer_node_insert**函数在timer的root链表中添加新节点。(需要注意的是,因为这个定时器是一次性的,因此并不会改变timer->num的值) 473 | 474 | ####**1.5.8.swTimer_select** 475 | 声明: 476 | ```c 477 | // timer.c 31h 478 | int swTimer_select(swTimer *timer); 479 | ``` 480 | 功能:遍历timer列表找到需要响应的定时器
481 | 核心源码:
482 | ```c 483 | uint64_t key; 484 | swTimer_interval_node *timer_node; 485 | struct timeval now; 486 | 487 | if (gettimeofday(&now, NULL) < 0) 488 | { 489 | swSysError("gettimeofday() failed."); 490 | return SW_ERR; 491 | } 492 | //swWarn("%d.%d", now.tv_sec, now.tv_usec); 493 | 494 | if (timer->onTimeout == NULL) 495 | { 496 | swWarn("timer->onTimeout is NULL"); 497 | return SW_ERR; 498 | } 499 | /** 500 | * timeout task list 501 | */ 502 | uint32_t now_ms = now.tv_sec * 1000 + now.tv_usec / 1000; 503 | swTimer_node *tmp = timer->root; 504 | while (tmp) 505 | { 506 | if (tmp->exec_msec > now_ms) 507 | { 508 | break; 509 | } 510 | else 511 | { 512 | timer->onTimeout(timer, tmp->data); 513 | timer->root = tmp->next; 514 | sw_free(tmp); 515 | tmp = timer->root; 516 | } 517 | } 518 | 519 | if (timer->onTimer == NULL) 520 | { 521 | swWarn("timer->onTimer is NULL"); 522 | return SW_ERR; 523 | } 524 | uint32_t interval = 0; 525 | do 526 | { 527 | //swWarn("timer foreach start\n----------------------------------------------"); 528 | timer_node = swHashMap_each_int(timer->list, &key); 529 | 530 | //hashmap empty 531 | if (timer_node == NULL) 532 | { 533 | break; 534 | } 535 | //the interval time(ms) 536 | interval = (now.tv_sec - timer_node->lasttime.tv_sec) * 1000 + (now.tv_usec - timer_node->lasttime.tv_usec) / 1000; 537 | 538 | /** 539 | * deviation 1ms 540 | */ 541 | if (interval >= timer_node->interval - 1) 542 | { 543 | memcpy(&timer_node->lasttime, &now, sizeof(now)); 544 | timer->onTimer(timer, timer_node->interval); 545 | } 546 | } while (timer_node); 547 | ``` 548 | 源码解释:
549 | 首先获取当前系统时间。
550 | 判定**onTimeout**回调是否被设置,如果未设置则返回错误。随后,遍历timeout定时任务列表,找到exec_msec时间小于等于当前时间的任务,调用onTimeout响应这些回调,并移除该任务。
551 | 判定**onTimer**回调是否被设置,如果未设置则返回错误。随后,遍历定时任务列表,判定当前节点是否需要响应(当前时间 - lasttime >= interval - 1ms),如果需要响应则设置新的lasttime并调用onTimer回调。
552 | 553 | ##**2.EventTimer** 554 | ###**2.1.EventTimer原理** 555 | EventTimer的实现原理是利用了epoll的timeout超时设置。通过设置epoll的timeout,就能在timeout时间后捕获一个事件,在捕获该事件后,通过遍历对应的事件列表即可得知哪些事件需要处理。 556 | ###**2.2.EventTimer私有操作函数** 557 | ####**2.2.1.swEventTimer_add** 558 | 声明: 559 | ```c 560 | // EventTimer.c 19h 561 | static int swEventTimer_add(swTimer *timer, int _msec, int interval, void *data); 562 | ``` 563 | 功能:向timer中添加一个定时器
564 | 核心源码:
后 565 | ```c 566 | swTimer_node *node = sw_malloc(sizeof(swTimer_node)); 567 | if (!node) 568 | { 569 | swSysError("malloc(%d) failed.", (int )sizeof(swTimer_node)); 570 | return SW_ERR; 571 | } 572 | 573 | int now_msec = swEventTimer_get_relative_msec(); 574 | if (now_msec < 0) 575 | { 576 | return SW_ERR; 577 | } 578 | node->data = data; 579 | node->exec_msec = now_msec + _msec; 580 | node->interval = interval ? _msec : 0; 581 | swTimer_node_insert(&timer->root, node); 582 | ``` 583 | 源码解释:
584 | 初始化并向Timer的root中添加一个节点。 585 | 586 | ####**2.2.2.swEventTimer_del** 587 | 声明: 588 | ```c 589 | // timer.c 20h 590 | static int swEventTimer_del(swTimer *timer, int _msec); 591 | ``` 592 | 功能:从timer的列表中移除一个指定定时器
593 | 核心源码:
594 | ```c 595 | if (timer->root) 596 | { 597 | swTimer_node_destory(&timer->root); 598 | } 599 | ``` 600 | 源码解释:
601 | 从timer的root中移除ms对应的定时器 602 | 603 | ####**2.2.3.swEventTimer_select** 604 | 声明: 605 | ```c 606 | // timer.c 21h 607 | static int swEventTimer_select(swTimer *timer); 608 | ``` 609 | 功能:从timer中选出需要响应的定时器
610 | 核心源码:
611 | ```c 612 | uint32_t now_msec = swEventTimer_get_relative_msec(); 613 | if (now_msec < 0) 614 | { 615 | return SW_ERR; 616 | } 617 | 618 | swTimer_node *tmp = timer->root; 619 | while (tmp) 620 | { 621 | if (tmp->exec_msec > now_msec) 622 | { 623 | break; 624 | } 625 | else 626 | { 627 | if (tmp->interval > 0) 628 | { 629 | timer->onTimer(timer, tmp->interval); 630 | } 631 | else 632 | { 633 | timer->onTimeout(timer, tmp->data); 634 | } 635 | 636 | timer->root = tmp->next; 637 | if (timer->root) 638 | { 639 | timer->root->prev = NULL; 640 | } 641 | if (tmp->interval > 0) 642 | { 643 | tmp->exec_msec += tmp->interval; 644 | swTimer_node_insert(&SwooleG.timer.root, tmp); 645 | } 646 | else 647 | { 648 | sw_free(tmp); 649 | } 650 | tmp = timer->root; 651 | } 652 | } 653 | if (timer->root == NULL) 654 | { 655 | SwooleG.main_reactor->timeout_msec = -1; 656 | } 657 | else 658 | { 659 | SwooleG.main_reactor->timeout_msec = timer->root->exec_msec - now_msec; 660 | } 661 | ``` 662 | 源码解释:
663 | 遍历root链表,如果节点已经需要响应(exec_msec大于当前时间),则根据interval是否为0来执行各种不同的回调函数,并且如果interval为0,还需要移除当前节点。
664 | 最后,重新设置SwooleG.main_reactor的timeout时间。如果timer中没有定时任务,则设定为无超时。 665 | 666 | ####**2.2.4.swEventTimer_free** 667 | 声明: 668 | ```c 669 | // timer.c 22h 670 | static void swEventTimer_free(swTimer *timer); 671 | ``` 672 | 功能:释放timer
673 | 核心源码:
674 | ```c 675 | if (timer->root) 676 | { 677 | swTimer_node_destory(&timer->root); 678 | } 679 | ``` 680 | 源码解释:
681 | 释放timer的root链表 -------------------------------------------------------------------------------- /10.Factory模块(下).md: -------------------------------------------------------------------------------- 1 | Swoole源码学习记录 2 | =================== 3 | ------------- 4 | ##Swoole版本:1.7.5-stable 5 | ------------- 6 | 本章将分析FactoryProcess.c中剩下的函数,这些函数用于操作worker、manager以及writer。这些函数提供了最核心的进程创建、管理等功能,是Swoole的master-worker结构的基石。 7 | 8 | 先从worker相关的函数开始(manager相关函数基本都涉及操作worker进程)。在FactoryProcess.c中一共声明了4个操作函数,分别是: 9 | ```c 10 | static int swFactoryProcess_worker_loop(swFactory *factory, int worker_pti); 11 | static int swFactoryProcess_worker_spawn(swFactory *factory, int worker_pti); 12 | static sw_inline uint32_t swServer_worker_schedule(swServer *serv, uint32_t schedule_key); 13 | static sw_inline int swFactoryProcess_worker_excute(swFactory *factory, swEventData *task); 14 | ``` 15 | 先分析两个内联函数。swServer_worker_schedule用于根据不同的调度模式查找对应的需要执行任务的worker_id。其核心源码如下: 16 | ```c 17 | if (serv->dispatch_mode == SW_DISPATCH_ROUND) 18 | { 19 | target_worker_id = (serv->worker_round_id++) % serv->worker_num; 20 | } 21 | //Using the FD touch access to hash 22 | else if (serv->dispatch_mode == SW_DISPATCH_FDMOD) 23 | { 24 | target_worker_id = schedule_key % serv->worker_num; 25 | } 26 | //Preemptive distribution 27 | else 28 | { 29 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 30 | { 31 | //msgsnd参数必须>0 32 | //worker进程中正确的mtype应该是pti + 1 33 | target_worker_id = serv->worker_num; 34 | } 35 | else 36 | { 37 | int i; 38 | sw_atomic_t *round = &SwooleTG.worker_round_i; 39 | for (i = 0; i < serv->worker_num; i++) 40 | { 41 | sw_atomic_fetch_add(round, 1); 42 | target_worker_id = (*round) % serv->worker_num; 43 | 44 | if (serv->workers[target_worker_id].status == SW_WORKER_IDLE) 45 | { 46 | break; 47 | } 48 | } 49 | swTrace("schedule=%d|round=%d\n", target_worker_id, *round); 50 | } 51 | } 52 | ``` 53 | 源码解释:根据swServer的dispatch_mode(调度模式)决定具体的选取方法。SW_DISPATCH_ROUND为轮询模式,SW_DISPATCH_FDMOD为根据FD取模(同一个fd一定会被同一个worker进程处理),否则就是抢占模式(从第一个worker开始查找,找到第一个空闲的worker就占用该worker)。另外,在抢占模式下,如果使用了消息队列,则不需要分配worker_id,每个worker会从消息队列中获取自己需要处理的数据。 54 | 55 | swFactoryProcess_worker_excute函数用于处理一个worker接收到的swEventData* task,根据task的type不同执行不同的逻辑。核心代码如下: 56 | ```c 57 | swServer *serv = factory->ptr; 58 | swString *package = NULL; 59 | 60 | factory->last_from_id = task->info.from_id; 61 | //worker busy 62 | serv->workers[SwooleWG.id].status = SW_WORKER_BUSY; 63 | 64 | switch(task->info.type) 65 | { 66 | //no buffer 67 | case SW_EVENT_TCP: 68 | case SW_EVENT_UDP: 69 | case SW_EVENT_UNIX_DGRAM: 70 | 71 | //ringbuffer shm package 72 | case SW_EVENT_PACKAGE: 73 | onTask: 74 | factory->onTask(factory, task); 75 | 76 | if (!SwooleWG.run_always) 77 | { 78 | //only onTask increase the count 79 | worker_task_num --; 80 | } 81 | 82 | if (task->info.type == SW_EVENT_PACKAGE_END) 83 | { 84 | package->length = 0; 85 | } 86 | break; 87 | 88 | //package trunk 89 | case SW_EVENT_PACKAGE_START: 90 | case SW_EVENT_PACKAGE_END: 91 | //input buffer 92 | package = SwooleWG.buffer_input[task->info.from_id]; 93 | //merge data to package buffer 94 | memcpy(package->str + package->length, task->data, task->info.len); 95 | package->length += task->info.len; 96 | //printf("package[%d]. from_id=%d|data_len=%d|total_length=%d\n", task->info.type, task->info.from_id, task->info.len, package->length); 97 | //package end 98 | if (task->info.type == SW_EVENT_PACKAGE_END) 99 | { 100 | goto onTask; 101 | } 102 | break; 103 | 104 | case SW_EVENT_CLOSE: 105 | serv->onClose(serv, task->info.fd, task->info.from_id); 106 | break; 107 | case SW_EVENT_CONNECT: 108 | serv->onConnect(serv, task->info.fd, task->info.from_id); 109 | break; 110 | case SW_EVENT_FINISH: 111 | serv->onFinish(serv, task); 112 | break; 113 | default: 114 | swWarn("[Worker] error event[type=%d]", (int)task->info.type); 115 | break; 116 | } 117 | //worker idle 118 | serv->workers[SwooleWG.id].status = SW_WORKER_IDLE; 119 | ``` 120 | 源码解释:设置factory的最近一次接收的reactor的id为task的from_id。设置worker的状态值为BUSY状态。根据task的type类型决定具体的调用类型。这里的调用逻辑稍微复杂点: 121 | 1. SW_EVENT_TCP、SW_EVENT_UDP、SW_EVENT_UNIX_DGRAM这三种类型没有缓存区,因此直接调用onTask投递任务,如果没有设置run_always(一直运行),则将剩余可处理的worker_task_num减一; 122 | 2. SW_EVENT_PACKAGE是RingBuffer共享内存池的包,因为该package是完整的不需要拼接,因此同样直接onTask发送; 123 | 3. SW_EVENT_PACKAGE_START 获取reactor对应的缓存,将task的data放入缓存区中; 124 | 4. SW_EVENT_PACKAGE_END 所有数据发送完毕,跳到onTask,通过onTask投递任务,并将缓存区置空。(这里存在一个疑问,缓存区中的数据并没有通过onTask投递,也没有发现将缓存区数据存入task的行为,待解决) 125 | 5. SW_EVENT_CLOSE、SW_EVENT_CONNECT、SW_EVENT_FINISH分别调用对用的PHP回调函数。 126 | 执行完成后,设置worker状态为IDLE。 127 | 128 | 接下来是swFactoryProcess_worker_spawn,该函数的功能是重启一个worker进程,函数核心源码如下: 129 | ```c 130 | pid = fork(); 131 | if (pid < 0) 132 | { 133 | swWarn("Fork Worker failed. Error: %s [%d]", strerror(errno), errno); 134 | return SW_ERR; 135 | } 136 | //worker child processor 137 | else if (pid == 0) 138 | { 139 | ret = swFactoryProcess_worker_loop(factory, worker_pti); 140 | exit(ret); 141 | } 142 | //parent,add to writer 143 | else 144 | { 145 | return pid; 146 | } 147 | ``` 148 | 源码解释:调用fork创建worker进程,在进程内调用swFactoryProcess_worker_loop进入worker的工作循环,循环结束后推出进程。在父进程中返回worker进程的pid。 149 | 150 | 然后就是重要的swFactoryProcess_worker_loop函数,该函数定义了一个worker进程的主要工作循环,由于该函数较长,所以仍然是分段分析。下面上源码: 151 | ```c 152 | swServer *serv = factory->ptr; 153 | struct 154 | { 155 | long pti; 156 | swEventData req; 157 | } rdata; 158 | int n; 159 | int pipe_rd = serv->workers[worker_pti].pipe_worker; 160 | ``` 161 | 源码分析:获取swServer对象,构建rdata结构体(该结构体的成员和swDispatchData结构体完全一样)用于存放对应的worker_id和需要处理的数据。同时获取对应worker的读管道。 162 | ```c 163 | #ifdef HAVE_CPU_AFFINITY 164 | if (serv->open_cpu_affinity == 1) 165 | { 166 | cpu_set_t cpu_set; 167 | CPU_ZERO(&cpu_set); 168 | CPU_SET(worker_pti % SW_CPU_NUM, &cpu_set); 169 | if (0 != sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set)) 170 | { 171 | swWarn("pthread_setaffinity_np set failed"); 172 | } 173 | } 174 | #endif 175 | ``` 176 | 源码解释:如果定义了HAVE_CPU_AFFINITY宏且swServer中指定打开了cpu affinity setting(CPU亲和性设置,英文解释为always run this process on processor one,大概翻译一下就是每个进程只能在某个指定的处理器上运行(针对多核CPU)),则先通过CPU_SET将worker_id对应的CPU加入到CPU集合中,然后通过sched_setaffinity函数指定当前worker进程运行在这个CPU上。(这里就能理解为何Swoole要求将worker_num设置为CPU的核的数目) 177 | ```c 178 | //signal init 179 | swWorker_signal_init(); 180 | 181 | //worker_id 182 | SwooleWG.id = worker_pti; 183 | #ifndef SW_USE_RINGBUFFER 184 | int i; 185 | //for open_check_eof and open_check_length 186 | if (serv->open_eof_check || serv->open_length_check || serv->open_http_protocol) 187 | { 188 | SwooleWG.buffer_input = sw_malloc(sizeof(swString*) * serv->reactor_num); 189 | if (SwooleWG.buffer_input == NULL) 190 | { 191 | swError("malloc for SwooleWG.buffer_input failed."); 192 | return SW_ERR; 193 | } 194 | for (i = 0; i < serv->reactor_num; i++) 195 | { 196 | SwooleWG.buffer_input[i] = swString_new(serv->buffer_input_size); 197 | if (SwooleWG.buffer_input[i] == NULL) 198 | { 199 | swError("buffer_input init failed."); 200 | return SW_ERR; 201 | } 202 | } 203 | } 204 | #endif 205 | ``` 206 | 源码解释:调用swWorker_signal_init指定对应信号的处理函数,并设置SwooleWG全局变量中的id为当前worker_id。接下来这段就是处理Swoole提供的自动分包功能,如果使用RingBuffer,并且设置了eof检测、长度检测、http协议中的任意一种 ,则都需要设置worker的缓存区。首先给缓存区buffer_input分配空间,开辟一个长度为reactor_num的、存放swString指针的数组,然后遍历数组,为数组中的每一位创建一个swString对象,swString的长度为指定的缓存区长度(buffer_input_size,对应config中的package_max_length)。 207 | ```c 208 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 209 | { 210 | //抢占式,使用相同的队列type 211 | if (serv->dispatch_mode == SW_DISPATCH_QUEUE) 212 | { 213 | //这里必须加1 214 | rdata.pti = serv->worker_num + 1; 215 | } 216 | else 217 | { 218 | //必须加1 219 | rdata.pti = worker_pti + 1; 220 | } 221 | } 222 | ``` 223 | 源码解释:如果使用了消息队列,在抢占模式下,使用相同的队列type,其他模式下,指定rdata的进程id为worker_id + 1(我没理解为何要必须加1,后面发现了会过来补充)。 224 | ```c 225 | else 226 | { 227 | SwooleG.main_reactor = sw_malloc(sizeof(swReactor)); 228 | if (SwooleG.main_reactor == NULL) 229 | { 230 | swError("[Worker] malloc for reactor failed."); 231 | return SW_ERR; 232 | } 233 | if (swReactor_auto(SwooleG.main_reactor, SW_REACTOR_MAXEVENTS) < 0) 234 | { 235 | swError("[Worker] create worker_reactor failed."); 236 | return SW_ERR; 237 | } 238 | swSetNonBlock(pipe_rd); 239 | SwooleG.main_reactor->ptr = serv; 240 | SwooleG.main_reactor->add(SwooleG.main_reactor, pipe_rd, SW_FD_PIPE); 241 | SwooleG.main_reactor->setHandle(SwooleG.main_reactor, SW_FD_PIPE, swFactoryProcess_worker_onPipeReceive); 242 | 243 | #ifdef HAVE_SIGNALFD 244 | if (SwooleG.use_signalfd) 245 | { 246 | swSignalfd_setup(SwooleG.main_reactor); 247 | } 248 | #endif 249 | } 250 | ``` 251 | 源码解释:如果没有使用消息队列,初始化并调用swReactor_auto函数创建主要的reactor,设置读管道pipe_rd为非阻塞模式,并将该管道加入reactor中监听,并设置回调函数为swFactoryProcess_worker_onPipeReceive(从名字就能看出这是干嘛的……)。最后一段,如果设置了HAVE_SIGNALFD宏并且开启了use_signalfd,则调用swSignalfd_setup函数设置main_reactor。(对于这段的分析将在对Swoole的signal.c文件分析时给出) 252 | ```c 253 | if (serv->max_request < 1) 254 | { 255 | SwooleWG.run_always = 1; 256 | } 257 | else 258 | { 259 | worker_task_num = serv->max_request; 260 | worker_task_num += swRandom(worker_pti); 261 | } 262 | ``` 263 | 源码解释:如果没有设置max_request(每个worker允许处理的最大请求数),则设置run_always为true,否则,设置worker_task_num数量为max_request,然后加上一个随机数(个位数的随机数 * worker_id) 264 | ```c 265 | //worker start 266 | swServer_worker_onStart(serv); 267 | 268 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 269 | { 270 | while (SwooleG.running > 0) 271 | { 272 | n = serv->read_queue.out(&serv->read_queue, (swQueue_data *) &rdata, sizeof(rdata.req)); 273 | if (n < 0) 274 | { 275 | if (errno == EINTR) 276 | { 277 | if (SwooleG.signal_alarm) 278 | { 279 | swTimer_select(&SwooleG.timer); 280 | } 281 | } 282 | else 283 | { 284 | swWarn("[Worker%ld] read_queue->out() failed. Error: %s [%d]", rdata.pti, strerror(errno), errno); 285 | } 286 | continue; 287 | } 288 | swFactoryProcess_worker_excute(factory, &rdata.req); 289 | } 290 | } 291 | else 292 | { 293 | struct timeval timeo; 294 | timeo.tv_sec = SW_REACTOR_TIMEO_SEC; 295 | timeo.tv_usec = SW_REACTOR_TIMEO_USEC; 296 | SwooleG.main_reactor->wait(SwooleG.main_reactor, &timeo); 297 | } 298 | 299 | //worker shutdown 300 | swServer_worker_onStop(serv); 301 | ``` 302 | 源码解释:首先调用swServer_worker_onStart回调(这里对应onWorkerStart回调,通知PHP程序)。如果是消息队列模式,则进入循环,每次从消息队列中取出一条消息,调用swFactoryProcess_worker_excute处理该消息;如果不是消息队列模式,则调用main_reactor的wait方法进入事件监听(详情请看第八章Reactor模块)。循环结束后,调用swServer_worker_onStop回调通知PHP程序。 303 | 304 | swFactoryProcess_worker_onPipeReceive函数的核心源码如下: 305 | ```c 306 | if (read(event->fd, &task, sizeof(task)) > 0) 307 | { 308 | /** 309 | * Big package 310 | */ 311 | ret = swFactoryProcess_worker_excute(factory, &task); 312 | if (task.info.type == SW_EVENT_PACKAGE_START) 313 | { 314 | //no data 315 | if (ret < 0 && errno == EAGAIN) 316 | { 317 | return SW_OK; 318 | } 319 | else if (ret > 0) 320 | { 321 | goto read_from_pipe; 322 | } 323 | } 324 | return ret; 325 | } 326 | ``` 327 | 源码解释:从管道中读出一个task数据,然后调用swFactoryProcess_worker_excute函数执行任务。(这里的task来源就是reactor的监听) 328 | 329 | 这里可以整理出一个完整的worker流程:swFactoryProcess_worker_spawn创建worker进程,随后通过swFactoryProcess_worker_loop进入worker的主循环;当有任务来临时,通过swServer_worker_schedule获取对应的worker,调用swFactoryProcess_worker_excute执行任务。 330 | 331 | FactoryProcess中共声明了两个manager操作函数,如下: 332 | ```c 333 | static int swFactoryProcess_manager_loop(swFactory *factory); 334 | static int swFactoryProcess_manager_start(swFactory *factory); 335 | ``` 336 | swFactoryProcess_manager_start函数用于启动一个manager进程,该manager进程用于创建和管理每个worker进程。其核心源码较长,分段分析: 337 | ```c 338 | if (serv->ipc_mode == SW_IPC_MSGQUEUE) 339 | { 340 | //读数据队列 341 | if (swQueueMsg_create(&serv->read_queue, 1, serv->message_queue_key, 1) < 0) 342 | { 343 | swError("[Master] swPipeMsg_create[In] fail. Error: %s [%d]", strerror(errno), errno); 344 | return SW_ERR; 345 | } 346 | //为TCP创建写队列 347 | if (serv->have_tcp_sock == 1) 348 | { 349 | //写数据队列 350 | if (swQueueMsg_create(&serv->write_queue, 1, serv->message_queue_key + 1, 1) < 0) 351 | { 352 | swError("[Master] swPipeMsg_create[out] fail. Error: %s [%d]", strerror(errno), errno); 353 | return SW_ERR; 354 | } 355 | } 356 | } 357 | ``` 358 | 源码解释:消息队列模式下,首先创建读数据队列,如果swServer设置了TCP sock的监听,则需创建写队列。 359 | ```c 360 | else 361 | { 362 | object->pipes = sw_calloc(serv->worker_num, sizeof(swPipe)); 363 | if (object->pipes == NULL) 364 | { 365 | swError("malloc[worker_pipes] failed. Error: %s [%d]", strerror(errno), errno); 366 | return SW_ERR; 367 | } 368 | //worker进程的pipes 369 | for (i = 0; i < serv->worker_num; i++) 370 | { 371 | if (swPipeUnsock_create(&object->pipes[i], 1, SOCK_DGRAM) < 0) 372 | { 373 | return SW_ERR; 374 | } 375 | serv->workers[i].pipe_master = object->pipes[i].getFd(&object->pipes[i], 1); 376 | serv->workers[i].pipe_worker = object->pipes[i].getFd(&object->pipes[i], 0); 377 | } 378 | } 379 | ``` 380 | 源码解释:非消息队列模式下,创建通讯管道(使用unix sock管道,以此保证管道是双向可用的) 381 | ```c 382 | if (SwooleG.task_worker_num > 0) 383 | { 384 | key_t msgqueue_key = 0; 385 | if (SwooleG.task_ipc_mode > 0) 386 | { 387 | msgqueue_key = serv->message_queue_key + 2; 388 | } 389 | 390 | if (swProcessPool_create(&SwooleG.task_workers, SwooleG.task_worker_num, serv->task_max_request, msgqueue_key) < 0) 391 | { 392 | swWarn("[Master] create task_workers failed."); 393 | return SW_ERR; 394 | } 395 | 396 | swWorker *worker; 397 | for(i = 0; i < SwooleG.task_worker_num; i++) 398 | { 399 | worker = swServer_get_worker(serv, serv->worker_num + i); 400 | if (swWorker_create(worker) < 0) 401 | { 402 | return SW_ERR; 403 | } 404 | } 405 | 406 | //设置指针和回调函数 407 | SwooleG.task_workers.ptr = serv; 408 | SwooleG.task_workers.onTask = swTaskWorker_onTask; 409 | SwooleG.task_workers.onWorkerStart = swTaskWorker_onWorkerStart; 410 | SwooleG.task_workers.onWorkerStop = swTaskWorker_onWorkerStop; 411 | } 412 | ``` 413 | 源码解释:如果设置了task_worker_num,则创建一个PorcessPool进程池(这里将在对Swoole的进程池、线程池做分析时给出详解)用于管理多个task进程,随后循环创建指定个数的task进程并设置swServer指针和回调函数。 414 | ```c 415 | pid = fork(); 416 | switch (pid) 417 | { 418 | //创建manager进程 419 | case 0: 420 | //创建子进程 421 | for (i = 0; i < serv->worker_num; i++) 422 | { 423 | //close(worker_pipes[i].pipes[0]); 424 | reactor_pti = (i % serv->writer_num); 425 | serv->workers[i].reactor_id = reactor_pti; 426 | pid = swFactoryProcess_worker_spawn(factory, i); 427 | if (pid < 0) 428 | { 429 | swError("Fork worker process fail"); 430 | return SW_ERR; 431 | } 432 | else 433 | { 434 | serv->workers[i].pid = pid; 435 | } 436 | } 437 | /** 438 | * create task worker pool 439 | */ 440 | if (SwooleG.task_worker_num > 0) 441 | { 442 | swProcessPool_start(&SwooleG.task_workers); 443 | } 444 | //标识为管理进程 445 | SwooleG.process_type = SW_PROCESS_MANAGER; 446 | ret = swFactoryProcess_manager_loop(factory); 447 | exit(ret); 448 | break; 449 | //主进程 450 | default: 451 | SwooleGS->manager_pid = pid; 452 | break; 453 | case -1: 454 | swError("fork() failed."); 455 | return SW_ERR; 456 | } 457 | ``` 458 | 源码解释:调用fork函数创建manager进程,在manager进程中,调用swFactoryProcess_worker_spawn创建子进程,调用swProcessPool_start启动task进程池,标记该进程为管理进程,并调用swFactoryProcess_manager_loop进入管理进程核心工作循环。 459 | 460 | 接下来是swFactoryProcess_manager_loop函数,该函数定义了manager进程的主工作流程,其核心工作是监听worker的exit事件并创建新的worker进程。核心源码如下: 461 | ```c 462 | swServer *serv = factory->ptr; 463 | swWorker *reload_workers; 464 | 465 | swSignal_set(SIGTERM, swWorker_signal_handler, 1, 0); 466 | 467 | if (serv->onManagerStart) 468 | { 469 | serv->onManagerStart(serv); 470 | } 471 | 472 | reload_worker_num = serv->worker_num + SwooleG.task_worker_num; 473 | reload_workers = sw_calloc(reload_worker_num, sizeof(swWorker)); 474 | if (reload_workers == NULL) 475 | { 476 | swError("[manager] malloc[reload_workers] failed"); 477 | return SW_ERR; 478 | } 479 | 480 | //for reload 481 | swSignal_add(SIGUSR1, swManager_signal_handle); 482 | ``` 483 | 设置Term终止事件的信号监听,如果设置了onManagerStart回调,调用它(onStart)。设置需要reload的worker数量为worker_num + task_worker_num.并分配reload_workers数组的空间用于临时存放worker结构体。同时设置针对reload信号的监听。这里的SIGUSR1信号为自定义信号,该信号的响应事件就是将manager_worker_reloading变量置为1,并将manager_reload_flag置0. 484 | ```c 485 | while (SwooleG.running > 0) 486 | { 487 | pid = wait(&worker_exit_code); 488 | 489 | if (pid < 0) 490 | { 491 | if (manager_worker_reloading == 0) 492 | { 493 | swTrace("[Manager] wait failed. Error: %s [%d]", strerror(errno), errno); 494 | } 495 | else if (manager_reload_flag == 0) 496 | { 497 | memcpy(reload_workers, serv->workers, sizeof(swWorker) * serv->worker_num); 498 | if (SwooleG.task_worker_num > 0) 499 | { 500 | memcpy(reload_workers + serv->worker_num, SwooleG.task_workers.workers, 501 | sizeof(swWorker) * SwooleG.task_worker_num); 502 | } 503 | manager_reload_flag = 1; 504 | goto kill_worker; 505 | } 506 | } 507 | if (SwooleG.running == 1) 508 | { 509 | for (i = 0; i < serv->worker_num; i++) 510 | { 511 | //compare PID 512 | if (pid != serv->workers[i].pid) 513 | { 514 | continue; 515 | } 516 | else 517 | { 518 | if (serv->onWorkerError!=NULL && WEXITSTATUS(worker_exit_code) > 0) 519 | { 520 | serv->onWorkerError(serv, i, pid, WEXITSTATUS(worker_exit_code)); 521 | } 522 | pid = 0; 523 | while (1) 524 | { 525 | new_pid = swFactoryProcess_worker_spawn(factory, i); 526 | if (new_pid < 0) 527 | { 528 | usleep(100000); 529 | continue; 530 | } 531 | else 532 | { 533 | serv->workers[i].pid = new_pid; 534 | break; 535 | } 536 | } 537 | } 538 | } 539 | 540 | //task worker 541 | if (pid > 0) 542 | { 543 | swWorker *exit_worker = swHashMap_find_int(SwooleG.task_workers.map, pid); 544 | if (exit_worker != NULL) 545 | { 546 | swProcessPool_spawn(exit_worker); 547 | } 548 | } 549 | } 550 | //reload worker 551 | kill_worker: if (manager_worker_reloading == 1) 552 | { 553 | //reload finish 554 | if (reload_worker_i >= reload_worker_num) 555 | { 556 | manager_worker_reloading = 0; 557 | reload_worker_i = 0; 558 | continue; 559 | } 560 | ret = kill(reload_workers[reload_worker_i].pid, SIGTERM); 561 | if (ret < 0) 562 | { 563 | swWarn("[Manager]kill() failed, pid=%d. Error: %s [%d]", reload_workers[reload_worker_i].pid, strerror(errno), errno); 564 | continue; 565 | } 566 | reload_worker_i++; 567 | } 568 | } 569 | ``` 570 | 源码解释:进入manager的核心工作循环。调用wait等待worker_exit事件的发生。wait函数会返回exit的worker进程的pid。 571 | 如果pid小于0,说明wait函数出错,如果manager_worker_reloading标记为0,说明当前并没有调用reload函数,则报错。如果manager_worker_reloading为1,且manager_reload_flag为0,则将所有worker进程的结构体copy进reload_workers中,,设置manager_reload_flag为1(代表正在重启进程),随后goto到kill_worker标签处; 572 | 573 | 如果pid大于0且swServer正在运行,首先遍历worker数组找到pid对应的worker进程,如果设置了onWorkerError回调并且worker是异常退出的,就调用该回调。随后,循环调用swFactoryProcess_worker_spawn函数创建新的worker进程直到创建成功,设置worker pid为新的pid。如果是task进程,则先通过swHashMap_find_int函数从SwooleG全局变量的task_workers里的map中找到对应的task_worker结构体,随后调用swProcessPool_spawn函数重建该task进程。 574 | 575 | **kill_worker**标签处,如果manager_worker_reloading为1,说明调用了reload函数,如果reload_worker_i索引大于或等于reload_worker_num,说明所有进程都重启了一遍,因此重置manager_worker_reloading标记和reload_worker_i索引,继续下一次循环;否则,调用kill函数杀死reload_worker_i索引指向的worker进程,将reload_worker_i后移一位。 576 | 577 | 这里处理了两种情况,一种是worker进程自己退出,这时仅重启退出的worker进程。一种是调用了reload函数,这时要重启全部的worker进程。 578 | 579 | 到此,FactoryProcess模块全部分析完毕。下一章将分析Swoole的Worker.c模块、Connection.c模块和ReactorProcess模块。 580 | 581 | --------------------------------------------------------------------------------