关闭

vold挂载管理

标签: voldSDcard存储
432人阅读 评论(0) 收藏 举报
分类:

看了很长时间Vold存储模块的相关知识,也深入的研究一段时间的Android源码,打算把自己看过的经验之贴、参考资料和自己的一些见解,以帖子的形式发出来,供有兴趣的同仁们参考,有不对的地方请指正,我们相互交流。

1.1 vold的原理与机制分析

1.1.1 Vold 架构


从上图中可知:

·  Vold中的NetlinkManager模块接收来自Linux内核的uevent消息。例如SD卡的插拔等动作都会引起Kernel向NM发送uevent消息。

·  NetlinkManager将这些消息转发给VolumeManager模块。VolumeManager会对应做一些操作,然后把相关信息通过CommandListener发送给MountService,MountService根据收到的消息会发送相关的处理命令给VolumeManager做进一步的处理。例如待SD卡插入后,VolumeManager会将来自NetlinkManager的“Disk Insert”消息发送给MountService,而后MountService则发送“Mount”指令给Vold,指示它挂载这个SD卡。

·  CommandListener模块内部封装了一个Socket用于跨进程通信。它在Vold进程中属于监听端(即是服务端),而它的连接端(即客户端)则是MountService。它一方面接收来自MountService的控制命令(例如卸载存储卡、格式化存储卡等),另一方面VolumeManagerNetlinkManager模块又会通过它,将一些信息发送给MountService。

1.1.2初识Vold

下面来初步的认识Vold,代码在system\vold\main.cpp

int main() {
    mkdir("/dev/block/vold", 0755);  //创建一个目录/dev/block/vold

  //创建一个VolumeManager对象,该对象为单例模式
    if (!(vm = VolumeManager::Instance())) {
        SLOGE("Unable to create VolumeManager");
        exit(1);
    };

  //创建一个NetlinkManager对象,该对象为单例模式
    if (!(nm = NetlinkManager::Instance())) {
        SLOGE("Unable to create NetlinkManager");
        exit(1);
    };

  //创建一个CommandListener对象
    cl = new CommandListener();
    vm->setBroadcaster((SocketListener *) cl);
    nm->setBroadcaster((SocketListener *) cl);

//启动VolumeManager
    if (vm->start()) {
        SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
        exit(1);
    }

//根据fstab配置文件初始化VolumeManager
    if (process_config(vm)) {
        SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
    }

//启动NetlinkManager对象
    if (nm->start()) {
        SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }
//通过往/sys/block目录下对应的uevent文件写“add\n”来触发内核发送Uevent消息
    coldboot("/sys/block");

//启动CommandListener
    if (cl->startListener()) {
        SLOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }
}

1.2 NetlinkManager模块的分析

1.2.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来读取数据)

1.2.2 start的分析

NetlinkManager模块将使用Netlink套接字实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,一起看下面代码
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;  
    }  
    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {  
        SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));  
        return -1;  
    }  
    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {  
        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));  
        return -1;  
    }  
  
    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {  
        SLOGE("Unable to bind uevent socket: %s", strerror(errno));  
        return -1;  
    }  
    // 利用新创建的socket实例化一个NetlinkHandler类对象,NetlinkHandler继承了类NetlinkListener,      
    // NetlinkListener又继承了类SocketListener      
    mHandler = new NetlinkHandler(mSock);  
    if (mHandler->start()) {  //启动NetlinkHandler  
        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));  
        return -1;  
    }  
    return 0;  
} 
NetlinkHandler构造

NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}
继承父类
NetlinkListener::NetlinkListener(int socket) :
                            SocketListener(socket, false) {
    mFormat = NETLINK_FORMAT_ASCII;
}

这里又是构造了一个SockListener的实例,传入了上面创建的socket标识。

接着调用的start()函数,也是最终实现在SockListener的startListener()。

int NetlinkHandler::start() {  
    return this->startListener();  
}  
  
int SocketListener::startListener() {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
    }  
  
    if (mListen && listen(mSock, 4) < 0) {  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)  
        mClients->push_back(new SocketClient(mSock, false));  
  
    if (pipe(mCtrlPipe)) {  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
}  
  
