Android的儲存系統—Vold與MountService分析(三),voldmountservice

來源:互聯網
上載者:User

Android的儲存系統—Vold與MountService分析(三),voldmountservice

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_fds12 13             if (mListen) {            // 如果需要監聽14                 max = mSock;15                 FD_SET(mSock, &read_fds);               // 把mSock加入到read_fds16             }17 18             FD_SET(mCtrlPipe[0], &read_fds);                 // 把管道mCtrlPipe[0]也加入到read_fds19             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接入的socket24                   int fd = (*it)->getSocket();25                   FD_SET(fd, &read_fds);      // 遍曆容器mClients的所有成員,調用內嵌函式getSocket()擷取檔案描述符,並添加到檔案描述符集read_fds26                   if (fd > max) {                                     // 也加入到read_fds27                       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感興趣的碼友們互粉,這樣我也能及時的看到你們的大神之作和經驗之貼,感謝感謝!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.