<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                在Vold代碼中,使用NM模塊的流程是: - 調用Instance創建一個NM對象。 - 調用setBroadcaster設置CL對象。 - 調用start啟動NM。 接下來,按這三個步驟來分析NM模塊。 1. 創建NM Vold調用Instance函數創建了一個NM對象。看到Instance這個函數,讀者應能想到,這里可能是采用了單例模式。來看是否如此,代碼如下所示。 **NetlinkManager.cpp** ~~~ NetlinkManager *NetlinkManager::Instance() { if(!sInstance) sInstance = new NetlinkManager();//果然是單例模式 returnsInstance; } ~~~ NM的創建真是非常簡單。再看第二個被調用的函數setBroadcaster。 2. setBroadcaster的分析 setBroadcaster就更簡單了,它的實現在NetlinkManger類的聲明中,如下所示: **NetlinkManager.h** ~~~ void setBroadcaster(SocketListener *sl) {mBroadcaster = sl; } ~~~ setBroadcaster參數中的那個sl其實際類型為CommandListener。需要說明的是,雖然NM設置了CL對象,但Vold的NM并沒有通過CL發送消息和接收命令,所以在圖9-1中,NM模塊和CL模塊并沒有連接線,這一點務請注意。 下面看最后一個函數start。 3. start的分析 前面說過,NM模塊將使用Netlink和Kernel進行IPC通信,那么它是怎么做到的呢?來看代碼,如下所示: **NetlinkManager.cpp** ~~~ int NetlinkManager::start() { //PF_NETLINK使用的socket地址結構是sockaddr_nl,而不是一般的sockaddr_in structsockaddr_nl nladdr; int sz= 64 * 1024; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = getpid(); //設置自己的進程pid nladdr.nl_groups = 0xffffffff; /* 創建PF_NETLINK地址簇的socket,目前只支持SOCK_DGRAM類型,第三個參數 NETLINK_KOBJECT_UEVENT表示要接收內核的Uevent事件。 */ if((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) { ...... return -1; } //設置Socket接收緩沖區大小 if(setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { ...... return -1; } //必須對該socket執行bind操作 if(bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) { ...... return -1; } //創建一個NetlinkHandler對象,并把創建好的Socket句柄傳給它。 mHandler = new NetlinkHandler(mSock); //調用NetlinkHandler對象的start if(mHandler->start()) { SLOGE("Unable to start NetlinkHandler: %s", strerror(errno)); return -1; } return0; } ~~~ 從代碼上看,NM的start函數分為兩個步驟: - 創建地址簇為PF_NETLINK類型的socket并做一些設置,這樣NM就能和Kernel通信了。關于Netlink的使用技巧網上有很多資料,讀者可在Linux系統上通過man netlink命令來查詢相關信息。 - 創建NetlinkHandler對象,并調用它的start。看來,后續工作都是由NetlinkHandler來完成的。 據上文分析可看出,NetlinkHandler才是真正的主角,下面就來分析它。為書寫方便起見,NetlinkHandler簡稱為NLH。 4. NetlinkHandler的分析 (1)創建NLH 代碼結構簡單的Vold程序中,NetlinkHandler卻有一個相對不簡單的派生關系,如圖9-2所示: :-: ![](http://img.blog.csdn.net/20150802164431558?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖9-2 NLH的派生關系圖 直接看代碼,來認識這個NLH: **NetlinkHandler.cpp** ~~~ NetlinkHandler::NetlinkHandler(int listenerSocket): NetlinkListener(listenerSocket) { //調用基類NetlinkListener的構造函數。注意傳入的參數是和Kernel通信的socket //句柄。注意,文件描述符和句柄表示的是同一個東西,這里不再區分二者。 } ~~~ 再看基類NetlinkListener的構造函數: **NetlinkListener.cpp** ~~~ NetlinkListener::NetlinkListener(int socket) : SocketListener(socket, false) { //調用基類SocketListener的構造函數,第二個參數為false。 } ~~~ 基類SocketListener的構造函數是: **SocketListener.cpp** ~~~ SocketListener::SocketListener(int socketFd,bool listen) { mListen = listen; //這個參數是false mSocketName = NULL; mSock = socketFd;//保存和Kernel通信的socket描述符 //初始化一個mutex,看來會有多個線程存在 pthread_mutex_init(&mClientsLock, NULL); /* SocketClientCollection的聲明如下,它是一個列表容器。 typedef android::List<SocketClient *>SocketClientCollection 其中,SocketClient代表和Socket服務端通信的客戶端。 */ mClients = new SocketClientCollection(); } ~~~ NLH的創建分析完了。此過程中沒有什么新鮮內容。下面看它的start函數。 本章內容會大量涉及Socket,所以讀者應先了解與Socket有關的知識,如果需要深入研究,建議閱讀《Unix NetworkingProgramming Volume I》[^write]一書。 (2)start的分析 在分析前面的代碼時,曾看到NetlinkHandler會創建一個同步互斥對象,這表明NLH會在多線程環境中使用,那么這個線程會在哪里創建呢?來看start的代碼,如下所示: **NetlinkHandler.cpp** ~~~ int NetlinkHandler::start() { returnthis->startListener();//startListener由SocketListener實現。 } ~~~ **SocketListener.cpp** ~~~ int SocketListener::startListener() { if(!mSocketName && mSock == -1) { errno = EINVAL; return -1; } elseif (mSocketName) { if((mSock = android_get_control_socket(mSocketName)) < 0) { return -1; } } /* 還記得構造NLH時的參數嘛?mListen為false,這表明NLH不是監聽端(listen)。 這里為了代碼和操作的統一,用mSock做參數構造了一個SocketClient對象, 并加入到mClients列表中,但這個SocketClient并不是真實客戶端的代表。 */ if(mListen && listen(mSock, 4) < 0) { ...... return -1; } else if (!mListen)//以mSock為參數構造SocketClient對象,并加入到對應列表中 mClients->push_back(new SocketClient(mSock)); /* pipe系統調用將創建一個匿名管道,mCtrlPipe是一個int類型的二元數組。 其中mCtrlPipe[0]用于從管道讀數據,mCtrlPipe[1]用于往管道寫數據 */ if(pipe(mCtrlPipe)) { ...... return -1; } //創建一個工作線程,線程函數是threadStart。 if(pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { ...... return -1; } return0; } ~~~ 如果熟悉Socket編程,理解上面的代碼就非常容易了。下面來看NLH的工作線程。 (3)工作線程的分析 工作線程的線程函數threadStart的代碼如下所示: **SocketListener.cpp** ~~~ void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast<SocketListener *>(obj); me->runListener();//調用runListener。 pthread_exit(NULL); returnNULL; } //直接分析runListener void SocketListener::runListener() { while(1) { SocketClientCollection::iterator it; fd_set read_fds; int rc = 0; int max = 0; FD_ZERO(&read_fds); if(mListen) {//mListen為false,所以不走這個if分支 max = mSock; FD_SET(mSock, &read_fds); } /* 計算max,為什么要有這個操作?這是由select函數決定的,它的第一個參數的取值 必須為它所監視的文件描述符集合中最大的文件描述符加1。 */ FD_SET(mCtrlPipe[0], &read_fds); if(mCtrlPipe[0] > max) max = mCtrlPipe[0]; //還是計算fd值最大的那個 pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it != mClients->end(); ++it) { FD_SET((*it)->getSocket(), &read_fds); if ((*it)->getSocket() > max) max = (*it)->getSocket(); } pthread_mutex_unlock(&mClientsLock); /* 注意select函數的第一個參數,為max+1。讀者可以通過man select來查詢 select的用法,注意,在Windows平臺上的select對第一個參數沒有要求。 */ if((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { sleep(1); continue; }else if (!rc) continue; //如果管道可讀的話,表示需要退出工作線程。 if(FD_ISSET(mCtrlPipe[0], &read_fds)) break; if(mListen && FD_ISSET(mSock, &read_fds)) { //如果是listen端的話,mSock可讀表示有客戶端connect上 struct sockaddr addr; socklen_t alen = sizeof(addr); int c; //調用accept接受客戶端的連接,返回用于和客戶端通信的Socket描述符 if ((c = accept(mSock, &addr, &alen)) < 0) { SLOGE("accept failed (%s)", strerror(errno)); sleep(1); continue; } pthread_mutex_lock(&mClientsLock); //根據返回的客戶端Socket描述符構造一個SocketClient對象,并加入到對應list mClients->push_back(new SocketClient(c)); pthread_mutex_unlock(&mClientsLock); } do{ pthread_mutex_lock(&mClientsLock); for (it = mClients->begin(); it !=mClients->end(); ++it) { int fd = (*it)->getSocket(); if (FD_ISSET(fd, &read_fds)) { pthread_mutex_unlock(&mClientsLock); /* 有數據通過Socket發送過來,所以調用onDataAvailable進行處理。 如果在onDataAvailable返回false,表示需要關閉該連接。 */ if (!onDataAvailable(*it)){ close(fd); pthread_mutex_lock(&mClientsLock); delete *it; it =mClients->erase(it); pthread_mutex_unlock(&mClientsLock); } FD_CLR(fd, &read_fds); continue; } } pthread_mutex_unlock(&mClientsLock); }while (0); } } ~~~ 從代碼中可看到: - 工作線程退出的條件是匿名管道可讀,但在一般情況下不需要它退出,所以可以忽略此項內容。 - 不論是服務端還是客戶端,收到數據后都會調用onDataAvailable進行處理。 下面就來看NLH的數據處理。 (4)數據處理 根據前面的分析,收到數據后首先調用onDataAvailable函數進行處理,這個函數由NLH的基類NetlinkListener實現。代碼如下所示: **NetlinkListener** ~~~ bool NetlinkListener::onDataAvailable(SocketClient*cli) { intsocket = cli->getSocket(); intcount; /* 調用recev接收數據,如果接收錯誤,則返回false,這樣這個socket在 上面的工作線程中就會被close。 */ if((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) { SLOGE("recv failed (%s)", strerror(errno)); return false; } //new一個NetlinkEvent,并調用decode來解析接收到的Uevent數據 NetlinkEvent *evt = new NetlinkEvent(); if(!evt->decode(mBuffer, count)) { goto out; } //調用onEvent,并傳遞NetlinkEvent對象。 onEvent(evt); out: deleteevt; return true; ~~~ decode函數就是將收到的Uevent信息填充到一個NetlinkEvent對象中,例如Action是什么,SUBSYSTEM是什么等,以后處理Uevent時就不用再解析字符串了。 看onEvent函數,此函數是由NLH自己實現的,代碼如下所示: **NetlinkHandler.cpp** ~~~ void NetlinkHandler::onEvent(NetlinkEvent *evt){ VolumeManager *vm = VolumeManager::Instance(); constchar *subsys = evt->getSubsystem(); if(!subsys) { return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); //調用VM的handleBlockEvent } elseif (!strcmp(subsys, "switch")) { vm->handleSwitchEvent(evt);//調用VM的handleSwitchEvent } else if (!strcmp(subsys, "battery")){ //這兩個事件和外部存儲系統沒有關系,所以不處理 } elseif (!strcmp(subsys, "power_supply")) { } } ~~~ NLH的工作已介紹完,下面總結一下NM模塊的工作。 5. NM模塊的總結 NM模塊的功能就是從Kernel接收Uevent消息,然后轉換成一個NetlinkEvent對象,最后會調用VM的處理函數來處理這個NetlinkEvent對象。 [^write]: 該書中文版名為《UNIX網絡編程第3版.第1卷,套接字聯網API》,人民郵電出版社,2009年版。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看