2018年5月8日 星期二

VolD (Volume Daemon) 介紹

1.Vold (Volume Daemon)介紹

vold進程接收來自內核的外部設備消息,用於管理和控制Android平台外部存儲設備,包括SD插撥、掛載、卸載、格式化等;當外部設備發生變化時,內核通過Netlink發送uEvent格式的消息給用戶空間程序, Netlink是一種基於異步通信機制,在內核與用戶應用間進行雙向數據傳輸的特殊socket,用戶態應用使用標準的socket API就可以使用netlink提供的強大功能;

2.Vold 框架設計



Vold是native程序,用於管理和控制Android平台外部存儲設備的管控中心,是一個後台運行的進程。它與Java層的MountService交互,Vold接收來自kernel的uevent消息,然後向上層轉發,MountService接收來自vold的消息,同時也可以向vold發送控制命令。從以上Vold設計框架圖中可以看到,Vold有三個模塊,分別為NetlinkManager,VolumeManager,CommandListener。
NetlinkManager模塊專門接收來自Linux內核uevent消息,並將消息轉發給VolumeManager處理,VolumeManager模塊接著又把相關信息通過CommandListener發送給MountService,MountService根據收到的消息會發送相應的處理命令給VolumeManager,VolumeManager接收到命令後直接對外部存儲設備進行操作。
CommandListener模塊內部封裝良一個Socket用於跨進程通信,Java層的客戶端MountService就是通過該Socket和服務端Vold進行通信的。

1.Netlink介紹

Netlink是Linux系統中用戶空間進程和Kernel進行通信的一種機制,用戶空間進程可以接收來自Kernel的消息,同時也可以向Kernel發送一些控制命令。LINUX netlink機制一文中詳細介紹了Netlink的用法。

2.Uevent介紹

uevent和Linux的設備文件系統及設備模型有關,是sysfs向用戶空間發送的消息。消息格式實際上是一串字符串。當外部設備發生變化時,會引起Kernel發送Uevent消息;一般設備在/sys對應的目錄下有個叫uevent的文件,往該文件裡寫入指定數據,也會觸發Kernel發送和該設備相關的uevent消息,內核通過uevent告知外部存儲系統發生的變化。

3.Vold 源碼分析

 \ SYSTEM \的vold \ main.cpp中

[cpp] 查看純文本 
  1. int  main()   
  2. {  
  3.         VolumeManager * vm;  
  4.         CommandListener * cl;  
  5.         NetlinkManager * nm;  
  6.         //創建vold設備文件夾  
  7.         mkdir(“/ dev / block / vold” ,0755);  
  8.           
  9.         //初始化Vold相關的類實例 single  
  10.         vm = VolumeManager :: Instance();  
  11.         nm = NetlinkManager :: Instance();  
  12.           
  13.         //CommandListener 創建vold socket監聽上層消息  
  14.         cl =  new  CommandListener();  
  15.         vm-> setBroadcaster((SocketListener *)cl);  
  16.         nm-> setBroadcaster((SocketListener *)cl);  
  17.           
  18.         //啟動VolumeManager   
  19.         VM->開始();  
  20.           
  21.         //根據配置文件/etc/vold.fstab 初始化VolumeManager   
  22.         process_config(VM);  
  23.           
  24.         //啟動NetlinkManager socket監聽內核發送uevent  
  25.         NM->開始();  
  26.           
  27.         //向/sys/block/目錄下所有設備uevent文件寫入“add\n”,  
  28.         //觸發內核sysfs發送uevent消息  
  29.         冷啟動(“/ sys / block” );  
  30.           
  31.         //啟動CommandListener監聽vold socket  
  32.         CL-> startListener();  
  33.           
  34.         //最終我們將成為監控線程  
  35.         而(1){  
  36.             睡眠(1000);  
  37.         }  
  38.           
  39.         出口(0);  
  40. }  
/etc/vold.fstab的內容如下:
[純] 查看純文本 
  1. # ######################  
  2. ##常規設備安裝  
  3. ##  
  4. ##格式:dev_mount
  5. ## label - 卷的標籤  
  6. ## mount_point - 卷的安裝位置  
  7. ## part - Partition#(1 based),或'auto'表示第一個可用的分區。  
  8. ## - 源設備的sysfs路徑列表  
  9. ######################  
  10.   
  11. #安裝指定設備的第一個可用分區  
  12. #dev_mount sdcard / mnt / sdcard auto / block / mmcblk0  
  13. dev_mount internal / mnt / sdcard 19 /devices/platform/sprd-sdhci.3/mmc_host/mmc3  
  14. dev_mount sdcard / mnt / sdcard / external auto /devices/platform/sprd-sdhci.0/mmc_host/mmc0 </ span>  


process_config解析vold.fstab文件:

