用户层通过setsockopt设置UDP_CORK选项的值,当其使能的时候,此套接口所有发送的数据将汇聚到一个报文中,当UDP_CORK选项的值禁用后,执行发送操作。此选项为Linux内核自有选项,据此实现的应用程序在其他平台系统上可能无法工作。
内核的处理函数为udp_lib_setsockopt。只要用户层传入的值不为0,置位corkflag变量,意味着CORK功能的使能。当用户层设置零值时,corkflag复位为0,同时发送阻塞的报文,由函数push_pending_frames实现。
int udp_lib_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, unsigned int optlen, int (*push_pending_frames)(struct sock *))
{
struct udp_sock *up = udp_sk(sk);
switch (optname) {
case UDP_CORK:
if (val != 0) {
up->corkflag = 1;
} else {
up->corkflag = 0;
lock_sock(sk);
push_pending_frames(sk);
release_sock(sk);
}
break;
}
由函数udp_setsockopt可知,函数指针push_pending_frames实际上为UDP函数udp_push_pending_frames。
int udp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen)
{
if (level == SOL_UDP || level == SOL_UDPLITE)
return udp_lib_setsockopt(sk, level, optname, optval, optlen,
udp_push_pending_frames);
}
int udp_push_pending_frames(struct sock *sk) /* Push out all pending data as one UDP datagram. Socket is locked. */
{
skb = ip_finish_skb(sk, fl4);
err = udp_send_skb(skb, fl4);
out:
up->len = 0;
up->pending = 0;
}
以下以内核的UDP发送函数udp_sendmsg为例,看下CORK阻塞情况。如上节所述,在使能UDP_CORK之后,corkflag置位。对于非CORK阻塞情况,调用ip_make_skb分配SKB缓存后,直接调用udp_send_skb进行发送。对于使能UDP_CORK的情况,在首次将设置UDP套接口的pending值为AF_INET,调用函数ip_append_data缓存发送数据。
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
{
int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;
/* Lockless fast path for the non-corking case. */
if (!corkreq) {
skb = ip_make_skb(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt, msg->msg_flags);
err = PTR_ERR(skb);
if (!IS_ERR_OR_NULL(skb))
err = udp_send_skb(skb, fl4);
goto out;
}
/* Now cork the socket to pend data. */
up->pending = AF_INET;
do_append_data:
up->len += ulen;
err = ip_append_data(sk, fl4, getfrag, msg, ulen, sizeof(struct udphdr), &ipc, &rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
out:
return err;
}
对于UDP的发送函数udp_sendpage而言,其仅检查UDP套接口的pending变量。当pending不为真时,调用以上的udp_sendmsg函数执行发送,由于udp_sendpage设置了MSG_MORE标志,udp_sendmsg函数将阻塞发送,置位pending变量。如果用户层未使能UDP_CORK并且也未设置发送函数send/sendmsg的MSG_MORE标志,内核将发送阻塞的数据,见函数udp_push_pending_frames。
int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags)
{
struct inet_sock *inet = inet_sk(sk);
struct udp_sock *up = udp_sk(sk);
if (!up->pending) {
struct msghdr msg = { .msg_flags = flags|MSG_MORE };
ret = udp_sendmsg(sk, &msg, 0);
}
lock_sock(sk);
up->len += size;
if (!(up->corkflag || (flags&MSG_MORE)))
ret = udp_push_pending_frames(sk);
if (!ret)
ret = size;
}
内核版本 4.15