void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  
    me->runListener();  
    pthread_exit(NULL);  
    return NULL;  
}  
  
void SocketListener::runListener() {  
  
    SocketClientCollection *pendingList = new SocketClientCollection();  
  
    while(1) { // 死循环,一直监听  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds); //清空文件描述符集read_fds   
  
        if (mListen) {  
            max = mSock;  
            FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds  
        }  
  
        FD_SET(mCtrlPipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds  
        if (mCtrlPipe[0] > max)  
            max = mCtrlPipe[0];  
  
        pthread_mutex_lock(&mClientsLock); //对容器mClients的操作需要加锁  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            int fd = (*it)->getSocket();  
            FD_SET(fd, &read_fds); ////遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds  
            if (fd > max)  
                max = fd;  
        }  
        pthread_mutex_unlock(&mClientsLock);  
        // 等待文件描述符中某一文件描述符或者说socket有数据到来  
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
            if (errno == EINTR)  
                continue;  
            SLOGE("select failed (%s)", strerror(errno));  
            sleep(1);  
            continue;  
        } else if (!rc)  
            continue;  
  
        if (FD_ISSET(mCtrlPipe[0], &read_fds))  
            break;  
        if (mListen && FD_ISSET(mSock, &read_fds)) { //监听套接字处理  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen); //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,将其添加到mClient容器  
            } while (c < 0 && errno == EINTR);  
            if (c < 0) {  
                SLOGE("accept failed (%s)", strerror(errno));  
                sleep(1);  
                continue;  
            }  
            pthread_mutex_lock(&mClientsLock);  
            mClients->push_back(new SocketClient(c, true));  
            pthread_mutex_unlock(&mClientsLock);  
        }  
  
        /* Add all active clients to the pending list first */  
        pendingList->clear();  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            int fd = (*it)->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {  
                pendingList->push_back(*it);  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
  
        /* Process the pending list, since it is owned by the thread, 
         * there is no need to lock it */  
        while (!pendingList->empty()) { //非监听套接字处理  
            /* Pop the first item from the list */  
            it = pendingList->begin();  
            SocketClient* c = *it;  
            pendingList->erase(it);  
            /* Process it, if false is returned and our sockets are 
             * connection-based, remove and destroy it */  
            // ****** onDataAvailable在NetlinkListener中实现*********  
             if (!onDataAvailable(c) && mListen) {  
                /* Remove the client from our array */  
                pthread_mutex_lock(&mClientsLock);  
                for (it = mClients->begin(); it != mClients->end(); ++it) {  
                    if (*it == c) {  
                        mClients->erase(it);  
                        break;  
                    }  
                }  
                pthread_mutex_unlock(&mClientsLock);  
                /* Remove our reference to the client */  
                c->decRef();  
            }  
        }  
    }  
    delete pendingList;  
}
SocketListener::runListener是线程真正执行的函数:mListen成员用来判定是否监听套接字,Netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用函数onDataAvailable读取数据
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
    int socket = cli->getSocket();
    ssize_t count;
    uid_t uid = -1;

    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
                                       socket, mBuffer, sizeof(mBuffer), &uid)); //从socket中读取event信息
    if (count < 0) {
        if (uid > 0)
            LOG_EVENT_INT(65537, uid);
        SLOGE("recvmsg failed (%s)", strerror(errno));
        return false;
    }

    NetlinkEvent *evt = new NetlinkEvent();
    if (!evt->decode(mBuffer, count, mFormat)) {   //调用NetlinkEvent解析event
        SLOGE("Error decoding NetlinkEvent");
    } else {
        onEvent(evt);  //传递event给子类NetlinkHandler处理
    }

    delete evt;
    return true;
}

到NetlinkHandler.cpp中:
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")) {   //EVENT为block类型
        int action = evt->getAction();

        vm->updatePullOutState(evt);
        vm->setHotPlug(true);
        vm->handleBlockEvent(evt);  //把event交给<span style="font-family: Arial; line-height: 26px;">VolumeManager处理</span>
        vm->setHotPlug(false);

    }

1.2.3 NM模块的总结

NM模块的功能就是从Kernel接收Uevent消息,然后转换成一个NetlinkEvent对象,最后会调用VM的处理函数来处理这个NetlinkEvent对象。

