ngx_write_channel
1 定义ngx_write_channel 函数 定义在 ./nginx-1.24.0/src/os/unix/ngx_channel.cngx_int_tngx_write_channel(ngx_socket_ts,ngx_channel_t*ch,size_tsize,ngx_log_t*log){ssize_tn;ngx_err_terr;structioveciov[1];structmsghdrmsg;#if(NGX_HAVE_MSGHDR_MSG_CONTROL)union{structcmsghdrcm;charspace[CMSG_SPACE(sizeof(int))];}cmsg;if(ch-fd-1){msg.msg_controlNULL;msg.msg_controllen0;}else{msg.msg_control(caddr_t)cmsg;msg.msg_controllensizeof(cmsg);ngx_memzero(cmsg,sizeof(cmsg));cmsg.cm.cmsg_lenCMSG_LEN(sizeof(int));cmsg.cm.cmsg_levelSOL_SOCKET;cmsg.cm.cmsg_typeSCM_RIGHTS;/* * We have to use ngx_memcpy() instead of simple * *(int *) CMSG_DATA(cmsg.cm) ch-fd; * because some gcc 4.4 with -O2/3/s optimization issues the warning: * dereferencing type-punned pointer will break strict-aliasing rules * * Fortunately, gcc with -O1 compiles this ngx_memcpy() * in the same simple assignment as in the code above */ngx_memcpy(CMSG_DATA(cmsg.cm),ch-fd,sizeof(int));}msg.msg_flags0;#elseif(ch-fd-1){msg.msg_accrightsNULL;msg.msg_accrightslen0;}else{msg.msg_accrights(caddr_t)ch-fd;msg.msg_accrightslensizeof(int);}#endifiov[0].iov_base(char*)ch;iov[0].iov_lensize;msg.msg_nameNULL;msg.msg_namelen0;msg.msg_ioviov;msg.msg_iovlen1;nsendmsg(s,msg,0);if(n-1){errngx_errno;if(errNGX_EAGAIN){returnNGX_AGAIN;}ngx_log_error(NGX_LOG_ALERT,log,err,sendmsg() failed);returnNGX_ERROR;}returnNGX_OK;}ngx_write_channel 函数用于 Nginx 进程间通信通过 Unix 域套接字发送一个 ngx_channel_t 消息 并可附带传递一个文件描述符如新接受的连接。 它封装了 sendmsg 系统调用处理不同平台的辅助数据差异并返回操作状态。2 详解1 函数签名ngx_int_tngx_write_channel(ngx_socket_ts,ngx_channel_t*ch,size_tsize,ngx_log_t*log)返回值类型ngx_int_t 含义函数调用后返回一个状态码便于调用者进行统一的错误处理。 可能的返回值包括 NGX_OK消息成功发送。 NGX_AGAIN套接字发送缓冲区已满本次无法完成发送调用者通常需要等待可写事件后重试。 NGX_ERROR发生了不可恢复的错误参数1 ngx_socket_t s 一个已建立的 Unix 域套接字AF_UNIX连接描述符 通常由 socketpair() 创建 用于父子进程或兄弟进程间的通信。参数2 ngx_channel_t *ch 指向 ngx_channel_t 结构体的指针。该结构体是 Nginx 进程间消息的载体参数3 size_t size size_t无符号整数表示对象大小。 作用显式地告诉 sendmsg 要将从 ch 地址开始的多少字节作为正常数据发送。参数4 ngx_log_t *log 指向 ngx_log_t 的指针Nginx 的日志抽象对象。2 逻辑流程1 局部变量 2 文件描述符 3 设置消息头部 4 发送 5 返回成功1 局部变量{ssize_tn;ngx_err_terr;structioveciov[1];structmsghdrmsg;#if(NGX_HAVE_MSGHDR_MSG_CONTROL)union{structcmsghdrcm;charspace[CMSG_SPACE(sizeof(int))];}cmsg;2 文件描述符if(ch-fd-1){msg.msg_controlNULL;msg.msg_controllen0;}else{msg.msg_control(caddr_t)cmsg;msg.msg_controllensizeof(cmsg);ngx_memzero(cmsg,sizeof(cmsg));cmsg.cm.cmsg_lenCMSG_LEN(sizeof(int));cmsg.cm.cmsg_levelSOL_SOCKET;cmsg.cm.cmsg_typeSCM_RIGHTS;/* * We have to use ngx_memcpy() instead of simple * *(int *) CMSG_DATA(cmsg.cm) ch-fd; * because some gcc 4.4 with -O2/3/s optimization issues the warning: * dereferencing type-punned pointer will break strict-aliasing rules * * Fortunately, gcc with -O1 compiles this ngx_memcpy() * in the same simple assignment as in the code above */ngx_memcpy(CMSG_DATA(cmsg.cm),ch-fd,sizeof(int));}msg.msg_flags0;#1 判断是否传递文件描述符 检查 ngx_channel_t 结构体中的 fd 字段。 若为 -1表示本次消息无需传递文件描述符 相应地将控制信息指针设为 NULL长度设为 0#2 如果有有效的文件描述符需要传递fd ! -1则执行以下初始化 设置 msg_control 指向联合体 cmsg 的首地址 转换为通用指针类型 caddr_t通常为 void* 或 char*。 msg_controllen 设置为整个联合体的大小告诉内核这片内存用于辅助数据。 清零整个 cmsg 区域防止栈上的随机值干扰后续字段设置以及对齐部分的未定义内容 确保内核读取的控制消息干净可靠。 填充 cmsghdr 头部通过 cm 成员访问 cmsg_len 使用 CMSG_LEN(sizeof(int)) 计算。 CMSG_LEN 返回包含头部及数据大小不含尾部最终对齐填充的总长度内核需要这个值来确定消息边界。 cmsg_level SOL_SOCKET表示这是套接字级别的控制操作。 cmsg_type SCM_RIGHTS表示当前控制消息传递的是文件描述符访问权。接收方据此解释数据。 gx_memcpy 调用 从 ch-fd 的地址ch-fd拷贝 sizeof(int) 字节到 CMSG_DATA(cmsg.cm) 返回的控制消息数据区。 CMSG_DATA 宏返回一个指向 cmsghdr 之后实际数据开始位置的指针类型为 unsigned char *。 这样内核就能获取到要传递的文件描述符数值。 将 msg_flags 设为 0 避免未初始化值误导老式 BSD 系统分支#else#elseif(ch-fd-1){msg.msg_accrightsNULL;msg.msg_accrightslen0;}else{msg.msg_accrights(caddr_t)ch-fd;msg.msg_accrightslensizeof(int);}#endif3 设置消息头部iov[0].iov_base(char*)ch;iov[0].iov_lensize;msg.msg_nameNULL;msg.msg_namelen0;msg.msg_ioviov;msg.msg_iovlen1;iov[0].iov_base 指向 ngx_channel_t 结构体起始地址 强制转换为 char * 以满足 struct iovec 的类型要求 要发送的正常数据就是整个结构体。 iov[0].iov_len 设置为参数 size即结构体的大小。 尽管结构体中包含 fd 字段 但那个整数值仅作为数据副本传递真正的文件描述符通过辅助数据发送两者相互独立。 msg_name 设置为 NULL因为套接字 s 是已连接的 无需指定目标地址sendmsg 在已连接套接字上忽略此字段。 msg_namelen 设为 0与 msg_name 对应。 msg_iov 指向 I/O 向量数组 iov内核将从这里读取要发送的数据。 msg_iovlen 数组元素个数这里为 1因为只有一块数据。4 发送nsendmsg(s,msg,0);if(n-1){errngx_errno;if(errNGX_EAGAIN){returnNGX_AGAIN;}ngx_log_error(NGX_LOG_ALERT,log,err,sendmsg() failed);returnNGX_ERROR;}调用 sendmsg套接字 s、消息指针 msg、标志 flags 为 0无特殊行为。 该调用将 iov 描述的数据和 msg_control 中可能的文件描述符一并发送。 返回值保存在 n 中 若成功n 等于实际发送的字节数应与 size 一致 若失败返回 -1 检查错误码是否为 EAGAIN在 Nginx 中映射为 NGX_EAGAIN。 EAGAIN 表示套接字发送缓冲区已满本次发送无法完成对于非阻塞套接字。 此时函数返回 NGX_AGAIN通知调用者稍后重试5 返回成功returnNGX_OK;}