編輯:關於android開發
Android的存儲系統(三)
回顧:前帖分析了Vold的main()函數和NetlinkManager的函數調用流程,截止到NetlinkHandler的創建和start()調用,本帖繼續分析源碼
1、處理block類型的uevent
main()函數創建了CommandListener對象,NetlinkManager的start()函數又創建了NetlinkHandler對象,如果將CommandListener類和NetlinkHandler類的繼承關系圖畫出來,會發現它們都是從SocketListener類派生出來的,如下圖所示:
圖1 NetlinkHandler和CommandListener的繼承關系
原理:處於最底層的SocketListener類的作用是監聽socket的數據,接收到數據後分別交給FrameworkListener類和NetlinkListener類的函數,並分別對來自Framework和驅動的數據進行分析,分析後根據命令再分別調用CommandListener和NetlinkHandler中的函數。
觀察NetlinkHandler類的構造方法,代碼如下:
NetlinkHandler::NetlinkHandler(int listenerSocket) : NetlinkListener(listenerSocket) { }
這個構造方法很簡單,再看看它的start()方法,代碼如下:
int NetlinkHandler::start() { return this->startListener(); }
可以發現,start()方法調用了SocketListener的startListener()函數,代碼如下:
1 int SocketListener::startListener(int backlog) { 2 if (!mSocketName && mSock == -1) { 3 SLOGE("Failed to start unbound listener"); 4 errno = EINVAL; 5 return -1; 6 } else if (mSocketName) { // 只有CommandListener中會設置mSocketName 7 if ((mSock = android_get_control_socket(mSocketName)) < 0) { 8 SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno)); 9 return -1; 10 } 11 SLOGV("got mSock = %d for %s", mSock, mSocketName); 12 } 13 14 if (mListen && listen(mSock, backlog) < 0) { 15 SLOGE("Unable to listen on socket (%s)", strerror(errno)); 16 return -1; 17 } else if (!mListen) 18 mClients->push_back(new SocketClient(mSock, false, mUseCmdNum)); 19 20 if (pipe(mCtrlPipe)) { // 創建管道,用於退出監聽線程 21 SLOGE("pipe failed (%s)", strerror(errno)); 22 return -1; 23 } 24 25 if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { // 創建一個監聽線程 26 SLOGE("pthread_create (%s)", strerror(errno)); 27 return -1; 28 } 29 30 return 0; 31 }
startListener()函數開始監聽socket,這個函數在NetlinkHandler中會被調用,在CommandListener也會被調用。
startListener()函數首先判斷變量mSocketName是否有值,只有CommandListener對象會對這個變量賦值,它的值就是在init.rc中定義的socket字符串。
調用函數 android_get_control_socket()的目的是從環境變量中取得socket的值,這樣CommandListener對象得到了它需要監聽的socket,
而對於NetlinkHandler對象而言,它的mSocket不為NULL,前面已經創建了socket。
startListener()函數接下來會根據成員變量mListener的值來判斷是否需要調用Listen()函數來監聽socket。這個mListen的值在對象構造時根據參數來初始化。
對於CommandListener對象,mListener的值為ture,對於NetlinkHandler對象,mListener的值為false,這是因為CommandListener對象和SystemServer通信,需要監聽socket連接,而NetlinkHandler對象則不用。
接下來startListener()函數會創建一個管道,這個管道的作用是通知線程停止監聽,這個線程就是startListener()函數最後創建的監聽線程,它的運行函數是threadStart(),在前貼的NetlinkManager家族圖系中我們可以清晰的發現,其代碼如下:
void *SocketListener::threadStart(void *obj) { SocketListener *me = reinterpret_cast<SocketListener *>(obj); me->runListener(); // 調用runListener()方法 pthread_exit(NULL); return NULL; }
threadStart()中又調用了runListener()函數,代碼如下:
1 void SocketListener::runListener() { 2 3 SocketClientCollection pendingList; 4 5 while(1) { // 無限循環,一直監聽 6 SocketClientCollection::iterator it; 7 fd_set read_fds; 8 int rc = 0; 9 int max = -1; 10 11 FD_ZERO(&read_fds); // 清空文件描述符集read_fds 12 13 if (mListen) { // 如果需要監聽 14 max = mSock; 15 FD_SET(mSock, &read_fds); // 把mSock加入到read_fds 16 } 17 18 FD_SET(mCtrlPipe[0], &read_fds); // 把管道mCtrlPipe[0]也加入到read_fds 19 if (mCtrlPipe[0] > max) 20 max = mCtrlPipe[0]; 21 22 pthread_mutex_lock(&mClientsLock); // 對容器mClients的操作需要加鎖 23 for (it = mClients->begin(); it != mClients->end(); ++it) { // mClient中保存的是NetlinkHandler對象的socket,或者CommandListener接入的socket 24 int fd = (*it)->getSocket(); 25 FD_SET(fd, &read_fds); // 遍歷容器mClients的所有成員,調用內聯函數getSocket()獲取文件描述符,並添加到文件描述符集read_fds 26 if (fd > max) { // 也加入到read_fds 27 max = fd; 28 } 29 } 30 pthread_mutex_unlock(&mClientsLock); 31 SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName); 32 if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { // 執行select調用,開始等待socket上的數據到來 33 if (errno == EINTR) // 因為中斷退出select,繼續 34 continue; 35 SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max); 36 sleep(1); // select出錯,休眠1秒後繼續 37 continue; 38 } else if (!rc) 39 continue; // 如果fd上沒有數據到達,繼續 40 41 if (FD_ISSET(mCtrlPipe[0], &read_fds)) { 42 char c = CtrlPipe_Shutdown; 43 TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1)); 44 if (c == CtrlPipe_Shutdown) { 45 break; 46 } 47 continue; 48 } 49 if (mListen && FD_ISSET(mSock, &read_fds)) { // 如果是CommandListener對象上有連接請求 50 struct sockaddr addr; 51 socklen_t alen; 52 int c; 53 54 do { 55 alen = sizeof(addr); 56 c = accept(mSock, &addr, &alen); // 接入連接請求 57 SLOGV("%s got %d from accept", mSocketName, c); 58 } while (c < 0 && errno == EINTR); // 如果是中斷導致失敗,重新接入 59 if (c < 0) { 60 SLOGE("accept failed (%s)", strerror(errno)); 61 sleep(1); 62 continue; // 接入發生錯誤,繼續循環 63 } 64 pthread_mutex_lock(&mClientsLock); 65 mClients->push_back(new SocketClient(c, true, mUseCmdNum)); // 把接入的socket連接加入到mClients,這樣再循環時就會監聽到它的數據到達 66 pthread_mutex_unlock(&mClientsLock); 67 } 68 69 /* Add all active clients to the pending list first */ 70 pendingList.clear(); 71 pthread_mutex_lock(&mClientsLock); 72 for (it = mClients->begin(); it != mClients->end(); ++it) { 73 SocketClient* c = *it; 74 int fd = c->getSocket(); 75 if (FD_ISSET(fd, &read_fds)) { 76 pendingList.push_back(c); // 如果mClients中的某個socket上有數據了,把它加入到pendingList列表中 77 c->incRef(); 78 } 79 } 80 pthread_mutex_unlock(&mClientsLock); 81 82 /* Process the pending list, since it is owned by the thread,* there is no need to lock it */ 83 while (!pendingList.empty()) { // 處理pendingList列表 84 /* Pop the first item from the list */ 85 it = pendingList.begin(); 86 SocketClient* c = *it; 87 pendingList.erase(it); // 把處理了的socket從pendingList列表中刪除 88 /* Process it, if false is returned, remove from list */ 89 if (!onDataAvailable(c)) { 90 release(c, false); // 調用release()函數-->調用onDataAvailable()方法 91 } 92 c->decRef(); 93 } 94 } 95 }
SocketListener::runListener是線程真正執行的函數。
以上runListener()函數雖然比較長,但這是一段標准的處理混合socket連接的代碼,對於我們編寫socket的程序大有幫助,這裡先做簡單了解。
<--------接下來,我們繼續分析......-------->
runListener()函數收到從驅動傳遞的數據或者MountService傳遞的數據後,調用onDataAvailable()函數來處理,FrameworkListener類和NetlinkListener類都會重載這個函數。
首先來分析一下NetlinkListener類的onDataAvailable()函數是如何實現的!
直接上代碼:
1 bool NetlinkListener::onDataAvailable(SocketClient *cli) 2 { 3 int socket = cli->getSocket(); 4 ssize_t count; 5 uid_t uid = -1; 6 /*從socket中讀取kernel發送來的uevent消息*/ 7 count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid)); 8 if (count < 0) { // 如果count<0,進行錯誤處理 9 if (uid > 0) 10 LOG_EVENT_INT(65537, uid); 11 return false; 12 } 13 14 NetlinkEvent *evt = new NetlinkEvent(); // 創建NetlinkEvent對象 15 if (evt->decode(mBuffer, count, mFormat)) { // 調用decode()函數 16 onEvent(evt); // 在NetlinkHandler中實現17 } else if (mFormat != NETLINK_FORMAT_BINARY) { 18 SLOGE("Error decoding NetlinkEvent"); 19 } 20 delete evt; 21 return true; 22 }
NetlinkListener類的onDataAvailable()函數首先調用uevent_kernel_multicast_uid_recv()函數來接收uevent消息。
接收到消息後,會創建NetlinkEvent對象,然後調用它的decode()函數對消息進行解碼,然後用得到的消息數據給NetlinkEvent對象的成員變量賦值。
最後onDataAvailable()函數調用了onEvent()函數繼續處理消息,onEvent()函數的代碼如下:
void NetlinkHandler::onEvent(NetlinkEvent *evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); // 調用VolumeManager的handleBlockEvent()函數來處理 } }
NetlinkHandler的onEvent()函數中會判斷event屬於哪個子系統的,如果屬於“block”(SD熱插拔),則調用VolumeManager的handleBlockEvent()函數來處理,代碼如下:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) { const char *devpath = evt->findParam("DEVPATH"); VolumeCollection::iterator it; bool hit = false; for (it = mVolumes->begin(); it != mVolumes->end(); ++it) { if (!(*it)->handleBlockEvent(evt)) { // 對每個DirectVolume對象,調用它handleBlockEvent來處理這個event hit = true; // 如果某個Volume對象處理了Event,則返回 break; } } ..... }
總結:本帖的源碼分析先到這裡為止,下一貼再分析DirectVolume對象的handleBlockEvent()函數以及CommandListener對象如何處理從MountService發送的命令數據,即我們之前還沒有討論的關於FrameworkListener的onDataAvailable()函數的代碼!
PS:希望對Android手機開發、IOS、以及游戲(純興趣,白菜)和Java EE感興趣的碼友們互粉,這樣我也能及時的看到你們的大神之作和經驗之貼,感謝感謝!
使用SVG圖片格式 SVG格式, 適應屏幕, 圖片較小, 還有很多優點, 參考. 本文講解如何使用SVG格式. SVG: Scalable Vector Graphic
Android--音樂播放器 1、什麼是Open Core? Open Core是 Android 多媒體框架的核心,所有 Android平台的音頻、視頻的采用以及播
【Android】如何寫一個JsBridge JsBridge 簡介 Android JsBridge 就是用來在 Android app的原生 java 代碼與 j
Android模擬登錄評論CSDN 有時候作為非官方開發的APP集成了官方的所有信息,但是現在需要實現另一個功能那就是登錄發表評論到官方的網站,而非官方的APP並不知道官