阅读 skynet 代码 socket_server 部分,发现对 socket 的写操作流程是这样的:
1. 各个服务(各线程)将数据写到 sendctrl_fd,这是一个 pipe 的 写端
2. ctrl_cmd 函数从 recvctrl_fd 读出数据,然后写到真正的 socketfd
在第一步中,多个线程向 sendctrl_fd 写数据,但是代码中并没有给 sendctrl_fd 加锁。
static void
send_request(struct socket_server *ss, struct request_package *request, char type, int len) {
request->header[] = (uint8_t)type;
request->header[] = (uint8_t)len;
for (;;) {
int n = write(ss->sendctrl_fd, &request->header[], len+);
if (n<) {
if (errno != EINTR) {
fprintf(stderr, "socket-server : send ctrl command error %s.\n", strerror(errno));
}
continue;
}
assert(n == len+);
return;
}
}
经过各种搜索得出结论:
写 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