[cpp] 查看純文本 
  1. static int  process_config(VolumeManager * vm){   
  2.         //打開vold.fstab的配置文件  
  3.         fp = fopen(“/etc/vold.fstab” ,  “r” )  
  4.         //解析vold.fstab 配置存儲設備的掛載點  
  5.     while (fgets(line,  sizeof (line),fp)){  
  6.         const char  * delim =  “\ t” ;   
  7.         char  * type,* label,* mount_point,* part,* mount_flags,* sysfs_path;  
  8.   
  9.         type = strtok_r(line,delim,&save_ptr)  
  10.         label = strtok_r(NULL,delim,&save_ptr)  
  11.         mount_point = strtok_r(NULL,delim,&save_ptr)  
  12.               //判斷分區 auto沒有分區  
  13.         part = strtok_r(NULL,delim,&save_ptr)  
  14.         如果 (!strcmp(part,  “auto” )){  
  15.              //創建DirectVolume對象 相關的掛載點設備的操作  
  16.            dv =  new  DirectVolume(vm,label,mount_point,-1);  
  17.         }  else  {  
  18.            dv =  new  DirectVolume(vm,label,mount_point,atoi(part));  
  19.         }  
  20.                 //添加掛載點設備路徑  
  21.         while  ((sysfs_path = strtok_r(NULL,delim,&save_ptr))){  
  22.             DV->讓addpath(sysfs_path)  
  23.         }  
  24.               //將DirectVolume 添加到VolumeManager管理  
  25.               VM-> addVolume(DV);  
  26.     }  
  27.   
  28.     FCLOSE(FP);  
  29.     返回 0;  
  30. }  

4.Vold各模塊分析

前面介紹了Vold包含NetlinkManager,VolumeManager,CommandListener三大模塊,他們之間的關係如下:
接下來就分別針對每個模塊進行詳細分析。

1.NetlinkManager模塊

NetlinkManager模塊接收從Kernel發送的Uevent消息,解析轉換成NetlinkEvent對象;再將此NetlinkEvent對像傳遞給VolumeManager處理。
啟動流程
1)構造NetlinkManager實例:nm = NetlinkManager::Instance() 
2)設置事件廣播監聽:nm->setBroadcaster((SocketListener *) cl) 
3)啟動NetlinkManager:nm->start()

構造NetlinkManager實例

[cpp] 查看純文本 
  1. NetlinkManager * NetlinkManager :: Instance(){  
  2.     if  (!sInstance)  //採用單例模式創建NetlinkManager實例  
  3.         sInstance =  new  NetlinkManager();  
  4.     返回 sInstance;  
  5. }  

啟動NetlinkManager

