参考《linux高性能服务器编程》13.9
在进程之间经常遇到需要在各进程之间传递文件描述符的情况,例如有一种设备它在加电期间只能打开一次,如果关闭后再次打开就会发生错误。这时就需要有一个调度程序,它调度多个相同设备,当有客户端需要此类型的设备时会向它发送一个请求,服务器会把某个设备的描述符给客户端。但是,由于不同进程之间的文件描述符所表示的对象是不同的,这需要一种特殊的机制来实现上述的要求。
在Linux下,我们可以利用Unix域socket在进程间传递特殊的辅助数据,以实现文件描述符的传递。
过程如下:
(1).创建一个字节流或者数据报的Unix域套接口。
(2).进程可以用任何返回描述符的Unix函数打开一个描述符:例如open(),pipe(),mkfifo(),socket(),accept(),可以在进程间传递任何类型的描述符。
(3).发送进程建立一个msghdr结构,其中包含要传递的描述符。在POSIX中说明该描述符作为辅助数据发送,但老的实现使用msg_accright成员。发送进程调用sendmsg()通过第一部得到的UNIX域套接字发出套接字。这时这个描述符是在飞行中的。即使在发送进程调用sendmsg()之后,但在接受进程调用recvmsg()之前将描述符关闭,它仍会为接收进程保持打开状态。描述符的发送导致它的访问统计数加1。
struct msghdr {
void *msg_name;//存数据包的目的地址,网络包指向sockaddr_in
//向内核发数据时,指向sockaddr_nl
int msg_namelen;//地址长度
struct iovec *msg_iov;
__kernel_size_t msg_iovlen;
void *msg_control;
__kernel_size_t msg_controllen;
unsigned msg_flags;
};
结构成员可以分为四组。他们是:
套接口地址成员msg_name与msg_namelen。
I/O向量引用msg_iov与msg_iovlen。
附属数据缓冲区成员msg_control与msg_controllen。
接收信息标记位msg_flags。
------------------------------------------------------------
第一组是msg_name和msg_namelen,记录这个消息的名字,其实就是数据包的目的地址。msg_name是指向一个结构体struct sockaddr的指针。长度为16:
struct sockaddr{
sa_family_t sa_family;
char sa_addr[14];
}
需要注意的是,结构体struct sockaddr只在进行参数传递时使用,无论是在用户态还是在内核态,我们都把其强制转化为结构体struct sockaddr_in:strcut sockaddr_in{
sa_family_t sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
struct in_addr{
__u32 s_addr;
}
---------------------------------------------------------------------------------------------------------------------------
第二组是msg_iov和msg_iovlen,记录这个消息的内容。msg_iov是一个指向结构体struct iovec的指针,实际上,确切地说,应该是一个结构体strcut iovec的数组。下面是该结构体的定义:
struct iovec{
void __user *iov_base;
__kernel_size_t iov_len;
};
iov_base指向数据包缓冲区,即参数buff,iov_len是buff的长度。msghdr中允许一次传递多个buff,以数组的形式组织在 msg_iov中,msg_iovlen就记录数组的长度(即有多少个buff)。
----------------------------------------------------------------
第三组是
void *msg_control;
__kernel_size_t msg_controllen;
,它们可被用于发送任何的控制信息。----------------------------------------------------------------
第四组是msg_flags。。raw协议不支持MSG_OOB向标志,即带外数据。向向内核发送msg时使用msghdr,netlink socket使用自己的消息头nlmsghdr和自己的消息地址sockaddr_nl:
struct sockaddr_nl
(4)接收进程调用recvmsg()在UNIX域套接字上接收套接字。通常接收进程收到的描述符的编号和发送进程中的描述符的编号不同,但这没有问题。传递描述符不是传递描述符的编号,而是在接收进程中建立一个新的描述符,指向内核的文件表中与发送进程发送的描述符相同的项。
{
sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
struct nlmsghdr
{
__u32 nlmsg_len; /* Length of message */
__u16 nlmsg_type; /* Message type*/
__u16 nlmsg_flags; /* Additional flags */
__u32 nlmsg_seq; /* Sequence number */
__u32 nlmsg_pid; /* Sending process PID */
};