1.3 VolumeManager模块的分析

1.3.1 Vold使用VM模块的流程


  调用Instance创建一个VM对象。

  调用setBroadcaster设置CL对象

  调用start启动VM。

  调用process_config配置VM。

1.3.2 VM的创建

VolumeManager *VolumeManager::Instance() {
    if (!sInstance)
        sInstance = new VolumeManager();
    return sInstance;
}

VolumeManager::VolumeManager() {
    mDebug = false;
    mVolumes = new VolumeCollection();  //一个容器,保存<span style="color: rgb(85, 85, 85); font-family: 'microsoft yahei'; font-size: 15px; line-height: 35px;">Volume的集合</span>
    mActiveContainers = new AsecIdCollection();
    mBroadcaster = NULL;   //指向socketlisten,用于发送挂载事件
    mUmsSharingCount = 0;
    mSavedDirtyRatio = -1;
    mUmsDirtyRatio = dirtyRatio();
    mVolManagerDisabled = 0;
    mIsHotPlug= false;
    mUseBackupContainers =false;
    mIsFirstBoot = false;
    mIpoState = State_Ipo_Start;
}

1.3.3 VM的启动

int VolumeManager::start() {
    return 0;   //<span style="font-family: Arial; font-size: 14px; line-height: 26px;">VM的启动什么也没有做</span>
}

1.3.4 process_config分析

这个函数用于解析指定的配置文件,根据内容构造DirectVolume以及父类然后保存进VolumeManager的容器中,供VolumeManager用于挂载事件的管理
static int process_config(VolumeManager *vm)
{
    char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
    fstab = fs_mgr_read_fstab(fstab_filename);   //读取fstab文件
    if (!fstab) {
        SLOGE("failed to open %s\n", fstab_filename);
        return -1;
    }
    /* 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;
            flags = 0;
 
            /* Set any flags that might be set for this volume */
            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
                flags |= VOL_NONREMOVABLE;
            }
            if (fs_mgr_is_encryptable(&fstab->recs[i])) {
                flags |= VOL_ENCRYPTABLE;
            }
            /* Only set this flag if there is not an emulated sd card */
            if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
                !strcmp(fstab->recs[i].fs_type, "vfat")) {
                flags |= VOL_PROVIDES_ASEC;
            }
            dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
            if (dv->addPath(fstab->recs[i].blk_device)) {
                SLOGE("Failed to add devpath %s to volume %s",
                      fstab->recs[i].blk_device, fstab->recs[i].label);
                goto out_fail;
            }
            vm->addVolume(dv);  //添加到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

/devices/platform/mtk-msdc.0/mmc_host   auto      vfat      defaults        voldmanaged=sdcard0:emmc@fat,noemulatedsd
/devices/platform/mtk-msdc.1/mmc_host   auto      vfat      defaults        voldmanaged=sdcard1:auto


1.3.5 DirectVolume分析


DirectVolume从Volume类派生,可把它看成是一个外部存储卡(例如一张SD卡)在代码中的代表物。它封装了对外部存储卡的操作,例如加载/卸载存储卡、格式化存储卡等
DirectVolume构造
DirectVolume::DirectVolume(VolumeManager *vm,const char *label,
                           const char*mount_point, int partIdx) :
              Volume(vm, label, mount_point) {//初始化基类
   /*
      注意其中的参数:
     label为”sdcard”,mount_point为”/mnt/sdcard”,partIdx为1    
*/
   mPartIdx = partIdx;
//PathCollection定义为typedef android::List<char *> PathCollection
//其实就是一个字符串list
    mPaths= new PathCollection();
    for(int i = 0; i < MAX_PARTITIONS; i++)
       mPartMinors[i] = -1;
   mPendingPartMap = 0;
   mDiskMajor = -1;  //存储设备的主设备号
   mDiskMinor = -1;  //存储设备的次设备号,一个存储设备将由主次两个设备号标识。
   mDiskNumParts = 0;
   //设置状态为NoMedia
   setState(Volume::State_NoMedia);
}
//再来看addPath函数,它主要目的是添加设备在sysfs中的路径,
int DirectVolume::addPath(const char *path) {
   mPaths->push_back(strdup(path));
    return0;
}
addPath把和某个存储卡接口相关的设备路径与这个DirectVolume绑定到一起,并且这个设备路径和Uevent中的DEVPATH是对应的,这样就可以根据Uevent的DEVPATH找到是哪个存储卡的DirectVolume发生了变动。当然手机上目前只有一个存储卡接口,所以Vold也只有一个DirectVolume。