[cpp] 查看純文本 
  1. int  NetlinkManager :: start(){  
  2.     //netlink使用的socket結構  
  3.     struct  sockaddr_nl nladdr;  
  4.           
  5.     //初始化socket數據結構  
  6.     memset(&nladdr,0,  sizeof (nladdr));  
  7.     nladdr.nl_family = AF_NETLINK;  
  8.     nladdr.nl_pid = getpid();  
  9.     nladdr.nl_groups = 0xffffffff;  
  10.     //創建socket PF_NETLINK類型  
  11.     mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT);  
  12.     //配置socket 大小  
  13.     setsockopt(mSock,SOL_SOCKET,SO_RCVBUFFORCE,&sz,  sizeof (sz);  
  14.     setsockopt(mSock,SOL_SOCKET,SO_PASSCRED,&on,  sizeof (on);  
  15.     //bindsocket地址  
  16.     bind(mSock,(struct  sockaddr *)&nladdr,  sizeof (nladdr);  
  17.           
  18.     //創建NetlinkHandler 傳遞socket標識,並啟動  
  19.     mHandler =  新的 NetlinkHandler(mSock);  
  20.     mHandler->開始();  
  21.     返回 0;  
  22. }  
在啟動NetlinkManager時,初始化socket用於創建NetlinkManager的屬性變量mHandle實例,並啟動NetlinkHandler,NetlinkHandler繼承於NetlinkListener,NetlinkListener又繼承於SocketListener,因此在構造NetlinkHandler實例時首先構造SocketListener及NetlinkListener,構造NetlinkListener對象的父類對象SocketListener:
[cpp] 查看純文本 
  1. SocketListener :: SocketListener(int  socketFd,  bool  listen){  
  2.     init(NULL,socketFd,listen,  false );  
  3. }  
[cpp] 查看純文本 
  1. void  SocketListener :: init(const char  * socketName,  int  socketFd,  bool  listen,  bool  useCmdNum){   
  2.     mListen = list;  
  3.     mSocketName = socketName;  
  4.     mSock = socketFd;  
  5.     mUseCmdNum = useCmdNum;  
  6.     pthread_mutex_init(&mClientsLock,NULL);  
  7.     mClients =  new  SocketClientCollection();  
  8. }  
NetlinkListener構造函數:
[cpp] 查看純文本 
  1. NetlinkListener :: NetlinkListener(int  socket):  
  2.                             SocketListener(socket,  false ){  
  3.     mFormat = NETLINK_FORMAT_ASCII;  
  4. }  
NetlinkHandler構造函數:
[cpp] 查看純文本 
  1. NetlinkHandler :: NetlinkHandler(int  listenerSocket):  
  2.                 NetlinkListener(listenerSocket){  
  3. }  
因此構造NetlinkHandler實例過程僅僅創建良一個socket客戶端連接。
NetlinkHandler開始:
[cpp] 查看純文本 
  1. int  NetlinkHandler :: start(){  
  2.     //父類startListener  
  3.     返回這個- > startListener();   
  4. }  
SocketListener開始:
[cpp] 查看純文本 
  1. int  SocketListener :: startListener(){  
  2.    // NetlinkHandler mListen為false   
  3.     if  (mListen && listen(mSock,4)<0 font="" nbsp="">
  4.         返回 -1;  
  5.     }  else if  (!mListen){   
  6.         //mListen為false 用於netlink消息監聽  
  7.         //創建SocketClient作為SocketListener 的客戶端   
  8.         mClients-> push_back(新的 SocketClient(mSock,  false ,mUseCmdNum));  
  9.     }  
  10.   
  11.     //創建匿名管道  
  12.     管(mCtrlPipe);  
  13.     //創建線程執行函數threadStart 參this  
  14.     pthread_create(&mThread,NULL,SocketListener :: threadStart,  this );  
  15. }  

uevent消息監聽線程

啟動NetlinkHandler過程通過創建一個SocketListener工作線程來監聽Kernel netlink發送的UEvent消息,該線程完成的工作:

[cpp] 查看純文本 
  1. void  * SocketListener :: threadStart(void  * obj){  
  2.   
  3.     //參數轉換  
  4.     SocketListener * me =  reinterpret_cast (obj);  
  5.   
  6.     我 - > runListener();  
  7.   
  8.     了pthread_exit(NULL);  
  9.   
  10.     返回 NULL;  
  11.   
  12. }  
SocketListener 線程消息循環:

[cpp] 查看純文本 
  1. void  SocketListener :: runListener(){  
  2.         // SocketClient列表  
  3.     SocketClientCollection * pendingList =  new  SocketClientCollection();  
  4.   
  5.     而(1){  
  6.         fd_set read_fds;  
  7.         // mListen為false  
  8.         if  (mListen){  
  9.             max = mSock;  
  10.             FD_SET(mSock,&read_fds);  
  11.         }  
  12.         //加入一組文件描述符集合 選擇fd最大的max  
  13.         FD_SET(mCtrlPipe [0],&read_fds);  
  14.         的pthread_mutex_lock(&mClientsLock);  
  15.         for  (it = mClients-> begin(); it!= mClients-> end(); ++ it){  
  16.             int  fd =(* it) - > getSocket();  
  17.             FD_SET(fd,&read_fds);  
  18.             如果 (fd> max)  
  19.                 max = fd;  
  20.         }  
  21.         調用pthread_mutex_unlock(&mClientsLock);  
  22.           
  23.         //監聽文件描述符是否變化  
  24.         rc = select(max + 1,&read_fds,NULL,NULL,NULL);  
  25.         //匿名管道被寫,退出線程  
  26.         如果 (FD_ISSET(mCtrlPipe [0],&read_fds))  
  27.             休息;  
  28.         // mListen為false  
  29.         if  (mListen && FD_ISSET(mSock,&read_fds)){  
  30.                 //mListen 為ture 表示正常監聽socket  
  31.             struct  sockaddr addr;  
  32.             做 {  
  33.                 //接收客戶端連接  
  34.                 c = accept(mSock,&addr,&alen);  
  35.             }  while  (c <0 amp="" eintr="" errno="=" font="" nbsp="">
  36.               
  37.             //此處創建一個客戶端SocketClient加入mClients列表中,異步延遲處理  
  38.             的pthread_mutex_lock(&mClientsLock);  
  39.             mClients-> push_back(新的 SocketClient(c,  true ,mUseCmdNum));  
  40.             調用pthread_mutex_unlock(&mClientsLock);  
  41.         }  
  42.   
  43.         / *先將所有活動客戶端添加到待處理列表中* /  
  44.         pendingList->清晰();  
  45.         //將所有有消息的Client加入到pendingList中  
  46.         的pthread_mutex_lock(&mClientsLock);  
  47.         for  (it = mClients-> begin(); it!= mClients-> end(); ++ it){  
  48.             int  fd =(* it) - > getSocket();  
  49.             if  (FD_ISSET(fd,&read_fds)){  
  50.                 pendingList->的push_back(*它);  
  51.             }  
  52.         }  
  53.         調用pthread_mutex_unlock(&mClientsLock);  
  54.   
  55.         //處理所有消息  
  56.         while  (!pendingList-> empty()){  
  57.             it = pendingList-> begin();  
  58.             SocketClient * c = * it;  
  59.             pendingList->擦除(它);  
  60.                //處理有數據發送的socket 虛函數  
  61.             if  (!onDataAvailable(c)&& mListen){  
  62.                // mListen為假  
  63.             }  
  64.         }  
  65.     }  
  66. }  
在消息循環中調用onDataAvailable處理消息,onDataAvailable是個虛函數,NetlinkListener重寫了此函數。

[cpp] 查看純文本 
  1. bool  NetlinkListener :: onDataAvailable(SocketClient * cli)  
  2. {  
  3.     //獲取socket id  
  4.     int  socket = cli-> getSocket();  
  5.     //接收netlink uevent消息  
  6.     count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(  
  7.                                  socket,mBuffer,  sizeof (mBuffer),&uid));  
  8.     //解析uevent消息為NetlinkEvent消息  
  9.     NetlinkEvent * evt =  new  NetlinkEvent();  
  10.     evt->解碼(mBuffer,count,mFormat);  
  11.       
  12.     //處理NetlinkEvent onEvent虛函數  
  13.     onEvent(evt);  
  14. }  
將接收的Uevent數據轉化成NetlinkEvent數據,調用onEvent處理,NetlinkListener子類NetlinkHandler重寫了此函數。

[cpp] 查看純文本 
  1. void  NetlinkHandler :: onEvent(NetlinkEvent * evt){  
  2.   
  3.     //獲取VolumeManager實例  
  4.     VolumeManager * vm = VolumeManager :: Instance();  
  5.   
  6.     //設備類型  
  7.     const char  * subsys = possible-> getSubsystem();   
  8.                    
  9.     //將消息傳遞給VolumeManager處理  
  10.     如果 (!strcmp(subsys,  “block” )){  
  11.         VM>作用塊事件(如果有的話);  
  12.   
  13.     }  
  14. }  
NetlinkManager 通過NetlinkHandler將接收到Kernel內核發送的Uenvet消息,轉化成了NetlinkEvent結構數據傳遞給VolumeManager處理,uevent消息的上傳流程:

2.VolumeManager模塊

啟動流程
1)構造VolumeManager對象實例
2)設置事件廣播監聽
3)啟動VolumeManager 
4)配置VolumeManager
VolumeManager類關係圖:
DirectVolume是一個實體存儲設備的抽象,通過系統調用直接操作存儲設備。VolumeManager的SocketListenner與NetlinkManager的SocketListenner有所不同的:
NetlinkManager構造的SocketListenner:Kernel與Vold通信;
VolumeManager構造的SocketListenner:Native Vold與Framework MountService 通信;
NetlinkManager與VolumeManager交互流程圖:

