zoukankan      html  css  js  c++  java
  • Android中的网络管理源码分析--netd


    http://www.voidcn.com/blog/a34140974/article/p-5033426.html


    1      Netd简介

    Netd是Android的网络守护进程。NetD是个网络管家,封装了复杂的底层各种类型的网络(NAT,PLAN,PPP,SOFTAP,TECHER,ETHO,MDNS等),隔离了底层网络接口的差异,给Framework提供了统一调用接口,简化了网络的使用。NetD主要功能是:第一、接收Framework的网络请求,处理请求,向Framework层反馈处理结果;第二、监听网络事件(断开/连接/错误等),向Framework层上报。

    2     Netd的启动过程

    Netd作为后台服务进程在Andriod系统启动的init1阶段就被启动了,其在init.rc文件的配置如下:

    service netd /system/bin/netd

        class main

        socket netd stream 0660 root system

        socket dnsproxyd stream 0660 root inet

        socket mdns stream 0660 root system

        socket fwmarkd stream 0660 root inet

    看一看到,这里为netd配置了4个socket(比老版本多了一个名字为“fwmakd”的socket),根据配置可找到netd的入口函数为main():

    int main() {

        CommandListener *cl;

        NetlinkManager *nm;

        DnsProxyListener *dpl;

        MDnsSdListener *mdnsl;

        FwmarkServer* fwmarkServer;

        ALOGI("Netd 1.0 starting");

        remove_pid_file();//猜测为每次重启时删除旧的

        blockSigpipe();//禁止SIGPIPE中断

        //创建NetlinkManager实例

        if (!(nm = NetlinkManager::Instance())) {

            ALOGE("Unable to create NetlinkManager");

            exit(1);

        };

        //创建CommandListener实例,并将其设置为NetlinkManagerBroadcaster,之后启动nm

        cl = new CommandListener();

        nm->setBroadcaster((SocketListener *) cl);

        if (nm->start()) {

            ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));

            exit(1);

        }

        // Set local DNS mode, to prevent bionic from proxying

        // back to this service, recursively.

        setenv("ANDROID_DNS_MODE", "local", 1);

        //创建并开始监听“dnsproxydsocket

        dpl = new DnsProxyListener(CommandListener::sNetCtrl);

        if (dpl->startListener()) {

            ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));

            exit(1);

        }

        //创建并开始监听“mdnssocket

        mdnsl = new MDnsSdListener();

        if (mdnsl->startListener()) {

            ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));

            exit(1);

        }

        //创建并开始监听“fwmarkdsocket

        fwmarkServer = new FwmarkServer(CommandListener::sNetCtrl);

        if (fwmarkServer->startListener()) {

            ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));

            exit(1);

        }

        /*

         * Now that we're up, we can respond to commands

         */

    //开始监听“netdsocket

        if (cl->startListener()) {

            ALOGE("Unable to start CommandListener (%s)", strerror(errno));

            exit(1);

        }

        bool wrote_pid = write_pid_file();

        while(1) {

            sleep(30); // 30 sec

            if (!wrote_pid) {

                wrote_pid = write_pid_file();

            }

        }

        ALOGI("Netd exiting");

        remove_pid_file();

        exit(0);

    }

    从上面个可以看出netd的启动并不复杂,主要是启动了4个监听socket,后面的分析将会看到每个socket对应这一个监听线程。首先来看NetlinkManage,NetlinkManager(以后简称NM)主要负责接收并解析来自Kernel的UEvent消息。如果对linux的socket特别熟悉的话,光从“NetlinkMananger”的名字就能推断出此类的基本实现和作用:肯定使用了PF_NETLINK的socket。这种socket一般是在应用层(相对于内核)监听内核事件的时候使用。例如USB的插拔等等。从main的代码可以知道它的入口为start()函数。

    int NetlinkManager::start() {

      //创建接收NETLINK_KOBJECT_UEVENT消息的socket,其值保存在mUeventSock

      //其中,NETLINK_FORMAT_ASCII代表UEvent消息的内容为ASCII字符串

        if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,

             0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII, false)) == NULL) {

            return -1;

        }

    //创建接收RTMGPR_LINK消息的socket,其值保存在mRouteSock

      //其中,NETLINK_FORMAT_BINARY代表UEvent消息的类型为结构体,故需要进行二进制解析

        if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,

                                         RTMGRP_LINK |

                                         RTMGRP_IPV4_IFADDR |

                                         RTMGRP_IPV6_IFADDR |

                                         RTMGRP_IPV6_ROUTE |

                                         (1 << (RTNLGRP_ND_USEROPT - 1)),

             NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {

            return -1;

        }

      //创建接收NETLINK_NFLOG消息的socket,其值保存在mQuotaSock

        if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,

                NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY, false)) == NULL) {

            ALOGE("Unable to open quota socket");

           

        }

    //创建接收NETLINK_NETFILTER消息的socket,其值保存在mQuotaSock

          if ((mStrictHandler = setupSocket(&mStrictSock, NETLINK_NETFILTER,

                0, NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST, true)) == NULL) {

            ALOGE("Unable to open strict socket");

           

        }

        return 0;

    }

    start()四次调用了setupSocket函数,新建了4个PF_NETLINK类型的socket监听内核的不同事件。查看函数setupSocket()。

    NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,

        int groups, int format, bool configNflog) {

        struct sockaddr_nl nladdr;

        int sz = 64 * 1024;

        int on = 1;

        memset(&nladdr, 0, sizeof(nladdr));

        nladdr.nl_family = AF_NETLINK;

        nladdr.nl_pid = getpid();

        nladdr.nl_groups = groups;

        //新建socket,一定要注意这里的socket类型为SOCK_DGRAM,这句是整个Nm的关键

        //netlinkFamily指定了soket监听的内核事件

        if ((*sock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, netlinkFamily)) < 0) {

            ALOGE("Unable to create netlink socket: %s", strerror(errno));

            return NULL;

        }

        //设置socket的属性

        if (setsockopt(*sock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

            ALOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));

            close(*sock);

            return NULL;

        }

        if (setsockopt(*sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {

            SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));

            close(*sock);

            return NULL;

        }

        //绑定

        if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

            ALOGE("Unable to bind netlink socket: %s", strerror(errno));

            close(*sock);

            return NULL;

        }

        if (configNflog) {//只有mStrictSock对应的为true

            if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_UNBIND, AF_INET) < 0) {

                ALOGE("Failed NFULNL_CFG_CMD_PF_UNBIND: %s", strerror(errno));

                return NULL;

            }

            if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_PF_BIND, AF_INET) < 0) {

                ALOGE("Failed NFULNL_CFG_CMD_PF_BIND: %s", strerror(errno));

                return NULL;

            }

            if (android_nflog_send_config_cmd(*sock, 0, NFULNL_CFG_CMD_BIND, AF_UNSPEC) < 0) {

                ALOGE("Failed NFULNL_CFG_CMD_BIND: %s", strerror(errno));

                return NULL;

            }

        }

        //socket封装成 NetLinkHandler,从而在socket有活动的时候处理

        NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);

        if (handler->start()) {//启动NetlinkHandler,实际就是启动监听

            ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));

            close(*sock);

            return NULL;

        }

        return handler;

    }

    NetlinkHandler的start()函数转调了this-> startListener(),此方法实际上是继承自SocketListener类。这个类是一个比较通用的类,很多与socket的IO复用有关的模块都会调用此类的相关方法。

    int SocketListener::startListener(int backlog) {

    //注意这个变量是类的成员变量,实际上这里就是想方设法得到socket

        if (!mSocketName && mSock == -1) {

            SLOGE("Failed to start unbound listener");

            errno = EINVAL;

            return -1;

        } else if (mSocketName) {

            if ((mSock = android_get_control_socket(mSocketName)) < 0) {

                SLOGE("Obtaining file descriptor socket '%s' failed: %s",

                     mSocketName, strerror(errno));

                return -1;

            }

            SLOGV("got mSock = %d for %s", mSock, mSocketName);

            fcntl(mSock, F_SETFD, FD_CLOEXEC);

        }

       //如果设置了mListen则监听socket,如果没有设置则新建一个socketClient放入客户端集合

       //注意短路,对于NetlinkHandler,从其构造函数可知mListenfalse

        if (mListen && listen(mSock, backlog) < 0) {

            SLOGE("Unable to listen on socket (%s)", strerror(errno));

            return -1;

        } else if (!mListen)

            mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));//这里

        if (pipe(mCtrlPipe)) {

            SLOGE("pipe failed (%s)", strerror(errno));

            return -1;

        }

       //创建线程处理监听socket,这里其实并没有所谓的“监听socket”,因为是NETLINK型的socket

        if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {

            SLOGE("pthread_create (%s)", strerror(errno));

            return -1;

        }

        return 0;

    }

    进入线程的入口函数SocketListener::threadStart()

    void *SocketListener::threadStart(void *obj) {

        SocketListener *me = reinterpret_cast<SocketListener *>(obj);

        //注意obj为主线程传递进来的参数,就是SocketListener

        me->runListener();

        pthread_exit(NULL);

        return NULL;

    }

    进入runListener

    void SocketListener::runListener() {

        //此函数的主要逻辑就是select()

        SocketClientCollection pendingList;//新建一个socketClientCollection存放活动fd

        while(1) {

            SocketClientCollection::iterator it;

            fd_set read_fds;

            int rc = 0;

            int max = -1;

            FD_ZERO(&read_fds);

            if (mListen) {//监听listenSocket的读事件,前面已经知道mListen此时为fasle

                max = mSock;

                FD_SET(mSock, &read_fds);

            }

            FD_SET(mCtrlPipe[0], &read_fds);//这里的pipe什么作用?中断循环标志?

            if (mCtrlPipe[0] > max)

                max = mCtrlPipe[0];

            pthread_mutex_lock(&mClientsLock);

            //遍历mClients集合

            for (it = mClients->begin(); it != mClients->end(); ++it) {

                // NB: calling out to an other object with mClientsLock held (safe)

                int fd = (*it)->getSocket();//获取与客户通信的socket

                FD_SET(fd, &read_fds);//监听它

                if (fd > max) {

                    max = fd;

                }

            }

            pthread_mutex_unlock(&mClientsLock);

            SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);

            if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//select

                if (errno == EINTR)

                    continue;

                SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);

                sleep(1);

                continue;

            } else if (!rc)

                continue;

            if (FD_ISSET(mCtrlPipe[0], &read_fds)) {//如果是pipe有活动

                char c = CtrlPipe_Shutdown;

                TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));//读取管道

                if (c == CtrlPipe_Shutdown) {

                    break;//难道这就是监听pipe的作用?

                }

                continue;

            }

    //如果是监听socket,接收连接请求,当然NETLINK不会走这里

            if (mListen && FD_ISSET(mSock, &read_fds)) {

                struct sockaddr addr;

                socklen_t alen;

                int c;

                do {

                    alen = sizeof(addr);

                    c = accept(mSock, &addr, &alen);

                    SLOGV("%s got %d from accept", mSocketName, c);

                } while (c < 0 && errno == EINTR);

                if (c < 0) {

                    SLOGE("accept failed (%s)", strerror(errno));

                    sleep(1);

                    continue;

                }

                fcntl(c, F_SETFD, FD_CLOEXEC);

                pthread_mutex_lock(&mClientsLock);

    //放入client集合

                mClients->push_back(new SocketClient(c, true, mUseCmdNum));

                pthread_mutex_unlock(&mClientsLock);

            }

           //将所有活动的fd都放入pendingList,貌似也只有一个

            pendingList.clear();

            pthread_mutex_lock(&mClientsLock);

            for (it = mClients->begin(); it != mClients->end(); ++it) {

                SocketClient* c = *it;

                // NB: calling out to an other object with mClientsLock held (safe)

                int fd = c->getSocket();

                if (FD_ISSET(fd, &read_fds)) {//fd如果有活动

                    pendingList.push_back(c);//放入pendingList

                    c->incRef();

                }

            }

            pthread_mutex_unlock(&mClientsLock);

            //处理pendingList,这里的具体意思就是内核有事件了,需要上层处理

            while (!pendingList.empty()) {

                /* Pop the first item from the list */

                it = pendingList.begin();

                SocketClient* c = *it;

                pendingList.erase(it);

                /* Process it, if false is returned, remove from list */

                if (!onDataAvailable(c)) {

                    release(c, false);

                }

                c->decRef();

            }

        }

    }

    从上面的函数可以看到,这里实际上是对3类fd作了监听处理。一类是监听socket,一类是client socket,并且这类socket被封装成SocketClient集中在一个集合之内。还有一个就是pipe。从NetlinkManager.start()中我们已经知道启动了四套这样的结构,其socket分别为mUeventSock ,mRouteSock,mQuotaSock,mStrictSock。这些Socket都是PF_NETLINK类型的sockegt,并不是监听socket,具体一点就是他们对应的mListen均为false。也就是这四个socket被当做SocketClient添加进了mClients(注意有四个实例)。等等,那么监听socket在哪呢?压根就没有监听socket,这里采用的是SOCK_DGRAM类型的socket!


    当检测到这些socket有可读事件发生时,也就是内核有上层感兴趣的事件发生时。相应的onDataAvailable()被调用,这是一个虚函数。分析可知此时this的具体类型为NetlinkHandler,因此调用的是NetlinkHandler的onDataAvailable()。

    bool NetlinkListener::onDataAvailable(SocketClient *cli)

    {

        int socket = cli->getSocket();

        ssize_t count;

        uid_t uid = -1;

        bool require_group = true;

        if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {

            require_group = false;

        }

        //读取数据

        count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,

                mBuffer, sizeof(mBuffer), require_group, &uid));

        if (count < 0) {

            if (uid > 0)

                LOG_EVENT_INT(65537, uid);

            SLOGE("recvmsg failed (%s)", strerror(errno));

            return false;

        }

        NetlinkEvent *evt = new NetlinkEvent();//新建一个NetLinkEvent

        if (evt->decode(mBuffer, count, mFormat)) {//解码

            onEvent(evt);//调用了此函数,对event做了处理

        } else if (mFormat != NETLINK_FORMAT_BINARY) {

            // Don't complain if parseBinaryNetlinkMessage returns false. That can

            // just mean that the buffer contained no messages we're interested in.

            SLOGE("Error decoding NetlinkEvent");

        }

        delete evt;

        return true;

    }

    这里调用了onEvent()才是NetlinkHandler的入口。

    void NetlinkHandler::onEvent(NetlinkEvent *evt) {

        const char *subsys = evt->getSubsystem();

        if (!subsys) {

            ALOGW("No subsystem found in netlink event");

            return;

        }

        if (!strcmp(subsys, "net")) {

            NetlinkEvent::Action action = evt->getAction();

            const char *iface = evt->findParam("INTERFACE");

            if (action == NetlinkEvent::Action::kAdd) {

                notifyInterfaceAdded(iface);

            } else if (action == NetlinkEvent::Action::kRemove) {

                notifyInterfaceRemoved(iface);

            } else if (action == NetlinkEvent::Action::kChange) {

                evt->dump();

                notifyInterfaceChanged("nana", true);

            } else if (action == NetlinkEvent::Action::kLinkUp) {

                notifyInterfaceLinkChanged(iface, true);

            } else if (action == NetlinkEvent::Action::kLinkDown) {

                notifyInterfaceLinkChanged(iface, false);

            } else if (action == NetlinkEvent::Action::kAddressUpdated ||

                       action == NetlinkEvent::Action::kAddressRemoved) {

                const char *address = evt->findParam("ADDRESS");

                const char *flags = evt->findParam("FLAGS");

                const char *scope = evt->findParam("SCOPE");

                if (action == NetlinkEvent::Action::kAddressRemoved && iface && address) {

                    int resetMask = strchr(address, ':') ? RESET_IPV6_ADDRESSES : RESET_IPV4_ADDRESSES;

                    resetMask |= RESET_IGNORE_INTERFACE_ADDRESS;

                    if (int ret = ifc_reset_connections(iface, resetMask)) {

                        ALOGE("ifc_reset_connections failed on iface %s for address %s (%s)", iface,

                              address, strerror(ret));

                    }

                }

                if (iface && flags && scope) {

                    notifyAddressChanged(action, address, iface, flags, scope);

                }

            } else if (action == NetlinkEvent::Action::kRdnss) {

                const char *lifetime = evt->findParam("LIFETIME");

                const char *servers = evt->findParam("SERVERS");

                if (lifetime && servers) {

                    notifyInterfaceDnsServers(iface, lifetime, servers);

                }

            } else if (action == NetlinkEvent::Action::kRouteUpdated ||

                       action == NetlinkEvent::Action::kRouteRemoved) {

                const char *route = evt->findParam("ROUTE");

                const char *gateway = evt->findParam("GATEWAY");

                const char *iface = evt->findParam("INTERFACE");

                if (route && (gateway || iface)) {

                    notifyRouteChange(action, route, gateway, iface);

                }

            }

        } else if (!strcmp(subsys, "qlog")) {

            const char *alertName = evt->findParam("ALERT_NAME");

            const char *iface = evt->findParam("INTERFACE");

            notifyQuotaLimitReached(alertName, iface);

        } else if (!strcmp(subsys, "strict")) {

            const char *uid = evt->findParam("UID");

            const char *hex = evt->findParam("HEX");

            notifyStrictCleartext(uid, hex);

        } else if (!strcmp(subsys, "xt_idletimer")) {

            const char *label = evt->findParam("INTERFACE");

            const char *state = evt->findParam("STATE");

            const char *timestamp = evt->findParam("TIME_NS");

            const char *uid = evt->findParam("UID");

            if (state)

                notifyInterfaceClassActivity(label, !strcmp("active", state),

                                             timestamp, uid);

    #if !LOG_NDEBUG

        } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {

            /* It is not a VSYNC or a backlight event */

            ALOGV("unexpected event from subsystem %s", subsys);

    #endif

        }

    }

    可以看到,这里对不同的时间进行看了notifXxx所有的notifyXXXXX函数都会调用notify()函数

    void NetlinkHandler::notify(int code, const char *format, ...) {

        char *msg;

        va_list args;

        va_start(args, format);

        if (vasprintf(&msg, format, args) >= 0) {

            //一定要注意这里的所使用的是clsocket,名字为”netd”,而非之前的那四个

            mNm->getBroadcaster()->sendBroadcast(code, msg, false);

            free(msg);

        } else {

            SLOGE("Failed to send notification: vasprintf: %s", strerror(errno));

        }

        va_end(args);

    }

    mNm就是之前在main()中新建的NetworkMananger。其BroadCaster已经设置为了cl(即CommandListener的一个实例)。CommandListener通过netd向NetworkManagementService发送消息。这里的消息可能有两种:一种是底层主动上报的消息,另一种是上层请求的response。(这个和RILD很类似)

    现在我们来整理一下上面的步骤:NetlinkManager新建了4个PF_NETLINK类型的socket,监听内核发生的uEvent。当内核发生相应的uEvent被对应的 NetlinkManager检测到。NetlinkManager将着个uEvent转化为NetlinkEvent 通过CommandListener广播到更上层。而这里的“更上层”指的是java层。可见,底层C/C++和上层java的联系是通过socket联系在一起的。

    这里一定要清楚出两点:之前的4个socket并不是这里的BroadCaster的socket;而且,个人觉得这个BroadCaster名字也容易让人产生误解,以为是广播,广播对应的socket就应该是UDP。而实际上这个socket是init.rc配置的名字为“netd”的socke所accept出来的clientSocket,是一个TCPsocket。而TCPsocket是无法广播的。这里直接将sendBroadCast理解为sendMsg后面的就很好理解了。

    接着分析CommandListener。这个类同样继承自SocketListener,与之前的4个Netlink socket所不同的是此类的mListen被设置为true。也就是“netd”为监听socket。CommandListener在之前的main函数中调用startListener开启监听来自java层的连接。当上层有连接时,select返回,accpet得到一个clientSocket,之后将其封装成SocketClient添加经list,并添加进select的监听队列。当java层下发命令,SocketClient的可读事件被检测到,从而做后续的处理,最后将底层处理结果response回上层,底层主动上报的消息也是通过此clientSocket上发到上层的。熟悉网络编程的就应该知道,这里是一个很典型的Select型的IO复用服务端模型。

    除“netd”外,其他三个在init.rc中配置的socket:dnsproxyd  mdns  fwmarkd也构建了几乎一样的服务端结构。这里就不再赘述。以下为netd的大致框图:


  • 相关阅读:
    关于grunt
    关于网页上标题图标显示
    form表单原理
    js判断是android访问还是ios访问
    判断客户端是手机访问还是电脑访问网站(php代码)
    电脑手机模拟器模拟手机浏览器,在线浏览手机网站
    手机网站通过JS判断是否为iPhone手机访问
    手机页面一键拨号
    html5手机网站常用的9个CSS属性
    js解析与序列化json数据(一)json.stringify()的基本用法
  • 原文地址:https://www.cnblogs.com/ztguang/p/12646017.html
Copyright © 2011-2022 走看看