1.3.6 VM与NM交互

在分析NM模块的数据处理时发现,NM模块接收到Uevent事件后,会调用VM模块进行处理,如果Uevent是block子系统,则调用handleBlockEvent
void VolumeManager::handleBlockEvent(NetlinkEvent*evt) {
    constchar *devpath = evt->findParam("DEVPATH");
 
/*
前面在process_config中构造的DirectVolume对象保存在了mVolumes中,它的定义如下:
typedef android::List<Volume *>VolumeCollection,也是一个列表。
  注意它保存的是Volume指针,而我们的DirectVolume是从Volume派生的
*/
   VolumeCollection::iterator it;
    boolhit = false;
for (it = mVolumes->begin(); it !=mVolumes->end(); ++it) {
        //调用每个Volume的handleBlockEvent事件,实际上将调用
        //DirectVolume的handleBlockEvent函数。
        if(!(*it)->handleBlockEvent(evt)) {
           hit = true;
           break;
        }
    }
}
int DirectVolume::handleBlockEvent(NetlinkEvent*evt) {
    constchar *dp = evt->findParam("DEVPATH");
 
PathCollection::iterator  it;
//将Uevent的DEVPATH和addPath添加的路径进行对比,判断属不属于自己管理的范围。
    for(it = mPaths->begin(); it != mPaths->end(); ++it) {
        if(!strncmp(dp, *it, strlen(*it))) {
           int action = evt->getAction();
           const char *devtype = evt->findParam("DEVTYPE");
 
           if (action == NetlinkEvent::NlActionAdd) {
               int major = atoi(evt->findParam("MAJOR"));
               int minor = atoi(evt->findParam("MINOR"));
               char nodepath[255];
 
               snprintf(nodepath,
                         sizeof(nodepath),"/dev/block/vold/%d:%d",
                         major, minor);
                 //创建设备节点
               if (createDeviceNode(nodepath, major, minor)) {
                    ......
               }
               if (!strcmp(devtype, "disk")) {
                    handleDiskAdded(dp, evt);//添加一个磁盘
               } else {
                    /*
对于有分区的SD卡,先收到上面的“disk”消息,然后每个分区就会收到
                   一个分区添加消息。
                   */
                    handlePartitionAdded(dp,evt);
               }
           } else if (action == NetlinkEvent::NlActionRemove) {
                ......
           } else if (action == NetlinkEvent::NlActionChange) {
              ......
           }
           ......
           return 0;
        }
    }
    errno= ENODEV;
    return-1;
}

1.4 commandlisten模块分析

1.4.1 commandlisten构造

 /system/vold/main.cpp
cl = new CommandListener();  
vm->setBroadcaster((SocketListener *) cl);  
nm->setBroadcaster((SocketListener *) cl);  
  
...  
  
/* 
* Now that we're up, we can respond to commands 
*/  
if (cl->startListener()) {  
   SLOGE("Unable to start CommandListener (%s)", strerror(errno));  
   exit(1);  
}
构造函数/system/vold/CommandListener.cpp
CommandListener::CommandListener() :
                 FrameworkListener("vold", true) {
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd());
    registerCmd(new AsecCmd());
    registerCmd(new ObbCmd());
    registerCmd(new StorageCmd());
    registerCmd(new XwarpCmd());
    registerCmd(new CryptfsCmd());
    registerCmd(new FstrimCmd());
    //M{
#ifndef MTK_EMULATOR_SUPPORT
    registerCmd(new USBCmd());
#endif
    registerCmd(new CDROMCmd());
    //}M
#if defined (ENG_BUILD_ENG)    
    registerCmd(new SilkRoad());
#endif
}

