該篇文章主要講解vold是如何啓動的,以及內部組件間的關係javascript
Android5.0 vold-總體架構
Android5.0 vold-註冊過程(上)
Android5.0 vold-註冊過程(下)java
由vold總體架構能夠知道, vold是個native程序,而且有三部分組成,首先來看下這個程序的入口node
android系統的啓動會先去解析init.rc文件,因此首先來看看init.rcandroid
service vold /system/bin/vold
class core socket vold stream 0660 root mount ioprio be 2複製代碼
從init.rc (/system/core/rootdir/init.rc)裏面能夠看出,vold可執行文件位置在手機系統的/system/bin/vold.
那/system/bin/vold這個可執行程序是由哪些c文件編譯而成的,讓咱們再來看看它的mk文件,位於/system/vold/Android.mkapi
LOCAL_SRC_FILES := \
main.cpp \
$(common_src_files)
LOCAL_MODULE := libvold複製代碼
這樣就根據init.rc和Android.mk文件找到vold的入口是在源碼目錄system/vold/main.cpp裏面架構
首先來看看main.cpp的main函數socket
int main() {
...
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
if (process_config(vm)) {
SLOGE( "Error reading configuration (%s)... continuing anyways",strerror(errno));
}
...
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
...
if (cl->startListener()) {
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
}複製代碼
看Instance方法名就能夠得知,vm和nm使用了單例模式實例化了對象,跳過他們,來看看new CommandListener作了些什麼事情.
在new CommandListener的時候,這裏會先調用父類FrameworkListener的構造方法,而後在註冊須要監聽的命令(registerCmd),若是沒有註冊,systemserver發過來的命令是沒法獲取的函數
CommandListener::CommandListener():FrameworkListener("vold", true) {
registerCmd(new DumpCmd());
...
registerCmd(new FstrimCmd());
};複製代碼
由於FrameworkListener又是繼承自SocketListener,再來看看SocketListener的構造方法oop
FrameworkListener::FrameworkListener(constchar*socketName, boolwithSeq) : SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);}
SocketListener::SocketListener(constchar*socketName, boollisten{
init(socketName, -1, listen, false);
}複製代碼
init函數並無作實際的事情,只是實例化了一些對象以備後面使用
接着日後看,查看源碼能夠發現,vm->start()並無作什麼事情,而是直接返回了0.
那咱們來看看process_config作了什麼事情post
process_config函數以下
#define FSTAB_PREFIX "/fstab."
static int process_config(VolumeManager *vm) {
...
property_get("ro.hardware", propbuf, "");
snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
fstab = fs_mgr_read_fstab(fstab_filename);
...
/* Loop through entries looking for ones that vold manages */
for(i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
DirectVolume *dv = NULL;
...
vm->addVolume(dv);
}
}
}複製代碼
經過getprop命令能夠知道ro.hardware的值爲qcom,因此這裏fstab_filename爲fstab.qcom(注:這裏可能根據不一樣的廠商會有不一樣的fstab文件)。
經過調用fs_mgr_read_fstab函數,獲得一個fatab的結構體。而後遍歷fstab的每一行,把每個掛載分區實例化一個DirectVolume對象 而後加入到VolumeManager的管理中去。
來看看fstab長什麼樣子
# Android fstab file.
#<src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK
/dev/block/mtdblock0 /system ext4 ro,barrier=1 wait
/dev/block/mtdblock1 /data ext4 noatime,nosuid,nodev,barrier=1,nomblk_io_submit wait,check
/dev/block/mtdblock2 /cache ext4 noatime,nosuid,nodev wait,check
/devices/platform/goldfish_mmc.0 auto vfat defaults voldmanaged=sdcard:auto複製代碼
通過前面一系列的操做,該實例化的對象已經實例化好了,須要作的準備工做也已經完成了,如今是萬事俱備只欠東風了。
咱們來看下開啓整個系統運做的按鈕cl->startListener, startListener的實現是在父類SocketListener裏面
int SocketListener::startListener(intbacklog) {
...
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
...
if (mListen && listen(mSock, backlog) < 0) {
...
if(pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
...
}
}
}
}複製代碼
這裏mSocketName的值爲vold,也就是前面FrameworkListener中傳進來的參數經過調用android_get_control_socket函數產生一個socket文件,而且調用listen方法監聽,這裏是基本的socket通訊的寫法。 而後啓用了一個線程pthread_create,而且執行了threadStart方法,threadStart方法也只是執行了runListener方法而已
void SocketListener::runListener() {
while(1) {
...
int fd = (*it)->getSocket();
...
if((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0 {
...
sleep(1);
continue;
} else if (!rc) ...
c = accept(mSock, &addr, &alen);
...
mClients->push_back(newSocketClient(c, true,mUseCmdNum));
}
...
SocketClient* c = *it;
...
if(!onDataAvailable(c)) {
release(c, false);
}
}複製代碼
這裏就是等待socket的寫端寫入數據,而後讀出相應的數據(eg 以前經過registerCmd註冊的一些命令)作相應的操做這個方法很簡單,當寫入端有數據寫入的時候,accept方法會獲得寫入的數據,封裝爲一個SocketClient對象而後調用onDataAvailable方法來處理獲得的數據值得一提的是爲了達到節省CPU的效果,這裏使用了select方法,該方法會使線程阻塞住,直到read_fds有數據的時候才喚醒.
onDataAvailable的實現是在子類FrameworkListener中,最終會調用dispatchCommand方法
該方法又最終會調用以前註冊的命令的runCommand方法這裏
vold的VolumeManager大體流程)已經講完了
NetlinkManager和VolumeManager十分類似,都只是作了一個監聽的動做,咱們來看看實現,從它的start方法看起仍是使用了socket方法創建一個socket,bind一個地址,在實例化一個NetlinkHandler對象
int NetlinkManager::start() {
...
if((mSock = socket(PF_NETLINK, SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0){
...
if (setsockopt (mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) { ...
if (setsockopt (mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { ...
if (bind (mSock, (structsockaddr *) &nladdr, sizeof(nladdr)) < 0) {
...
mHandler = new NetlinkHandler(mSock);
if(mHandler->start()) {
...
}複製代碼
查看代碼能夠發現NetlinkHandler繼承自NetlinkHandler,而NetlinkHandler又是繼承自SocketListener的。這和VolumeManager的結構十分類似
NetlinkHandler::NetlinkHandler(intlistenerSocket) : NetlinkListener(listenerSocket){
}
NetlinkListener::NetlinkListener(intsocket) : SocketListener(socket, false) {
mFormat = NETLINK_FORMAT_ASCII;
}複製代碼
NetlinkHandler的start方法最終會調用到SocketListener的startListener方法
這個方法在以前已經作過介紹,這裏不在講解,主要做用就是監聽socket端口,當有信息的時候調用onDataAvailable方法
這裏會將mBuffer的值封裝爲一個NetlinkEvent對象,而後調用onEvent
onEvent最終會調用到VolumeManager的handleBlockEvent方法
bool NetlinkListener::onDataAvailable(SocketClient *cli){
...
count =TEMP_FAILURE_RETRY(
uevent_kernel_multicast_uid_recv( socket, mBuffer, sizeof(mBuffer), &uid));
...
NetlinkEvent *evt = newNetlinkEvent();
if (evt->decode(mBuffer, count, mFormat)){
onEvent(evt);
}
...
}
voidNetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
...
vm->handleBlockEvent(evt);
}複製代碼
咱們來看看VolumeManager的handleBlockEvent方法
這裏遍歷了mVolumes,分別調用每一個volume對象的handleBlockEvent方法
前面已經講過process_config方法會解析fstab.qcom文件,並調用VolumeManager的addVolume添加volume
每個volume都是DirectVolume,因此最後調用到DirectVolume的handleBlockEvent方法
static int process_config (VolumeManager *vm){
...
DirectVolume *dv = NULL;
...
vm->addVolume(dv);
}
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
...
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if(!(*it)->handleBlockEvent(evt)) {
..
}
}
}複製代碼
handleBlockEvent會根據kernel傳遞過來的NetlinkEvent中的action作出相應的動做而後調用sendBroadcast通知上層kernel發出的信號
int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
...
int action = evt->getAction();
...
if (action == NetlinkEvent::NlActionAdd) {
...
if (!strcmp(devtype, "disk")) {
handleDiskAdded(dp, evt);
} else {
handlePartitionAdded(dp, evt);
}s
mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false);
...
} else if (action == NetlinkEvent::NlActionRemove) {
...
}else if (action == NetlinkEvent::NlActionChange) {
...
}
}複製代碼
一圖勝千言,上文中有關鍵的代碼流程,能夠結合流程圖來看纔能有一個總體的概念