79-辅助数据

Unix 域协议这一章的第一篇文章就是讨论如何在进程间传递描述符,可是后面似乎我们把这件事忘了。其实不然,我们一直在为这件事做铺垫,本文将进一步逼近“真相”。

辅助数据 (Ancillary) ,也叫控制数据,实际上在前面讲解 recvmsg 和 sendmsg 时提过一嘴,当时只是说先放一放。

1. 回忆 msghdr{}

还记得吧,这个结构体是 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 没有讲,现在时机到了。要想在进程间传递描述符,使用普通方法直接传递描述符是行不通的,所以辅助数据在这里派上了用场。

2. 辅助数据类型

辅助数据的类型是 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{}

2.1 三个长度概念

观察图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).

2.2 根据 cmsghdr{} 地址获取数据地址

如果知道了 cmsghdr{} 的首地址,就可以取到数据的地址,使用宏 CMSG_DATA 就可以了,它的定义如下:

unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

例如:

char* data = CMSG_DATA(&cm);

2.3 遍历所有 cmsghdr{}

有两个宏可以做到这一点:

// 根据 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);
}

3. 实验

程序路径:

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.1 showdata 代码

void showdata(int count) {
  int i, controllen;
  struct msghdr msg;
  struct cmsghdr *cmptr;
  union control{
    // 辅助数据头
    struct cmsghdr cm; 
    // 辅助数据对象大小是三部分的和。sizeof(int) 表示数据部分大小。
    char control[CMSG_SPACE(sizeof(int))]; 
  };
  union control *control_un;

  // 辅助数据总长度
  controllen = count * sizeof(union control);

  // 为辅助数据分配空间
  control_un = (union control*)malloc(controllen);;

  // 填充辅助数据
  for (i = 0; i < count; ++i) {
    control_un[i].cm.cmsg_len = CMSG_LEN(sizeof(int));
    control_un[i].cm.cmsg_level = i;
    control_un[i].cm.cmsg_type = i;
    *((int*) CMSG_DATA(&control_un[i].cm)) = 2*i + 1;
  }

  printf("controllen = %d\n", controllen);

  msg.msg_control = control_un;
  msg.msg_controllen = controllen;

  // 遍历辅助数据
  for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) {
    printf("cmsg_len = %d, cmsg_level = %d, cmsg_type = %d, data = %d\n"
        , cmptr->cmsg_len, cmptr->cmsg_level, cmptr->cmsg_type, *((int*) CMSG_DATA(cmptr)));
  }

  // 16 进制形式打印所有辅助数据
  printrawdata((char*)control_un, controllen);
}

3.2 实验结果


这里写图片描述
图2 查看三个长度


这里写图片描述
图3 遍历辅助数据

图 3 中创建了 5 个辅助数据对象,并进行遍历。

4. 总结

  • 掌握辅助数据的相关概念(几个长度概念)
  • 根据 cmsghdr{} 获取数据首地址
  • 遍历所有辅助数据对象
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值