你好,游客 登录 注册 搜索
背景:
阅读新闻

Android Vold 分析

Vold 消息接收及挂载/卸载处理部分分析

[日期:2011-02-09] 来源:Linux社区  作者:sustzombie [字体: ]

Android2.2 Vold 分析-(四)---20110106

Vold 消息接收及挂载/卸载处理部分分析

我们知道在关于Vold分析的第一篇“system/vold/main.cpp-----mian函数分析” 中,除了NetlinkManager 对象nm间接启动了startListner线程之外,还有一个CommandListener对象cl启动了startListener线程;
     cl->startListener()  调用SocketListener::startListener(),在startListener函数中启动线程pthread_create(&mThread, NULL, SocketListener::threadStart, this) ;
下面我们顺着这条线分析命令监听:
类的集成关系
    CommandListener ------> FrameworkListener ----------> SocketListener(基类)
所以此处是cl调用了基类SocketListener的成员函数SocketListener::startListener();

这一部分与Android2.2 Vold分析(二)中一样,不再赘述。
startListener()开启线程SocketListener::threadStart,并把cl(this)作为参数传递给线程函数SocketListener::threadStart();
在threadStart()函数中以传进来的this对象指针调用SocketListener::runListener(),在该函数中完成监听套接字的处理及非监听套接字的数据获取函数调用onDataAvailable(*it);
这里有两个问题:
    (1). 究竟调用那个类的onDataAvailable(*it);
    (2). 该线程处理的是那个套接字?是否为监听套接字?
下面先分析问题(2):
    在main.cpp中构建CommandListener对象时,除构建自身外还向其父类传递了参数"vold",如下:
CommandListener::CommandListener() :
                 FrameworkListener("vold") {
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd());
    registerCmd(new AsecCmd());
    registerCmd(new ShareCmd());
    registerCmd(new StorageCmd());
    registerCmd(new XwarpCmd());
}
同时构建该对像的父类FrameworkListener部分,并向其父类传递参数socketName, true(“vold”,true),套接字名称vold为监听套接字;
FrameworkListener::FrameworkListener(const char *socketName) :
                            SocketListener(socketName, true) {
    mCommands = new FrameworkCommandCollection();
}
同时构建该对像的父类的父类部分SocketListener;
SocketListener::SocketListener(const char *socketName, bool listen) {
    mListen = listen;             //true 是监听套接字
    mSocketName = socketName;
    mSock = -1;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}
这里会声明一个该对像自己的SocketClientCollection容器mClients;
这里可以回答问题(2)了:套接字“vold”在init进程启动vold是创建,并且为监听套接字,可以从init.rc中看出
**********************
init.rc部分
service vold /system/bin/vold
    socket vold stream 0660 root mount  //流式套接字
    ioprio be 2

**********************
int SocketListener::startListener() {

    if (!mSocketName && mSock == -1) {  }
    else if (mSocketName) {
        if ((mSock = android_get_control_socket(mSocketName)) < 0) { } //根据mSocketName 查找获得对应的socket文件描述符
    }

    if (mListen && listen(mSock, 4) < 0) {   }          //阻塞监听,如果有连接请求到来则正确返回执行下面的线程函数threadStart函数,如果没有则一直阻塞,或错误返回;另依当前的代码,当正确接收到一个链接请求返回后,不会再继续监听,所以此处只能是有一对连接;
    else if (!mListen)
        mClients->push_back(new SocketClient(mSock));
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  }
    return 0;
}
线程函数:传递的obj是this指针,调用这是CommandListener对象指针cl,所以此时this指的是cl;
void *SocketListener::threadStart(void *obj) {
     me->runListener();      //相当于cl调用runListener();
  }
真正的线程执行函数
该线程是cl对象指针调用,并且接收cl->sendBroadcast()发来的消息,进行处理;
void SocketListener::runListener() {
    while(1) {                                                 //无线循环
        SocketClientCollection::iterator it;
        fd_set read_fds;
        int rc = 0;
        int max = 0;

        FD_ZERO(&read_fds);

        if (mListen) {                               
            max = mSock;
            FD_SET(mSock, &read_fds);            //将监听套接字添加到文件描述符集read_fds;
        } 
        ... ...
        pthread_mutex_lock(&mClientsLock);
        for (it = mClients->begin(); it != mClients->end(); ++it) {
            FD_SET((*it)->getSocket(), &read_fds);         //将容器nClient中的sockets添加到文件描述符集read_fds,第一次为空
            if ((*it)->getSocket() > max)
                max = (*it)->getSocket();
        }
        pthread_mutex_unlock(&mClientsLock);

        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {     //等待文件描述符中某一文件描述符或者说socket有数据到来
            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 = sizeof(addr);
            int c;

            if ((c = accept(mSock, &addr, &alen)) < 0) {               //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,将其添加到mClient容器,cl调用执行这一步
                SLOGE("accept failed (%s)", strerror(errno));
                sleep(1);
                continue;
            }
            pthread_mutex_lock(&mClientsLock);
            mClients->push_back(new SocketClient(c));     //将以c为参数构建新的SocketClient,并添加到cl->mClient容器;
            pthread_mutex_unlock(&mClientsLock);
        }

        do {            //非监听套接字处理
            pthread_mutex_lock(&mClientsLock);
            for (it = mClients->begin(); it != mClients->end(); ++it) {
                int fd = (*it)->getSocket();
                if (FD_ISSET(fd, &read_fds)) {
                    pthread_mutex_unlock(&mClientsLock);
                    if (!onDataAvailable(*it)) {                   //调用相应的数据读取函数,读取数据,这里自然是调用cl->onDataAvailable(),但是cl类中没有该成员函数,那么在其父类FrameworkListener中查找
                        close(fd);
                        pthread_mutex_lock(&mClientsLock);
                        delete *it;
                        it = mClients->erase(it);
                        pthread_mutex_unlock(&mClientsLock);
                    }
                    FD_CLR(fd, &read_fds);
                    continue;
                }
            }
            pthread_mutex_unlock(&mClientsLock);
        } while (0);
    }
}