构造一个父类FrameworkListener
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
                            SocketListener(socketName, true, withSeq) {  
    init(socketName, withSeq);  
}
void FrameworkListener::init(const char *socketName, bool withSeq) {  
    mCommands = new FrameworkCommandCollection();  
    errorRate = 0;  
    mCommandCount = 0;  
    mWithSeq = withSeq;  
}
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
    init(socketName, -1, listen, useCmdNum);
}
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
    mListen = listen;
    mSocketName = socketName;
    mSock = socketFd;
    mUseCmdNum = useCmdNum;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}

1.4.2 Command注册

CommandListener的构造函数中调用父类的注册函数register, 注册commad到FrameworkListener的mCommands容器中
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
    mCommands->push_back(cmd);
}
CommandListener::VolumeCmd::VolumeCmd() :
                 VoldCommand("volume") {
}
VoldCommand::VoldCommand(const char *cmd) :
              FrameworkCommand(cmd)  {
}
FrameworkCommand::FrameworkCommand(const char *cmd) {
    mCommand = cmd;
}

将volume这个command注册到mCommands这个容器中之后,目的是当FrameworkListerer从SlocketListener接收到command的时候,会依据mCommands 中的command进行解析筛选判断分发,调用对应的command执行类

1.4.3 startListener的分析和数据处理

int SocketListener::startListener() {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {//获取socket的文件描述符,这里是获取Vold这个socket的  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
        SLOGV("got mSock = %d for %s", mSock, mSocketName);  
    }  
  
    if (mListen && listen(mSock, 4) < 0) {  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)//是否正常监听socket  
        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  
    if (pipe(mCtrlPipe)) {//新建管道,保存文件描述符到数组  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//开了一个线程来处理  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
} 
开启线程用于监听
void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);//threadStart为static函数,上面开线程创建的时候传了this,这里需要转换一个一样bit位的SocketListener指针  
  
    me->runListener();//SocketListener真正的执行函数  
    pthread_exit(NULL);  
    return NULL;  
} 
对socket监听处理
void SocketListener::runListener() {  
  
    SocketClientCollection *pendingList = new SocketClientCollection();//暂存mClients中的SocketClient  
  
    while(1) {  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds);//清空文件描述符集read_fds   
  
        if (mListen) {  
            max = mSock;  
            FD_SET(mSock, &read_fds);//如果正常的监听,这里就把之前获得的vold的文件描述符添加进去  
        }  
  
        FD_SET(mCtrlPipe[0], &read_fds);//添加管道读取端的文件描述符  
        if (mCtrlPipe[0] > max)  
            max = mCtrlPipe[0];  
  
        pthread_mutex_lock(&mClientsLock);//加锁操作,多线程安全  
        for (it = mClients->begin(); it != mClients->end(); ++it) {//遍历mClients,获取fd 添加到read_fds  
            int fd = (*it)->getSocket();  
            FD_SET(fd, &read_fds);  
            if (fd > max)  
                max = fd;  
        }  
        pthread_mutex_unlock(&mClientsLock);//解锁  
        SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
  
//linux下socket编程的select,这里检测read_fds集合里面是否有可读的,也就是有没有数据过来,没有数据的文件描述符会从read_fds中被剔除,这里的select设置time out为NULL,阻塞操作,直到read_fds集合中描述符有变化  
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
            if (errno == EINTR)  
                continue;  
            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
            sleep(1);  
            continue;  
        } else if (!rc)  
            continue;  
  
        if (FD_ISSET(mCtrlPipe[0], &read_fds))//如果匿名管道有数据可读,就退出  
            break;  
        if (mListen && FD_ISSET(mSock, &read_fds)) {//如果是正常监听的 mListen 为true,然后mSock这个描述符有可读数据,就创建链接,新建异步处理的SocketClient加入到mClients容器,这里的mSock 是vold这个套接字的描述符  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen);  
                SLOGV("%s got %d from accept", mSocketName, c);  
            } while (c < 0 && errno == EINTR);  
            if (c < 0) {  
                SLOGE("accept failed (%s)", strerror(errno));  
                sleep(1);  
                continue;  
            }  
            pthread_mutex_lock(&mClientsLock);  
            mClients->push_back(new SocketClient(c, true, mUseCmdNum));  
            pthread_mutex_unlock(&mClientsLock);  
        }  
  
        /* Add all active clients to the pending list first */  
        pendingList->clear();  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {//把上面有请求建立链接的Client加入到pendingList 容器中,后面处理  
            int fd = (*it)->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {  
                pendingList->push_back(*it);  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
  
        /* Process the pending list, since it is owned by the thread, 
         * there is no need to lock it */  
        while (!pendingList->empty()) {//遍历处理  
            /* Pop the first item from the list */  
            it = pendingList->begin();  
            SocketClient* c = *it;  
            pendingList->erase(it);  
            /* Process it, if false is returned and our sockets are 
             * connection-based, remove and destroy it */  
            if (!onDataAvailable(c) && mListen) {//调用到FrameworkListener 中onDataAvailable处理Socket事件  
                /* Remove the client from our array */  
                SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);  
                pthread_mutex_lock(&mClientsLock);  
                for (it = mClients->begin(); it != mClients->end(); ++it) {  
                    if (*it == c) {  
                        mClients->erase(it);//处理完成之后,从容器中移除这次的监听到的SocketClient  
                        break;  
                    }  
                }  
                pthread_mutex_unlock(&mClientsLock);  
                /* Remove our reference to the client */  
                c->decRef();  
            }  
        }  
    }  
    delete pendingList;  
}  
SocketListener的核心处理函数是onDataAvailable
bool FrameworkListener::onDataAvailable(SocketClient *c) {  
    char buffer[255];  
    int len;  
  
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//读Socket 内容保存到buffer  
    if (len < 0) {  
        SLOGE("read() failed (%s)", strerror(errno));  
        return false;  
    } else if (!len)  
        return false;  
  
    int offset = 0;  
    int i;  
  
    for (i = 0; i < len; i++) {  
        if (buffer[i] == '\0') {//一次传入一个字符串  
           <span style="font-family: Arial; font-size: 14px; line-height: 26px; background-color: rgb(240, 247, 254);"> //分发命令,最终会调用对应命令对象的runCommand进行函数处理。</span>
            dispatchCommand(c, buffer + offset);  
            offset = i + 1;  
        }  
    }  
    return true;  
}  </span>
调用dispatchcommand
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
    FrameworkCommandCollection::iterator i;  
    int argc = 0;  
    char *argv[FrameworkListener::CMD_ARGS_MAX];  
    char tmp[255];  
  
