回顧:前貼主要分析了Android存儲系統的架構和原理圖,簡要的介紹了整個從Kernel-->Vold-->上層MountService之間的數據傳輸流程,在這樣的基礎上,我們開始今天的源碼分析!
【源碼分析】
1. Vold的main函數
Vold也是通過init進程啟動,它在init.rc中的定義如下:
XML/HTML代碼
- service vold /system/bin/vold
- class core
- socket vold stream 0660 root mount
- ioprio be 2
Vold服務放到了core分組,這就意味著系統啟動時,它就會被init進程啟動。這裡定義的一個socket,主要用語Vold和Java層的MountService通信。
Vold模塊的源代碼位於system/vold,我們看看入口函數main(),代碼如下:
C++代碼
- int main() {
- VolumeManager *vm;
- CommandListener *cl;
- NetlinkManager *nm;
- SLOGI("Vold 2.1 (the revenge) firing up");
-
- mkdir("/dev/block/vold", 0755); // 創建vold目錄
- klog_set_level(6);
- if (!(vm = VolumeManager::Instance())) { // 創建VolumeManager對象
- exit(1);
- };
-
- if (!(nm = NetlinkManager::Instance())) { // 創建NetlinkManager對象
- exit(1);
- };
- cl = new CommandListener(); // 創建CommandListener對象
- vm->setBroadcaster((SocketListener *) cl); // 建立vm和cl的聯系
- nm->setBroadcaster((SocketListener *) cl); // 建立nm和cl的聯系
- if (vm->start()) { // 啟動VolumeManager
- exit(1);
- }
- if (process_config(vm)) { // 創建文件/fstab.xxx中定義的Volume對象
- SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
- }
- cryptfs_pfe_boot();
- if (nm->start()) { // 啟動NetlinkManager,會調用NetlinkManager的start()方法,它創建PF_NETLINK socket,並開啟線程從此socket中讀取數據
- exit(1);
- }
- coldboot("/sys/block"); // 冷啟動,創建/sys/block下的節點文件
- if (cl->startListener()) { // 開始監聽Framework的socket
- exit(1);
- }
-
- while(1) { // 進入循環
- sleep(1000); // 主線程進入休眠
- }
- SLOGI("Vold exiting");
- exit(0);
- }
main函數的主要工作是創建3個對象:VolumeManager、NetlinkManager和CommandListener,同時將CommandListener對象分別設置到了VolumeManager對象和NetlinkManager對象中。
從前貼的架構圖中可以發現,CommandListener對象用於和Java層的NativeDaemonConnector對象進行socket通信,因此,無論是VolumeManager對象還是NetlinkManager對象都需要擁有CommandListener對象的引用。
2. 監聽驅動發出的消息—Vold的NetlinkManager對象
NetlinkManager對象的主要工作是監聽驅動發出的uevent消息。
main()函數中調用NetlinkManager類的靜態函數Instance()來創建NetlinkManager對象,代碼如下:
C++代碼
- NetlinkManager *NetlinkManager::Instance() {
- if (!sInstance)
- sInstance = new NetlinkManager(); // NetlinkManager對象通過靜態變量sInstance來引用,這意味著vold進程中只有一個NetlinkManager對象。
- return sInstance;
- }
看下NetlinkManager的構造函數,代碼如下:
C++代碼
- NetlinkManager::NetlinkManager() {
- mBroadcaster = NULL;
- }
NetlinkManager的構造函數只是對mBroadcaster進行了初始化。我們可以發現main()函數中通過調用NetlinkManager的setBroadcaster()函數來給變量mBroadcaster重新賦值。
C++代碼
- nm->setBroadcaster((SocketListener *) cl);
main()函數還調用了NetlinkManager的start()函數,我們觀察一下NetlinkManager中的start()方法,代碼如下:
C++代碼
- int NetlinkManager::start() {
- 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 = 0xffffffff;
- /*創建一個socket用於內核空間和用戶空間的異步通信,監控系統的hotplug事件*/
- if ((mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
- SLOGE("Unable to create uevent socket: %s", strerror(errno));
- return -1;
- }
- /*設置緩沖區大小為64KB*/
- if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
- SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
- goto out;
- }
- /*設置允許 SCM_CREDENTIALS 控制消息的接收*/
- if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
- SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
- goto out;
- }
- /*綁定 socket 地址*/
- if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
- SLOGE("Unable to bind uevent socket: %s", strerror(errno));
- goto out;
- }
- /*利用新創建的socket實例化一個NetlinkHandler類對象用於監聽socket,NetlinkHandler繼承了類NetlinkListener,NetlinkListener又繼承了類SocketListener*/
- mHandler = new NetlinkHandler(mSock);
- if (mHandler->start()) { // 啟動NetlinkHandler,調用NetlinkHandler的start()函數
- SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
- goto out;
- }
-
- return 0;
-
- out:
- close(mSock);
- return -1;
- }
我們看一下NetlinkManager的家族關系,如下圖:
上面的虛線為啟動時的調用流程:
(1) class NetlinkManager(在其start函數中創建了NetlinkHandler對象,並把創建的socket作為參數)
(2)class NetlinkHandler: public NetlinkListener(實現了onEvent)
(3) class NetlinkListener : public SocketListener(實現了onDataAvailable)
(4) class SocketListener(實現了runListener,在一個線程中通過select查看哪些socket有數據,通過調用onDataAvailable來讀取數據)。
總結:此貼主要分析了Vold的main()函數和NetlinkManager對象的源碼,通過源碼了解對象的創建時機和函數調用流程,下一貼會繼續從NetlinkHandler的start()方法深入分析,繼續源碼的學習,很快會與大家見面,歡迎大家批評指正,我們互相學習。