实际执行FrameworkListener::onDataAvailable(SocketClient *c)

bool FrameworkListener::onDataAvailable(SocketClient *c) {
    char buffer[255];
    int len;

    if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {
        SLOGE("read() failed (%s)", strerror(errno));
        return errno;
    } else if (!len)
        return false;

    int offset = 0;
    int i;

    for (i = 0; i < len; i++) {
        if (buffer[i] == '\0') {
            dispatchCommand(c, buffer + offset);    //分发命令
            offset = i + 1;
        }
    }
    return true;
}

分发命令,cl对象在构建是注册了许多命令,在这里查找,并执行相关命令
CommandListener::CommandListener() :
                 FrameworkListener("vold") {
    registerCmd(new DumpCmd());
    registerCmd(new VolumeCmd());
    registerCmd(new AsecCmd());
    registerCmd(new ShareCmd());
    registerCmd(new StorageCmd());
    registerCmd(new XwarpCmd());
}
调用其父类FrameworkListener的成员函数registerCmd将命令注册到mCommands容器;
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
    mCommands->push_back(cmd);
}

分析接收到的数据,解析出命令,在mCommands中进行查找,然后执行
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
    FrameworkCommandCollection::iterator i;
    int argc = 0;
    char *argv[FrameworkListener::CMD_ARGS_MAX];
          。。。 。。。   
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
        FrameworkCommand *c = *i;

        if (!strcmp(argv[0], c->getCommand())) {
            if (c->runCommand(cli, argc, argv)) {          //调用相应的命令执行
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
            }
            goto out;
        }
    }

    cli->sendMsg(500, "Command not recognized", false);
out:
    int j;
    for (j = 0; j < argc; j++)
        free(argv[j]);
    return;
}
到此vold分析完成,下面以磁盘挂载为例说明消息接收及处理部分函数,下一篇对vold的整体实现机制进行总结
例如,handleDiskAdded发送的消息是ResponseCode::VolumeDiskInserted;
snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)", getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) 执行c->runCommand(cli,argc,argv)
argc = 3;
然后调用vm->mountVolume(argv[2]);
int VolumeManager::mountVolume(const char *label) {
    Volume *v = lookupVolume(label);

    if (!v) {
        errno = ENOENT;
        return -1;
    }

    return v->mountVol();
}
真正的磁盘挂载执行函数
int Volume::mountVol() {
    dev_t deviceNodes[4];
    int n, i, rc = 0;
    char errmsg[255];

    if (getState() == Volume::State_NoMedia) {
        snprintf(errmsg, sizeof(errmsg),
                 "Volume %s %s mount failed - no media",
                 getLabel(), getMountpoint());
        mVm->getBroadcaster()->sendBroadcast(
                                         ResponseCode::VolumeMountFailedNoMedia,
                                         errmsg, false);
        errno = ENODEV;
        return -1;
    } else if (getState() != Volume::State_Idle) {
        errno = EBUSY;
        return -1;
    }

    if (isMountpointMounted(getMountpoint())) {
        SLOGW("Volume is idle but appears to be mounted - fixing");
        setState(Volume::State_Mounted);
        // mCurrentlyMountedKdev = XXX
        return 0;
    }

    n = getDeviceNodes((dev_t *) &deviceNodes, 4);
    if (!n) {
        SLOGE("Failed to get device nodes (%s)\n", strerror(errno));
        return -1;
    }

    for (i = 0; i < n; i++) {
        char devicePath[255];

        sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
                MINOR(deviceNodes[i]));

        SLOGI("%s being considered for volume %s\n", devicePath, getLabel());

        errno = 0;
        setState(Volume::State_Checking);

        if (Fat::check(devicePath)) {
            if (errno == ENODATA) {
                SLOGW("%s does not contain a FAT filesystem\n", devicePath);
                continue;
            }
            errno = EIO;
            /* Badness - abort the mount */
            SLOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
            setState(Volume::State_Idle);
            return -1;
        }

        /*
         * Mount the device on our internal staging mountpoint so we can
         * muck with it before exposing it to non priviledged users.
         */
        errno = 0;
        if (Fat::doMount(devicePath, "/mnt/secure/staging", false, false, 1000, 1015, 0702, true)) {
            SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
            continue;
        }

        SLOGI("Device %s, target %s mounted @ /mnt/secure/staging", devicePath, getMountpoint());

        protectFromAutorunStupidity();

        if (createBindMounts()) {
            SLOGE("Failed to create bindmounts (%s)", strerror(errno));
            umount("/mnt/secure/staging");
            setState(Volume::State_Idle);
            return -1;
        }

        /*
         * Now that the bindmount trickery is done, atomically move the
         * whole subtree to expose it to non priviledged users.
         */
        if (doMoveMount("/mnt/secure/staging", getMountpoint(), false)) {
            SLOGE("Failed to move mount (%s)", strerror(errno));
            umount("/mnt/secure/staging");
            setState(Volume::State_Idle);
            return -1;
        }
        setState(Volume::State_Mounted);
        mCurrentlyMountedKdev = deviceNodes[i];
        return 0;
    }

    SLOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
    setState(Volume::State_Idle);

    return -1;
}

linux
【内容导航】
第1页:-system/vold/main.cpp-----mian函数分析 第2页:Vold 中 Netlink事件通信机制分析
第3页:Vold 中 volumeManager分析 第4页:Vold 消息接收及挂载/卸载处理部分分析
相关资讯       Android教程 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款