1.構造VolumeManager對象


[cpp] 查看純文本 
  1. VolumeManager * VolumeManager :: Instance(){  
  2.     如果 (!sInstance)  
  3.         sInstance =  new  VolumeManager();  
  4.     返回 sInstance;  
  5. }  
  6.   
  7. VolumeManager :: VolumeManager(){  
  8.     mDebug =  false ;  
  9.     mVolumes =  new  VolumeCollection();  
  10.     mActiveContainers =  new  AsecIdCollection();  
  11.     mBroadcaster = NULL;  
  12.     mUmsSharingCount = 0;  
  13.     mSavedDirtyRatio = -1;  
  14.     //當UMS處於活動狀態時,將臟比率設置為20  
  15.     mUmsDirtyRatio = 20;  
  16.     mVolManagerDisabled = 0;  
  17. }  

2.啟動VolumeManager


[cpp] 查看純文本 
  1. int  VolumeManager :: start(){  
  2.     返回 0;  
  3. }  
VolumeManager啟動過程什麼都沒有做。

3.uevent事件處理

前面NetlinkManager模塊中介紹到,NetlinkHandler在onEvent函數中,將NetlinkEvent事件轉交給VolumeManager處理:

[cpp] 查看純文本 
  1. void  NetlinkHandler :: onEvent(NetlinkEvent * evt){  
  2.         ……  
  3.             //將消息傳遞給VolumeManager處理  
  4.         如果 (!strcmp(subsys,  “block” )){  
  5.             VM>作用塊事件(如果有的話);  
  6.         }  
  7. }  

