一次sendmsg的改造过程

Posted 10087622blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次sendmsg的改造过程相关的知识,希望对你有一定的参考价值。

比较蛋疼的一个改造过程,简单记录一下。

场景:用户态使用sendmsg发包,tcp报文,由于内核实现过程中存在一次kernel_read,也就是存在将pagecache中的内容拷贝一次的问题。

为了减少这次拷贝,简单地将这个对pagecache的拷贝过程使用分散聚集io方式来进行map,map的数据来自于pagecahce中的文件。

这样就不存在拷贝了。

 

看过这部分代码的人肯定决定既然不想拷贝,为啥不用sendfile来实现,关键是因为需要每个包有部分数据来自于用户态,比如rtp头,rtsp头等。

而媒体数据在内核态组装,如果是udp好办,我们做了一个solaris版本的sendfile,可以带一个用户态数据区过来组装报文,然后直接发送。

但是tcp的话,存在重发控制,简单通过qdisc流控又达不到df值的要求,所以就遗留了这么一个改动。

 

在改动过程,一开始的方案是参照tcp_sendpage来改造,但由于用户传入的

struct msghdr {
    void    *    msg_name;    /* Socket name            */
    int        msg_namelen;    /* Length of name        */
    struct iovec *    msg_iov;    /* Data blocks            */
    __kernel_size_t    msg_iovlen;    /* Number of blocks        */
    void     *    msg_control;    /* Per protocol magic (eg BSD file descriptor passing) */
    __kernel_size_t    msg_controllen;    /* Length of cmsg list */
    unsigned    msg_flags;
};

msg_iov 的个数比较多,也就是msg_iovlen比较多,(这个msg_iov里面不是直接要发送的业务数据,而是针对file的偏移和长度段),这样的话,调用tcp_sendpage就很频繁,

由于3.10版本的内核的tcp_sendpage如下:

int tcp_sendpage(struct sock *sk, struct page *page, int offset,
         size_t size, int flags)
{
    ssize_t res;

    if (!(sk->sk_route_caps & NETIF_F_SG) ||//网卡是否支持分散聚集io
        !(sk->sk_route_caps & NETIF_F_ALL_CSUM))//网卡具备硬件执行校验和的标志
        return sock_no_sendpage(sk->sk_socket, page, offset, size,
                    flags);

    lock_sock(sk);
    res = do_tcp_sendpages(sk, &page, offset, size, flags);
    release_sock(sk);
    return res;
}

也就是说,每次操作一个很短的page都是在lock的情况下,这样锁操作多了,也间接影响了收包。

所以进一步参照 do_tcp_sendpages 的实现,自己申请了skb,挂载已经读取的poge段,放在分散聚集io的

skb_shinfo(skb)->frags 

数组中,而挂载的过程参照splice的实现。实现了免拷贝,以及减少了锁的申请,所以性能提升了20%左右。

另外一个有趣的发现是,由于接受方的0窗口,导致我们这边服务器发包的时候,没有窗口可以发送,间接影响了我们调用接口的时候的

static inline int sk_stream_memory_free(struct sock *sk)
{
    return sk->sk_wmem_queued < sk->sk_sndbuf;
}

出现了一些tryagain。

以上是关于一次sendmsg的改造过程的主要内容,如果未能解决你的问题,请参考以下文章

记一次改造react脚手架的过程

8人/天,小记一次 JAVA(APP后台) 项目改造 .NET 过程(后台代码已完整开源于 Github)

如何将图像视图从改造加载的片段传递给子片段?

记录一次使用UnifOfWork改造项目的过程。

Sublime Text自定制代码片段(Code Snippets)

一个项目的SpringCloud微服务改造过程