当前位置:网站首页>UDP协议之《套接口阻塞选项UDP_CORK》

UDP协议之《套接口阻塞选项UDP_CORK》

2022-08-10 03:26:00 程序员扫地僧

用户层通过setsockopt设置UDP_CORK选项的值,当其使能的时候,此套接口所有发送的数据将汇聚到一个报文中,当UDP_CORK选项的值禁用后,执行发送操作。此选项为Linux内核自有选项,据此实现的应用程序在其他平台系统上可能无法工作。


一、UDP_CORK配置

内核的处理函数为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发送函数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;
}

原网站

版权声明
本文为[程序员扫地僧]所创,转载请带上原文链接,感谢
https://blog.csdn.net/wuyongmao/article/details/126246801