[cpp] 查看純文本 
  1. void  VolumeManager :: handleBlockEvent(NetlinkEvent * evt){  
  2.     //有狀態變化設備路徑  
  3.   const char  * devpath = evt-> findParam(“DEVPATH” );   
  4.   
  5.   //遍歷VolumeManager中所管理Volume對象(各存儲設備代碼抽象)  
  6.   for  (it = mVolumes-> begin(); it!= mVolumes-> end(); ++ it){  
  7.       if  (!(* it) - > handleBlockEvent(evt)){  
  8.           hit =  true ;  
  9.           休息;  
  10.       }  
  11. }  
VolumeManager將消息交給各個DirectVolume對象處理:

[cpp] 查看純文本 
  1. int  DirectVolume :: handleBlockEvent(NetlinkEvent * possible){  
  2.     //從NetlinkEvent消息中取出有狀態變化設備路徑  
  3.     const char  * dp = evt-> findParam(“DEVPATH” );   
  4.   
  5.     PathCollection :: iterator它;  
  6.     //遍歷所有的存儲設備  
  7.     for  (it = mPaths-> begin(); it!= mPaths-> end(); ++ it){  
  8.         //根據存儲設備路徑進行匹配  
  9.         如果 (!strncmp(dp,* it,strlen(* it))){  
  10.             /* 從NetlinkEvent消息中取出設備變化的動作 */  
  11.             int  action = evt-> getAction();  
  12.             /* 從NetlinkEvent消息中取出設備類型 */  
  13.             const char  * devtype = evt-> findParam(“DEVTYPE” );   
  14.             SLOGE(“DirectVolume :: handleBlockEvent()evt的DEVPATH =%s DEVTYPE =%s action =%d” ,dp,devtype,action);    //設備插入  
  15.             if  (action == NetlinkEvent :: NlActionAdd){  
  16.                 int  major = atoi(evt-> findParam(“MAJOR” ));  
  17.                 int  minor = atoi(evt-> findParam(“MINOR” ));  
  18.                 char  nodepath [255];  
  19.   
  20.                 snprintf(nodepath,sizeof (nodepath),  “/ dev / block / vold /%d:%d” ,major,minor);  
  21.                 SLOGE(“DirectVolume :: handleBlockEvent()NlActionAdd - / dev / block / vold /%d:%d \ n” ,major,minor);  
  22.                 if  (createDeviceNode(nodepath,major,minor)){  
  23.                     SLOGE(“錯誤使設備節點'%s'(%s)” ,nodepath,strerror(errno));  
  24.                 }  
  25.                 //新增磁盤  
  26.                 如果 (!strcmp(devtype,  “disk” )){  
  27.                     handleDiskAdded(dp,如果有的話);  
  28.                 //新增分區  
  29.                 }  else  {  
  30.                     handlePartitionAdded(dp,如果有的話);  
  31.                 }  
  32.             //設備移除  
  33.             }  else if  (action == NetlinkEvent :: NlActionRemove){   
  34.                 SLOGE(“volume partition%d:%d removed” ,atoi(evt-> findParam(“MAJOR” )),atoi(evt-> findParam(“MINOR” )));  
  35.                 //刪除磁盤  
  36.                 如果 (!strcmp(devtype,  “disk” )){  
  37.                     handleDiskRemoved(dp,如果有的話);  
  38.                 //刪除分區  
  39.                 }  else  {  
  40.                     handlePartitionRemoved(dp,如果有的話);  
  41.                 }  
  42.             //設備改變  
  43.             }  else if  (action == NetlinkEvent :: NlActionChange){   
  44.                 //磁盤變化  
  45.                 如果 (!strcmp(devtype,  “disk” )){  
  46.                     handleDiskChanged(dp,如果有的話);  
  47.                 //分區變化  
  48.                 }  else  {  
  49.                     handlePartitionChanged(dp,如果有的話);  
  50.                 }  
  51.             }  else  {  
  52.                     SLOGW(“忽略不添加/刪除/更改事件” );  
  53.             }  
  54.   
  55.             返回 0;  
  56.         }  
  57.     }  
  58.     errno = ENODEV;  
  59.     返回 -1;  
  60. }  
每一個Volume可能對應多個Path;即一個掛載點對應多個物理設備,因此VolumeManager中的每一個Volume對像都需要處理SD狀態變換消息,當新增一個disk時:
[cpp] 查看純文本 
  1. void  DirectVolume :: handleDiskAdded(const char  * devpath,NetlinkEvent *可選){   
  2.     //主次設備號  
  3.     mDiskMajor = atoi(evt-> findParam(“MAJOR” ));  
  4.     mDiskMinor = atoi(evt-> findParam(“MINOR” ));  
  5.           
  6.     //設備分區情況  
  7.     const char  * tmp = evt-> findParam(“NPARTS” );   
  8.   mDiskNumParts = atoi(tmp);  
  9.       
  10.     if  (mDiskNumParts == 0){  
  11.         //沒有分區,Volume狀態為Idle  
  12.         的setState(音量:: State_Idle);  
  13.     }  else  {  
  14.         //有分區未加載,設置Volume狀態Pending  
  15.         的setState(音量:: State_Pending);  
  16.     }  
  17.     //格式化通知msg:"Volume sdcard /mnt/sdcard disk inserted (179:0)"  
  18.     char  msg [255];  
  19.     snprintf(msg,  sizeof (msg),  “Volume%s%s disk inserted(%d:%d)” ,getLabel(),getMountpoint(),mDiskMajor,mDiskMinor);  
  20.     //調用VolumeManager中的Broadcaster——>CommandListener 發送此msg  
  21.     mVm-> getBroadcaster() - > sendBroadcast(ResponseCode :: VolumeDiskInserted,msg,  false );  
  22. }  
將新增一個disk以消息的方式通過CommandListener向MountService發送,由於CommandListener繼承於FrameworkListener,而FrameworkListener又繼承於SocketListener,CommandListener和FrameworkListener都沒用重寫父類的sendBroadcast方法,因此消息是通過SocketListener的 sendBroadcast函數向上層發送的,VolumeManager通知上層的消息流程圖:

[cpp] 查看純文本 
  1. void  SocketListener :: sendBroadcast(int  code,  const char  * msg,  bool  addErrno)    
  2. {  
  3.     的pthread_mutex_lock(&mClientsLock);  
  4.     //遍歷所有的消息接收時創建的Client SocketClient  
  5.     // SocketClient將消息通過socket(“vold”)通信  
  6.     for  (i = mClients-> begin(); i!= mClients-> end(); ++ i){  
  7.         (* i) - > sendMsg(code,msg,addErrno,  false );  
  8.     }  
  9.     調用pthread_mutex_unlock(&mClientsLock);  
  10. }  

3.CommandListener模塊

1)構造CommandListener對象實例
2)調用startListener函數啟動監聽