...//解析判断command buffer  
  
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {//遍历之前register到FrameworkListener中的FrameworkCommand容器  
        FrameworkCommand *c = *i;  
  
        if (!strcmp(argv[0], c->getCommand())) {//其中有注册“Volume”,如果这里是Volume开头的command,那么就调用,Volume构造的时候所构造的父类FrameworkCommand中的runCommand函数  
            if (c->runCommand(cli, argc, argv)) {  
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
            }  
            goto out;  
        }  
    }   
} 
以Volume这个runcommand为例 /system/vold/CommandListener.cpp中

int CommandListener::VolumeCmd::runCommand(SocketClient *cli,  
                                                      int argc, char **argv) {  
    dumpArgs(argc, argv, -1);  
  
    if (argc < 2) {  
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false);  
        return 0;  
    }  
  
    VolumeManager *vm = VolumeManager::Instance();//获取已经存在的VolumeManager实例  
  
...  
  
else if (!strcmp(argv[1], "mount")) {//判断command 内容  
        if (argc != 3) {  
            cli->sendMsg(ResponseCode::CommandSyntaxError, "Usage: volume mount <path>", false);  
            return 0;  
        }  
        rc = vm->mountVolume(argv[2]);//交给VolumeManager来对Volume进行操作  
    }  
  
...  
  
} 

1.4.4 commandlisten 数据处理流程图




1.5 mountservice模块的介绍

1.5.1 mountservice构造

