阅读 skynet 代码 socket_server 部分,发现对 socket 的写操作流程是这样的:
1. 各个服务(各线程)将数据写到 sendctrl_fd,这是一个 pipe 的 写端
2. ctrl_cmd 函数从 recvctrl_fd 读出数据,然后写到真正的 socketfd
在第一步中,多个线程向 sendctrl_fd 写数据,但是代码中并没有给 sendctrl_fd 加锁。
1 static void 2 send_request(struct socket_server *ss, struct request_package *request, char type, int len) { 3 request->header[6] = (uint8_t)type; 4 request->header[7] = (uint8_t)len; 5 for (;;) { 6 int n = write(ss->sendctrl_fd, &request->header[6], len+2); 7 if (n<0) { 8 if (errno != EINTR) { 9 fprintf(stderr, "socket-server : send ctrl command error %s. ", strerror(errno)); 10 } 11 continue; 12 } 13 assert(n == len+2); 14 return; 15 } 16 }
经过各种搜索得出结论:
写 pipe 的数据大小如果小于等于 PIPE_BUF(4k),那么这个写操作是 atomic 的。如果超过了这个 PIPE_BUF,那就不能保证咯。。
http://stackoverflow.com/questions/4624071/pipe-buffer-size-is-4k-or-64k
https://docs.oracle.com/cd/E19683-01/806-6546/pipe6-7/index.html
http://man7.org/linux/man-pages/man7/pipe.7.html
http://stackoverflow.com/questions/9701757/when-to-use-pipes-vs-when-to-use-shared-memory
http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html#pipes