1.構造CommandListener對象

[cpp] 查看純文本 
  1. CommandListener :: CommandListener():  
  2.                 FrameworkListener(“vold” ,  true ){  
  3.   
  4.     //註冊Framework發送的相關命令 Command模式  
  5.   
  6.     registerCmd(new  DumpCmd());  
  7.   
  8.     registerCmd(new  VolumeCmd());  
  9.   
  10.     registerCmd(new  AsecCmd());  
  11.   
  12.     registerCmd(new  ObbCmd());  
  13.   
  14.     registerCmd(new  StorageCmd());  
  15.   
  16.     registerCmd(new  XwarpCmd());  
  17.   
  18.     registerCmd(new  CryptfsCmd());  
  19.   
  20. }  
父類FrameworkListener構造:
[cpp] 查看純文本 
  1. FrameworkListener :: FrameworkListener(const char  * socketName,  bool  withSeq):   
  2.                             SocketListener(socketName,  true ,withSeq){  
  3.     init(socketName,withSeq);  
  4. }  
直接調用init函數;
[cpp] 查看純文本 
  1. void  FrameworkListener :: init(const char  * socketName,  bool  withSeq){   
  2.     mCommands =  new  FrameworkCommandCollection();  
  3.     errorRate = 0;  
  4.     mCommandCount = 0;  
  5.     mWithSeq = withSeq;  
  6. }  
SocketListener的構造函數:
[cpp] 查看純文本 
  1. SocketListener :: SocketListener(const char  * socketName,  bool  listen,  bool  useCmdNum){   
  2.     init(socketName,-1,listen,useCmdNum);  
  3. }  
同樣調用init函數進行初始化:
[cpp] 查看純文本 
  1. void  SocketListener :: init(const char  * socketName,  int  socketFd,  bool  listen,  bool  useCmdNum){   
  2.     mListen = list;  
  3.     mSocketName = socketName;  
  4.     mSock = socketFd;  
  5.     mUseCmdNum = useCmdNum;  
  6.     pthread_mutex_init(&mClientsLock,NULL);  
  7.     mClients =  new  SocketClientCollection();  
  8. }  
構造CommandListener對象過程中,首先註冊了各種命令,並創建良一個socket客戶端連接。命令註冊過程:
[cpp] 查看純文本 
  1. void  FrameworkListener :: registerCmd(FrameworkCommand * cmd){  
  2.     mCommands->的push_back(CMD);  
  3. }  
將各種命令存放到mCommand列表中。

2.啟動CommandListener監聽