public MountService(Context context) {  
        mContext = context;  
  
        synchronized (mVolumesLock) {  
            readStorageListLocked(); // 解析/frameworks/base/core/res/res/xml/storage_list.xml保存volume到 MountService的list :mVolumes中  
        }  
  
        // XXX: This will go away soon in favor of IMountServiceObserver  
        mPms = (PackageManagerService) ServiceManager.getService("package");  
  
        mHandlerThread = new HandlerThread("MountService");  
        mHandlerThread.start();  
        mHandler = new MountServiceHandler(mHandlerThread.getLooper());//新建消息处理handler  
  
        // Watch for user changes  
        final IntentFilter userFilter = new IntentFilter();  
        userFilter.addAction(Intent.ACTION_USER_ADDED);  
        userFilter.addAction(Intent.ACTION_USER_REMOVED);  
        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);//注册广播接收  
  
        // Watch for USB changes on primary volume  
        final StorageVolume primary = getPrimaryPhysicalVolume();  
        if (primary != null && primary.allowMassStorage()) {  
            mContext.registerReceiver(  
                    mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);  
        }  
  
        // Add OBB Action Handler to MountService thread.  
        mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());  
  
        /* 
         * Create the connection to vold with a maximum queue of twice the 
         * amount of containers we'd ever expect to have. This keeps an 
         * "asec list" from blocking a thread repeatedly. 
         */  
        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25);//创建 vold 的监听接收,用于接收system中Vold的socket消息  
  
        Thread thread = new Thread(mConnector, VOLD_TAG);  
        thread.start();//启动线程,NativeDaemonConnector实现了Runnable接口,实现在 run中  
  
        // Add ourself to the Watchdog monitors if enabled.  
        if (WATCHDOG_ENABLE) {  
            Watchdog.getInstance().addMonitor(this);  
        }  
    } 

1.5.2构造了NativeDaemonConnector用来接收来自下层的socket消息

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,  
        int responseQueueSize, String logTag, int maxLogSize) {  
    mCallbacks = callbacks; //回调  
    mSocket = socket; // socket名称  
    mResponseQueue = new ResponseQueue(responseQueueSize);//构建一个响应队列  
    mSequenceNumber = new AtomicInteger(0);  
    TAG = logTag != null ? logTag : "NativeDaemonConnector";  
  }
