Unix 域协议这一章的第一篇文章就是讨论如何在进程间传递描述符,可是后面似乎我们把这件事忘了。其实不然,我们一直在为这件事做铺垫,本文将进一步逼近“真相”。
辅助数据 (Ancillary) ,也叫控制数据,实际上在前面讲解 recvmsg 和 sendmsg 时提过一嘴,当时只是说先放一放。
还记得吧,这个结构体是 recvmsg 和 sendmsg 函数的第二个参数类型。
struct msghdr { void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov; size_t msg_iovlen; void *msg_control; // 辅助数据 size_t msg_controllen; // 辅助数据大小 int msg_flags; };当时我们落下了两个成员 msg_control 和 msg_controllen 没有讲,现在时机到了。要想在进程间传递描述符,使用普通方法直接传递描述符是行不通的,所以辅助数据在这里派上了用场。
辅助数据的类型是 cmsghdr{},如下:
struct cmsghdr { socklen_t cmsg_len; int cmsg_level; int cmsg_type; /* followed by unsigned char cmsg_data[]; */ };如果要想在进程间传递描述符,可通过适当的设置 cmsg_level 和 cmsg_type 的值,并把描述符的值保存在 cmsg_data 中,就可以通过 sendmsg 将描述符发送给其它进程了。
msghdr{} 的成员 msg_control 是一个指针,它指向一个 cmsghdr{} 类型的”数组”。准确的说,它指向第一个辅助数据对象。
图1 msghdr{} 与 cmsghdr{}观察图1 ,一共有 3 个辅助数据对象,每个对象有三部分构成:{cmsghdr, 数据, 填充字节},这里衍生了几个长度概念:
CMSG_SPACE: 三部分长度的总和。CMSG_LEN:cmsghdr 的长度 + 数据长度。CMSG_ALIGN:数据长度 + 填充字节长度。(图中没有画)实际上,这三个长度有着对应的宏,可以计算出来,分别是
size_t CMSG_SPACE(size_t length); size_t CMSG_LEN(size_t length); size_t CMSG_ALIGN(size_t length);它们的参数,应该传递数据的长度。比如说数据长度是 4 字节,则根据三个宏算出来的 space, len 和 align 长度分别是:CMSG_SPACE(4), CMSG_LEN(4), CMSG_ALIGN(4).
如果知道了 cmsghdr{} 的首地址,就可以取到数据的地址,使用宏 CMSG_DATA 就可以了,它的定义如下:
unsigned char *CMSG_DATA(struct cmsghdr *cmsg);例如:
char* data = CMSG_DATA(&cm);有两个宏可以做到这一点:
// 根据 msghdr{} 获取第一个 cmsghdr{} 地址 struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh); // 根据 msghdr{} 和 cmsghdr{} 获取下一个 cmsghdr{} 地址 struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);例如:
struct msghdr msg; struct cmsghdr *cmptr; /* 填充 msg */ /* 调用 recvmsg */ for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NEXTHDR(&msg, cmptr)); { char* data = CMSG_DATA(cmptr); }程序路径:
git clone https://git.oschina.net/ivan_allen/unp.git如果你已经 clone 过这个代码了,请使用 git pull 更新一下。本节程序所使用的程序路径是 unp/program/unixdomainprotocols/ancillary.
程序 ancilla 使用方法如下:
./ancilla [--showsize size] [--showdata datacount] --showsize:根据数据大小 size 计算 space, len, align 长度--showdata:填充辅助数据,并遍历辅助数据,参数 datacount 表示创建几个辅助数据对象图 3 中创建了 5 个辅助数据对象,并进行遍历。