乐趣区

关于开源:rtthread-裁剪系列一-之-lwip

本文由 RT-Thread 论坛用户 @出出啊原创公布:https://club.rt-thread.org/as…

前言

很久之前就开始整顿上面的优化项列表了,然而有很多问题钻研不深,一时不敢冒失推出。
前不久,有人在论坛上发问,过后我给的答案比当初少,然而当初列出来的这些也不能保障是全副,当前再做补充吧。

lwip 协定栈、sal socket 形象层应用了很多全局数组变量当作线程栈,能够批改成从内存堆动静申请的内存。
有些性能和个性在嵌入式设施里是用不到的,能够先去掉。
还有的是可有可无的个性,如果想用,也存在优化空间,能够本人实现。

以下阐明不限于 lwip,sal 局部也有波及。

裁剪详解

sal 可裁剪优化项

  1. SAL_INTERNET_CHECK: 网络检测,应用到了 workqueue。检测原理就是尝试连贯 “link.rt-thread.org::8101″,发送检测数据。
    这个或者能够去掉检测,或者换成自家服务器。
  2. #define SAL_SOCKETS_NUM 4: 这个可能是反对创立 socket 的最大数量。
  3. RT_USING_NETDEV: 网络接口设施,没有终端操作的状况下能够优化掉。其中,NETDEV_USING_IFCONFIG NETDEV_USING_PING NETDEV_USING_NETSTAT NETDEV_USING_AUTO_DEFAULT 别离能够独自增删。
  4. NETDEV_IPV6: 目前反对还不遍及的吧,能够关掉,如果须要才开启。

lwip 可裁剪优化项

  1. RT_LWIP_IGMP 组播须要用到的,不必组播可能能够去掉
  2. RT_LWIP_ICMP ping 命令应用的协定,没有 ping 也不须要这个协定。
  3. RT_LWIP_DNS 局域网不须要这个,或者说,间接应用 ip 地址进行连贯而不是应用 url 链接地址,能够不应用 dns。
  4. RT_LWIP_TCP_WND tcp 接管窗口,这个应该是申请内存大小。能够适当减小。不定义就是 1460 x 2 字节
  5. RT_LWIP_TCP_SND_BUF tcp 发送缓存,同上,不定义就是 1460 x 2 字节
  6. LWIP_NO_TX_THREADLWIP_NO_RX_THREAD eth 线程,发送一个,接管一个。以下是几个相干宏定义,如果不定义堆栈大小,默认应用 1024

    #define RT_LWIP_ETHTHREAD_PRIORITY 12
    #define RT_LWIP_ETHTHREAD_STACKSIZE 1024
    #define RT_LWIP_ETHTHREAD_MBOX_SIZE 8
    #define LWIP_NO_TX_THREAD
    #define LWIP_NO_RX_THREAD

    源码里,这部分还有很大优化空间,具体见下文详解。

  7. LWIP_NETIF_STATUS_CALLBACK 和前边的 SAL_INTERNET_CHECK 无关,这里设置网络连接回调。能够告诉应用层连贯上 INTERNET 了。
  8. LWIP_NETIF_LINK_CALLBACK 网卡连贯状态,仅示意物理连贯接入网络,有可能是和电脑直连,或者交换机、路由器等等。
  9. SO_REUSE 端口复用,这个在组播,而且是 UDP 协定才有用。不须要就定义成 0
  10. LWIP_SO_SNDTIMEO LWIP_SO_RCVTIMEO LWIP_SO_RCVBUF 这三个,如果 rtconf.h 里没有定义,lwipopts.h 会定义,所以不须要就定义成 0。
    其中 LWIP_SO_RCVBUF 接管缓冲,波及到接管缓冲下限。少数状况下不会有影响,只有网络数据多的时候才可能达到这个缓存下限。
  11. RT_LWIP_USING_PING 这个和后面的 NETDEV_USING_PING RT_LWIP_ICMP 无关。
  12. RT_LWIP_STATS 这是一组 stat 的总开关,具体细节查看 lwipopts.h 文件内的定义。或者勾销 RT_LWIP_STATS 定义,敞开所有 stat 项,或者独自批改 lwipopts.h 文件中某些 stat 定义。
  13. 批改 eth_rx_thread 和 eth_tx_thread,启用 RT_USING_HEAP 后,增加动态创建线程。这两个线程被初始化在 INIT_PREV_EXPORT 阶段。片上内存堆和片外边疆堆初始化注册都在 INIT_BOARD_EXPORT 阶段,能够申请应用动态内存。

erx etx 两个线程

以 etx 为例。ethernetif_linkoutput 函数次要操作如下:

    if (rt_mb_send(ð_tx_thread_mb, (rt_uint32_t) &msg) == RT_EOK)
    {
        /* waiting for ack */
        rt_sem_take(&(enetif->tx_ack), RT_WAITING_FOREVER);
    }

发送了一个邮箱,而后期待一个信号量。这个信号量从哪儿来?看上面的 etx 线程入口函数。

static void eth_tx_thread_entry(void* parameter)
{
    struct eth_tx_msg* msg;

    while (1)
    {if (rt_mb_recv(ð_tx_thread_mb, (rt_ubase_t *)&msg, RT_WAITING_FOREVER) == RT_EOK)
        {
            struct eth_device* enetif;

            RT_ASSERT(msg->netif != RT_NULL);
            RT_ASSERT(msg->buf   != RT_NULL);

            enetif = (struct eth_device*)msg->netif->state;
            if (enetif != RT_NULL)
            {
                /* call driver's interface */
                if (enetif->eth_tx(&(enetif->parent), msg->buf) != RT_EOK)
                {/* transmit eth packet failed */}
            }

            /* send ACK */
            rt_sem_release(&(enetif->tx_ack));
        }
    }
}

etx 期待 ethernetif_linkoutput 的邮件音讯,而后调用 eth 驱动接口函数,实现后开释信号量给 ethernetif_linkoutput 一个应答。

从这里看,用上这个线程,须要额定减少两次 ipc 音讯。
去掉 etx 之后呢?ethernetif_linkoutput 变成上面的样子。

static err_t ethernetif_linkoutput(struct netif *netif, struct pbuf *p)
{
    struct eth_device* enetif;

    RT_ASSERT(netif != RT_NULL);
    enetif = (struct eth_device*)netif->state;

    if (enetif->eth_tx(&(enetif->parent), p) != RT_EOK)
    {return ERR_IF;}
    return ERR_OK;
}

与应用 etx 线程惟一不同的是:应用线程时,发送数据操作 eth 驱动都在 etx 线程里进行的;如果去掉,就有可能多个利用线程同时发送数据,呈现多个线程竞争 eth 驱动资源的景象。然而,这个能够通过优化应用层业务逻辑进行躲避。

更多对于不应用 etx 和 erx 线程的批改,请移步我的 gitee 仓库。

本系列提到的所有代码更改曾经提交到 gitee,欢送大家测试
https://gitee.com/thewon/rt_t…

退出移动版