开启监测线程的run方法
public void run() {  
    HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); //TAG 为 VoldConnector,这里新建一个名为VoldConnector.CallbackHandler的消息处理线程,用于下面接收到vold 的socket之后的处理  
    thread.start();  
    mCallbackHandler = new Handler(thread.getLooper(), this); //创建handler 用于分发消息  
     while (true) {  
        try {  
            listenToSocket();// while 循环 监听socket  
        } catch (Exception e) {  
            loge("Error in NativeDaemonConnector: " + e);  
            SystemClock.sleep(5000);  
        }  
    }  
} 
private void listenToSocket() throws IOException {  
    LocalSocket socket = null;  
  
    try {  
        socket = new LocalSocket();  //创建本地socket  
        LocalSocketAddress address = new LocalSocketAddress(mSocket,  
                LocalSocketAddress.Namespace.RESERVED);//获得服务端vold socket的地址  
  
        socket.connect(address);//连接  
  
        InputStream inputStream = socket.getInputStream();  
        synchronized (mDaemonLock) {  
            mOutputStream = socket.getOutputStream();  
        }//获取输入输出流  
  
        mCallbacks.onDaemonConnected();//回调,在MountService中执行,初始化一些Volume状态信息  
  
        byte[] buffer = new byte[BUFFER_SIZE];  
        int start = 0;  
  
        while (true) {  
            int count = inputStream.read(buffer, start, BUFFER_SIZE - start);//读取数据到buffer  
            if (count < 0) {//连接断开,跳出当前while ,外部while循环 重新调用该函数连接  
                loge("got " + count + " reading with start = " + start);  
                break;  
            }  
  
  
            // Add our starting point to the count and reset the start.  
            count += start;  
            start = 0;  
  
            for (int i = 0; i < count; i++) {  
                if (buffer[i] == 0) {  
                    final String rawEvent = new String(  
                            buffer, start, i - start, Charsets.UTF_8);  
                    log("RCV <- {" + rawEvent + "}");  
  
                    try {  
                        final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(  
                                rawEvent);   //解析成event 保存  
                        if (event.isClassUnsolicited()) {    //判断event的code范围 code >= 600 && code < 700  
                            // TODO: migrate to sending NativeDaemonEvent instances  
                            mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage( //发送消息,把event交给handle来分发处理  
                                    event.getCode(), event.getRawEvent()));  
                        } else {  
                            mResponseQueue.add(event.getCmdNumber(), event);//加入到响应队列  
                        }  
                    } catch (IllegalArgumentException e) {  
                        log("Problem parsing message: " + rawEvent + " - " + e);  
                    }  
  
                    start = i + 1;  
                }  
            }
在NativeDaemonConnector中的handle处理为:
public boolean handleMessage(Message msg) {  
    String event = (String) msg.obj;  
    try {  
        if (!mCallbacks.onEvent(msg.what, event, NativeDaemonEvent.unescapeArgs(event))) { //回调到MountService 的onEent函数  
            log(String.format("Unhandled event '%s'", event));  
        }  
    } catch (Exception e) {  
        loge("Error handling '" + event + "': " + e);  
    }  
    return true;  
} 
MountService中onEvent
public boolean onEvent(int code, String raw, String[] cooked) {  
        if (DEBUG_EVENTS) {  
            StringBuilder builder = new StringBuilder();  
            builder.append("onEvent::");  
            builder.append(" raw= " + raw);  
            if (cooked != null) {  
                builder.append(" cooked = " );  
                for (String str : cooked) {  
                    builder.append(" " + str);  
                }  
            }  
            Slog.i(TAG, builder.toString());  
        }  
        if (code == VoldResponseCode.VolumeStateChange) { //根据 Vold的Code 执行  
            /* 
             * One of the volumes we're managing has changed state. 
             * Format: "NNN Volume <label> <path> state changed 
             * from <old_#> (<old_str>) to <new_#> (<new_str>)" 
             */  
            notifyVolumeStateChange(  
                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),  
                            Integer.parseInt(cooked[10])); //更新状态  
        } else if ((code == VoldResponseCode.VolumeDiskInserted) ||  
                   (code == VoldResponseCode.VolumeDiskRemoved) ||  
                   (code == VoldResponseCode.VolumeBadRemoval))  
    ...  
  
                  if (code == VoldResponseCode.VolumeDiskInserted) { //如果接收到的是插入disk的消息,则执行挂载操作  
                new Thread() {  
                    @Override  
                    public void run() {  
                        try {  
                            int rc;  
                            if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {  
                                Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));  
                            }  
                        } catch (Exception ex) {  
                            Slog.w(TAG, "Failed to mount media on insertion", ex);  
                        }  
                    }  
                }.start();  
            }  
}

1.5.3 MountService下发command

MountService中对Volume的各种操作都是需要转换成符合Vold中socket command,这样Vold中才能正确的解析识别调用
private int doMountVolume(String path) {  
    int rc = StorageResultCode.OperationSucceeded;  
  
    final StorageVolume volume;  
    synchronized (mVolumesLock) {  
        volume = mVolumesByPath.get(path);  
    }  
  
    if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);  
    try {  
        mConnector.execute("volume", "mount", path);// 调用到NativeDaemonConnector中的execute  
    } 
NativeDaemonConnector的execute
public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)  
            throws NativeDaemonConnectorException {  
        final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();  
  
        final int sequenceNumber = mSequenceNumber.incrementAndGet();  
        final StringBuilder cmdBuilder =  
                new StringBuilder(Integer.toString(sequenceNumber)).append(' ');  
        final long startTime = SystemClock.elapsedRealtime();  
  
        makeCommand(cmdBuilder, cmd, args); //转换制作成标准的Command  
  
        final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */  
        log("SND -> {" + logCmd + "}");  
  
        cmdBuilder.append('\0');  
        final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */  
  
        synchronized (mDaemonLock) {  
            if (mOutputStream == null) {  
                throw new NativeDaemonConnectorException("missing output stream");  
            } else {  
                try {  
                    mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); //通过在listenToSocket中获取的输出流,写入转换好的sentCmd  
                } catch (IOException e) {  
                    throw new NativeDaemonConnectorException("problem sending command", e);  
                }  
            }  
        }  
}

1.5.4 Mountservice 数据处理流程图













0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:7368次
    • 积分:142
    • 等级:
    • 排名:千里之外
    • 原创:4篇
    • 转载:19篇
    • 译文:0篇
    • 评论:0条
    文章分类