unix域是一种基于单主机的进程间通信方式。实现模式类似tcp通信。今天先分析他的实现,后续会分析他的使用。在libuv中,unix域用uv_pipe_t表示。
struct uv_pipe_s { // uv_handle_s的字段 void* data; // 所属事件循环 uv_loop_t* loop; // handle类型 uv_handle_type type; // 关闭handle时的回调 uv_close_cb close_cb; // 用于插入事件循环的handle队列 void* handle_queue[2]; union { int fd; void* reserved[4]; } u; // 用于插入事件循环的closing阶段对应的队列 uv_handle_t* next_closing; // 各种标记 unsigned int flags; // 流拓展的字段 // 用户写入流的字节大小,流缓存用户的输入,然后等到可写的时候才做真正的写 size_t write_queue_size; // 分配内存的函数,内存由用户定义,主要用来保存读取的数据 uv_alloc_cb alloc_cb; // 读取数据的回调 uv_read_cb read_cb; // 连接成功后,执行connect_req的回调(connect_req在uv__xxx_connect中赋值) uv_connect_t *connect_req; // 关闭写端的时候,发送完缓存的数据,执行shutdown_req的回调(shutdown_req在uv_shutdown的时候赋值) uv_shutdown_t *shutdown_req; // 流对应的io观察者,即文件描述符+一个文件描述符事件触发时执行的回调 uv__io_t io_watcher; // 流缓存下来的,待写的数据 void* write_queue[2]; // 已经完成了数据写入的队列 void* write_completed_queue[2]; // 完成三次握手后,执行的回调 uv_connection_cb connection_cb; // 操作流时出错码 int delayed_error; // accept返回的通信socket对应的文件描述符 int accepted_fd; // 同上,用于缓存更多的通信socket对应的文件描述符 void* queued_fds; // 标记管道是否能在进程间传递 int ipc; // 用于unix域通信的文件路径 const char* pipe_fname; }unix域继承域handle和stream。下面看一下他的具体实现逻辑。
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->shutdown_req = NULL; handle->connect_req = NULL; handle->pipe_fname = NULL; handle->ipc = ipc; return 0; }uv_pipe_init逻辑很简单,就是初始化uv_pipe_t结构体。刚才已经见过uv_pipe_t继承于stream,uv__stream_init就是初始化stream(父类)的字段。文章开头说过,unix域的实现类似tcp的实现。遵循网络socket编程那一套。服务端使用bind,listen等函数启动服务。
// name是unix域的文件路径 int uv_pipe_bind(uv_pipe_t* handle, const char* name) { struct sockaddr_un saddr; const char* pipe_fname; int sockfd; int err; pipe_fname = NULL; pipe_fname = uv__strdup(name); name = NULL; // unix域套接字 sockfd = uv__socket(AF_UNIX, SOCK_STREAM, 0); memset(&saddr, 0, sizeof saddr); strncpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path) - 1); saddr.sun_path[sizeof(saddr.sun_path) - 1] = '