很久以前就知道网络Socket套接字API存在iovec接口,曾以为它能够实现零拷贝,但是,简单分析后,发现iovec指向的也是用户空间的地址,所以,认为它没什么太大价值,不能够实现用户空间到内核空间的零拷贝,就没有继续深入学习研究下去。
直到最近研究recvmmsg
接口,以及伴随一直思考的的性能优化任务,突然对iovec
产生了理解的电火花,用网络套接字iovec
类接口,可以对适用的场景节省一次不必要的数据拷贝工作!
下面列举一个比较容易理解的例子
简而言之,例子就是组件API对上层提供发送网络报文接口,但对于接口参数传递的报文数据,又必须在入参数据前加上扩展头后,才能发送出去,而且不得不做!
iovec
但是,在知道了iovec
类接口后,就不需要这么复杂了!!!
以前笔记积累过网络操作报文缓冲区预留前后缀空间的最佳实践
反例
extern struct T_GTPUHeader;
int sendMessageToPeer(..., const char* data, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen)
{
unsigned char abEncodeBuf[MAX_PKG];
struct T_GTPUHeader* ptGTPUHeader = (T_GTPUHeader*)abEncodeBuf;
// Use the input paras or some global info
fillGTPUHeader(ptGTPUHeader, ...);
// 多一次用户空间内的拷贝
memcpy(abEncodeBuf + sizeof(struct T_GTPUHeader), data, len);
// get sockfd by some mean
return sendto(sockfd, abEncodeBuf, sizeof(struct T_GTPUHeader) + len, flags, dest_addr, addrlen);
}
正例
extern struct T_GTPUHeader;
int sendMessageToPeer_iovec(..., const char* data, size_t len, int flags, struct sockaddr *dest_addr, socklen_t addrlen)
{
struct T_GTPUHeader tGTPUHeader = {};
// Use the input paras or some global info
fillGTPUHeader(&tGTPUHeader, ...);
// Use iovec api
struct iovec vec[2];
vec[0].iov_base = &tGTPUHeader;
vec[0].iov_len = sizeof(tGTPUHeader);
vec[1].iov_base = data;
vec[1].iov_len = len;
struct msghdr msghdr = {};
msghdr.msg_name = dest_addr;
msghdr.msg_namelen = addrlen;
msghdr.msg_iov = &vec; /* scatter/gather array */
msghdr.msg_iovlen = sizeof(vec)/sizeof(vec[0]); /* # elements in msg_iov */
// get sockfd by some mean
return sendmsg(sockfd, &msghdr, flags);
}
iovec可支持更自由的内存管理