[cpp] 查看純文本 
  1. int  SocketListener :: startListener(){  
  2.     // mSocketName =“暴力”  
  3.     mSock = android_get_control_socket(mSocketName);  
  4.           
  5.     //NetlinkHandler mListen為true 監聽socket  
  6.     if  (mListen && <0 font="" nbsp="">
  7.         返回 -1;  
  8.     }  else if  (!mListen){   
  9.         mClients-> push_back(新的 SocketClient(mSock,  false ,mUseCmdNum));  
  10.     }  
  11.           
  12.     //創建匿名管道  
  13.     管(mCtrlPipe);  
  14.     //創建線程執行函數threadStart 參數this  
  15.     pthread_create(&mThread,NULL,SocketListener :: threadStart,  this );  
  16. }  
  17.   
  18. void  * SocketListener :: threadStart(void  * obj){  
  19.     SocketListener * me =  reinterpret_cast (obj);  
  20.     我 - > runListener();  
  21. }  
  22.   
  23. void  SocketListener :: runListener(){  
  24.     // SocketClient列表  
  25.     SocketClientCollection * pendingList =  new  SocketClientCollection();  
  26.   
  27.     而(1){  
  28.         fd_set read_fds;  
  29.                 //mListen 為true  
  30.         if  (mListen){  
  31.             max = mSock;  
  32.             FD_SET(mSock,&read_fds);  
  33.         }  
  34.                 //加入一組文件描述符集合 選擇fd最大的max select有關  
  35.         FD_SET(mCtrlPipe [0],&read_fds);  
  36.         的pthread_mutex_lock(&mClientsLock);  
  37.         for  (it = mClients-> begin(); it!= mClients-> end(); ++ it){  
  38.             int  fd =(* it) - > getSocket();  
  39.             FD_SET(fd,&read_fds);  
  40.             如果 (fd> max)  
  41.                 max = fd;  
  42.         }  
  43.         調用pthread_mutex_unlock(&mClientsLock);  
  44.           
  45.         //監聽文件描述符是否變化  
  46.         rc = select(max + 1,&read_fds,NULL,NULL,NULL);  
  47.         //匿名管道被寫,退出線程  
  48.         如果 (FD_ISSET(mCtrlPipe [0],&read_fds))  
  49.             休息;  
  50.   
  51.         //mListen 為true  
  52.         if  (mListen && FD_ISSET(mSock,&read_fds)){  
  53.             //mListen 為ture 表示正常監聽socket  
  54.             struct  sockaddr addr;  
  55.             做 {  
  56.                 c = accept(mSock,&addr,&alen);  
  57.             }  while  (c <0 amp="" eintr="" errno="=" font="" nbsp="">
  58.               
  59.             //創建一個客戶端SocketClient,加入mClients列表中 到異步延遲處理  
  60.             的pthread_mutex_lock(&mClientsLock);  
  61.             mClients-> push_back(新的 SocketClient(c,  true ,mUseCmdNum));  
  62.             調用pthread_mutex_unlock(&mClientsLock);  
  63.         }  
  64.   
  65.         / *先將所有活動客戶端添加到待處理列表中* /  
  66.         pendingList->清晰();  
  67.         //將所有有消息的Client加入到pendingList中  
  68.         的pthread_mutex_lock(&mClientsLock);  
  69.         for  (it = mClients-> begin(); it!= mClients-> end(); ++ it){  
  70.             int  fd =(* it) - > getSocket();  
  71.             if  (FD_ISSET(fd,&read_fds)){  
  72.                 pendingList->的push_back(*它);  
  73.             }  
  74.         }  
  75.         調用pthread_mutex_unlock(&mClientsLock);  
  76.   
  77.         / *處理待處理列表,因為它由線程擁有,* /  
  78.         while  (!pendingList-> empty()){  
  79.             it = pendingList-> begin();  
  80.             SocketClient * c = * it;  
  81.                //處理有數據發送的socket   
  82.             if  (!onDataAvailable(c)&& mListen){  
  83.                //mListen為true  
  84.                ……  
  85.             }  
  86.         }  
  87.     }  
  88. }  
啟動CommandListener監聽過程其實就是創建一個監聽的工作線程,用於監聽客戶端即MountService發過來的命令。


2018年4月27日 星期五

Android 手機工程模式 (隱藏模式)

每一家廠商所開發的軟體都會有工程模式,提供專業的工程師與研發人員設定及調整。
秉持著 Android 的開發的精神,盡情的測試使用吧!
系統測試代碼
*#*#4636#*#*
顯示手機資訊、電池資訊、電池記錄、使用統計資料、WiFi 資訊
*#*#7780#*#*
重設為原廠設定,不會刪除預設程式,及 SD 卡檔案。
*2767*3855#
重設為原廠設定,會刪除 SD 卡所有檔案。
*#*#34971539#*#*
顯示相機相機韌體版本,或更新相機韌體
*#*#7594#*#*
當長按關機按鈕時,會出現一個切換手機模式的視窗,包括:
靜音模式、飛航模式及關機,你可以用以上代碼,直接變成關機按鈕。
*#*#273283*255*663282*#*#*
開啟一個能讓你備份媒體文件的地方,例如相片、聲音及影片等
*#*#197328640#*#*
啟動服務模式,可以測試手機部分設置及更改設定
WLAN、 GPS 及藍牙測試的代碼
*#*#232339#*#*
*#*#526#*#*
*#*#528#*#*
以上3個選一 WLAN 測試
*#*#232338#*#* – 顯示 WiFi MAC 地址
*#*#1472365#*#* – GPS 測試
*#*#1575#*#* – 其他 GPS 測試
*#*#232331#*#* – 藍牙測試
*#*#232337#*# – 顯示藍牙裝置地址
*#*#8255#*#*–啟動 GTalk 服務監視器
顯示手機韌體的代碼
*#*#4986*2650468#*#* – PDA、 Phone、 H/W、 RFCallDate
*#*#1234#*#* – PDA 及 Phone
*#*#1111#*#* – FTA SW 版本
*#*#2222#*#* – FTA HW 版本
*#*#44336#*#* – PDA 、Phone、 CSC、 Build Time、 Changelist number
各項硬件測試
*#*#0283#*#* – Packet Loopback
*#*#0*#*#* – LCD 測試
*#*#0673#*#* 或 *#*#0289#*#* – Melody 測試
*#*#0842#*#* – 裝置測試,例如振動、亮度
*#*#2663#*#* – 觸控螢幕版本
*#*#2664#*#* – 觸控螢幕測試
*#*#0588#*#* – 接近感應器測試
*#*#3264#*#* – 記憶體版本

Linux /dev/kvm

原文 http://blog.51cto.com/1054383/1664143

/dev/kvm到底是什麼?

1./dev/kvm設備是kvm(kernel-based virtual machine)虛擬機器出來的一個設備檔

2.說到kvm,不得不提qemu,qemu是一個類比軟體,運行於linux的用戶空間。qemu可以類比我們能見到的所有作業系統,如在x86平臺上模擬andrio,ios等,當然也可以模擬windows,linux(gnu),BSD。由於是通過類比的方法來實現系統虛擬化,它產生的所有CPU指令都翻譯轉換一次,因此其性能非常低。

3.那麼問題來了,kvm提供了什麼呢?其實,kvm只提供了三個模組,分別是kvm.ko,kvm_intel.ko,kvm_amd.ko,後兩個模組是根據物理主機的CPU所屬廠家自動匹配的。

4.由上可知,qemu是一個使用者空間的程式,kvm是一個運行於內核空間的程式。為了提供一個整體的解決方案(包括用戶空間工具集[由qemu提供],管理各種設備(由kvm內核模組提供)),kvm開發團隊借用了qemu代碼,並作了一些修改,形成了一套工具,也就是qemu-kvm(不是linux中的命令)

5.如何讓qemu與kvm內核模組結合起來,這時,/dev/kvm就起作用了。/dev/kvm是一個字元設備,當qemu打開這個設備後,通過ioctl這個系統調用就可以獲得kvm模組提供的三個抽象物件:

    kvm:代表kvm模組本身,用來管理kvm版本資訊,創建一個vm(通過)

    vm:代表一個虛擬機器。通過vm的io_ctl介面,可以為虛擬機器創建vcpu,設置記憶體區間,創建中斷控制晶片,分配中斷等等

    vcpu:代表一個vcpu。通過vcpu的io_ctl介面,可以啟動或者暫停vcpu,設置vcpu的寄存器,為vcpu注入中斷等等

Qemu的使用方式:

    1.打開/dev/kvm設備

    2.通過KVM_CREATE_VM創建一個虛擬機器物件

    3.通過KVM_CREATE_VCPU為虛擬機器創建vcpu對象

    4.通過KVM_RUN設置vcpu運行起來

6.So,/dev/kvm只是kvm內核模組提供給使用者空間的一個介面,這個介面被qemu-kvm調用,通過ioctl系統調用就可以給使用者提供一個工具用以創建,刪除,管理虛擬機器等

2018年4月15日 星期日

Linux quota control

原文 https://blog.csdn.net/lqhbupt/article/details/7788694

想要管理硬盤配額,需要安裝quota和quotatool軟件包。

Ubuntu系統中的硬盤配額管理是默認禁用的,可以被系統管理員啟用和配置。
系統管理員可以使用一系列的與硬盤配額相關的命令:
  • quotacheck初始化quota數據庫文件;
  • edquota設置和編輯用戶配額;
  • set quota配置硬盤配額;
  • quotaon和quotaoff控製配額服務;
  • warnquota自動發郵件給超過硬盤配額的用戶。
即使安裝了quota軟件包,quotas也沒有啟用。
當quota服務啟用後,可以查看/etc/fstb文件,在每一行的第4個字段定義了硬盤配額的相關信息。例如:
      /dev/hda5 /home /ext3 defaults,usrquota,grpquota 1 1
說明了/home分區啟動了配額服務。
啟用了quota服務的分區的根目錄下能找到兩個文件quota.user和quota.group,
文件包含了具體的配額信息。這兩個文件的權限是600,也就是說用戶不能都寫文件,否則用戶可能會為自己分配更大的配額。

要初始化硬盤配額,分區必須被重新掛載。如下命令:
        sudo mount -o ro, remount partition_to_be_remounted mount_point

常用的硬盤配額命令主要有:
  • quotaon,quotaoff——啟動或關閉一個分區的配額服務
  • repquota——用戶和群組硬盤配額情況的狀態報告
  • quotacheck——更新配額狀態(對比新舊硬盤使用情況,在 fsck後運行)
  • edquota——基本配額管理命令

可以通過更改/etc/fstab文件的條目來啟動和關閉硬盤配額服務。
在第4個字段增加usrquota或grpquota選項。
也可以通過在分區的根目錄下創建quota.user和quota.group文件,
然後用quotaon命令啟動硬盤配額服務。
        sudo touch /quota.user(創建 quota.user文件)
        sudo quotaon -av(啟用硬盤配額服務)
        quota -v(用戶可以查詢自己的硬盤配額情況)