zoukankan      html  css  js  c++  java
  • android usb挂载分析---vold处理内核消息

     

    android usb挂载分析---vold处理内核消息

    分类: u盘挂载

    MountService启动之后 ,一切准备工作都 做好了,就等待碰上u盘插上了,

     这里要讲的是内核发信息给vold,我们在 vold启动这篇曾讲到过注册了一个到内核的UEVENT事件,当有u盘插入的时候,我们就能从这个套接字上收到内核所发出的消息了,这样就开始了vold的消息处理。

    先看下消息处理的流程:

    在SocketListener::runListener()函数 中,我们一直在select,等待某个连接的到来或者已经的套接字上数据的到来,看下代码:

    1. if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) 0) {  
    2.             SLOGE("select failed (%s)", strerror(errno));  
    3.             sleep(1);  
    4.             continue;  
    5.         } else if (!rc)  
    6.             continue;  
    7.   
    8.         if (FD_ISSET(mCtrlPipe[0], &read_fds))  
    9.             break;  
    10.         if (mListen && FD_ISSET(mSock, &read_fds)) {  
    11.             struct sockaddr addr;  
    12.             socklen_t alen = sizeof(addr);  
    13.             int c;  
    14.   
    15.             if ((c = accept(mSock, &addr, &alen)) 0) {  
    16.                 SLOGE("accept failed (%s)", strerror(errno));  
    17.                 sleep(1);  
    18.                 continue;  
    19.             }  
    20.             pthread_mutex_lock(&mClientsLock);  
    21.             mClients->push_back(new SocketClient(c));  
    22.             pthread_mutex_unlock(&mClientsLock);  
    23.         }  
    24.   
    25.         do {  
    26.             pthread_mutex_lock(&mClientsLock);  
    27.             for (it = mClients->begin(); it != mClients->end(); ++it) {  
    28.                 int fd = (*it)->getSocket();  
    29.                 if (FD_ISSET(fd, &read_fds)) {  
    30.                     pthread_mutex_unlock(&mClientsLock);  
    31.                     if (!onDataAvailable(*it)) {  
    32.                         close(fd);  
    33.                         pthread_mutex_lock(&mClientsLock);  
    34.                         delete *it;  
    35.                         it = mClients->erase(it);  
    36.                         pthread_mutex_unlock(&mClientsLock);  
    37.                     }  
    38.                     FD_CLR(fd, &read_fds);  
    39.                     pthread_mutex_lock(&mClientsLock);  
    40.                     continue;  
    41.                 }  
    42.             }  
    43.             pthread_mutex_unlock(&mClientsLock);  
    44.         } while (0);  
    45.     }  
    当某个套接字上有数据到来时,首先看这个套接字是不是listen的那个套接字,如果是则接收 连接并加到mClients链表中,否则说明某个套接字上有数据到来,这时里是我们注册到内核的那个套接字,调用onDataAvailable函数,这里由于多态调用的是NetlinkListener::onDataAvailable中的这个函数:
    1. bool NetlinkListener::onDataAvailable(SocketClient *cli)  
    2. {  
    3.     int socket = cli->getSocket();  
    4.     int count;  
    5.   
    6.     if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {  
    7.         SLOGE("recv failed (%s)", strerror(errno));  
    8.         return false;  
    9.     }  
    10.   
    11.     NetlinkEvent *evt = new NetlinkEvent();  
    12.     if (!evt->decode(mBuffer, count)) {  
    13.         SLOGE("Error decoding NetlinkEvent");  
    14.         goto out;  
    15.     }  
    16.   
    17.     onEvent(evt);  
    18. out:  
    19.     delete evt;  
    20.     return true;  
    21. }  

    调用recv接收数据,接着new一个NetlinkEvent并调用它的 decode函数对收到的数据进行解析:

    1. bool NetlinkEvent::decode(char *buffer, int size) {  
    2.     char *s = buffer;  
    3.     char *end;  
    4.     int param_idx = 0;  
    5.     int i;  
    6.     int first = 1;  
    7.   
    8.     end = s + size;  
    9.     while (s < end) {  
    10.         if (first) {  
    11.             char *p;  
    12.             for (p = s; *p != '@'; p++);  
    13.             p++;  
    14.             mPath = strdup(p);  
    15.             first = 0;  
    16.         } else {  
    17.             if (!strncmp(s, "ACTION=", strlen("ACTION="))) {  
    18.                 char *a = s + strlen("ACTION=");  
    19.                 if (!strcmp(a, "add"))  
    20.                     mAction = NlActionAdd;  
    21.                 else if (!strcmp(a, "remove"))  
    22.                     mAction = NlActionRemove;  
    23.                 else if (!strcmp(a, "change"))  
    24.                     mAction = NlActionChange;  
    25.             } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM=")))  
    26.                 mSeq = atoi(s + strlen("SEQNUM="));  
    27.             else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM=")))  
    28.                 mSubsystem = strdup(s + strlen("SUBSYSTEM="));  
    29.             else  
    30.                 mParams[param_idx++] = strdup(s);  
    31.         }  
    32.         s+= strlen(s) + 1;  
    33.     }  
    34.     return true;  
    35. }  
    这里会对消息进行解析,解析出ACTION、DEVPATH、SUBSYSTEM等等,下面看一下我抓的 一个u盘插入抓的log:
    1. D/NetlinkEvent(  946): s = add@/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda  
    2. D/NetlinkEvent(  946): s = ACTION=add  
    3. D/NetlinkEvent(  946): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda  
    4. D/NetlinkEvent(  946): s = SUBSYSTEM=block  
    5. D/NetlinkEvent(  946): s = MAJOR=8  
    6. D/NetlinkEvent(  946): s = MINOR=0  
    7. D/NetlinkEvent(  946): s = DEVNAME=sda  
    8. D/NetlinkEvent(  946): s = DEVTYPE=disk  
    9. D/NetlinkEvent(  946): s = NPARTS=1  
    10. D/NetlinkEvent(  946): s = SEQNUM=1058  
    11.   
    12. D/NetlinkEvent( 1206): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host1/target1:0:0/1:0:0:0/block/sda/sda1  
    13. D/NetlinkEvent( 1206): s = SUBSYSTEM=block  
    14. D/NetlinkEvent( 1206): s = MAJOR=8  
    15. D/NetlinkEvent( 1206): s = MINOR=1  
    16. D/NetlinkEvent( 1206): s = DEVNAME=sda1  
    17. D/NetlinkEvent( 1206): s = DEVTYPE=partition  
    18. D/NetlinkEvent( 1206): s = PARTN=1  
    19. D/NetlinkEvent( 1206): s = SEQNUM=1059  
    这个u盘只有一个分区,下面是有两个分区的log(一部分):
    1. D/NetlinkEvent( 1207): s = ACTION=add  
    2. D/NetlinkEvent( 1207): s = DEVPATH=/devices/platform/hiusb-ehci.0/usb1/1-2/1-2.2/1-2.2:1.0/host2/target2:0:0/2:0:0:0/block/sdb  
    3. D/NetlinkEvent( 1207): s = SUBSYSTEM=block  
    4. D/NetlinkEvent( 1207): s = MAJOR=8  
    5. D/NetlinkEvent( 1207): s = MINOR=16  
    6. D/NetlinkEvent( 1207): s = DEVNAME=sdb  
    7. D/NetlinkEvent( 1207): s = DEVTYPE=disk  
    8. D/NetlinkEvent( 1207): s = NPARTS=2  
    9. D/NetlinkEvent( 1207): s = SEQNUM=1086  
    可以看到,从内核收到的消息中我们能获得很多的信息。

    解析完后,就调用onEvent函数对消息进行处理,这里调用的是NetlinkHandler的onEvent函数:

    1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {  
    2.     VolumeManager *vm = VolumeManager::Instance();  
    3.     const char *subsys = evt->getSubsystem();  
    4.   
    5.     if (!subsys) {  
    6.         SLOGW("No subsystem found in netlink event");  
    7.         return;  
    8.     }  
    9.   
    10.     if (!strcmp(subsys, "block")) {  
    11.         vm->handleBlockEvent(evt);  
    12.     } else if (!strcmp(subsys, "switch")) {  
    13.         vm->handleSwitchEvent(evt);  
    14.     } else if (!strcmp(subsys, "usb_composite")) {  
    15.         vm->handleUsbCompositeEvent(evt);  
    16.     } else if (!strcmp(subsys, "battery")) {  
    17.     } else if (!strcmp(subsys, "power_supply")) {  
    18.     }  
    19. }  
    从上面 的log可以看出这里获取的subsys是block,所以调用handleBlockEvent函数 
    1. void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {  
    2.     const char *devpath = evt->findParam("DEVPATH");  
    3.   
    4.     /* Lookup a volume to handle this device */  
    5.     VolumeCollection::iterator it;  
    6.     bool hit = false;  
    7.     for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {  
    8.         if (!(*it)->handleBlockEvent(evt)) {  
    9. #ifdef NETLINK_DEBUG  
    10.             SLOGD("Device '%s' event handled by volume %s ", devpath, (*it)->getLabel());  
    11. #endif  
    12.             hit = true;  
    13.             break;  
    14.         }  
    15.     }  
    16.   
    17.     if (!hit) {  
    18. #ifdef NETLINK_DEBUG  
    19.         SLOGW("No volumes handled block event for '%s'", devpath);  
    20. #endif  
    21.     }  
    22. }  
    mVolumes中我们在初始化的时候往里面add了 个DirectVolume,所以这里调用DirectVolume::handleBlockEvent
    1. int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {  
    2.     const char *dp = evt->findParam("DEVPATH");  
    3.   
    4.     PathCollection::iterator  it;  
    5.     for (it = mPaths->begin(); it != mPaths->end(); ++it) {  
    6.         if (!strncmp(dp, *it, strlen(*it))) {  
    7.             /* We can handle this disk */  
    8.             int action = evt->getAction();  
    9.             const char *devtype = evt->findParam("DEVTYPE");  
    10.   
    11.             if (action == NetlinkEvent::NlActionAdd) {  
    12.                 int major = atoi(evt->findParam("MAJOR"));  
    13.                 int minor = atoi(evt->findParam("MINOR"));  
    14.                 char nodepath[255];  
    15.   
    16.                 snprintf(nodepath,  
    17.                          sizeof(nodepath), "/dev/block/vold/%d:%d",  
    18.                          major, minor);  
    19.                 if (createDeviceNode(nodepath, major, minor)) {  
    20.                     SLOGE("Error making device node '%s' (%s)", nodepath,  
    21.                                                                strerror(errno));  
    22.                 }  
    23.                 if (!strcmp(devtype, "disk")) {  
    24.                     handleDiskAdded(dp, evt);  
    25.                 } else {  
    26.                     handlePartitionAdded(dp, evt);  
    27.                 }  
    28.             } else if (action == NetlinkEvent::NlActionRemove) {  
    29.                 if (!strcmp(devtype, "disk")) {  
    30.                     handleDiskRemoved(dp, evt);  
    31.                 } else {  
    32.                     handlePartitionRemoved(dp, evt);  
    33.                 }  
    34.             } else if (action == NetlinkEvent::NlActionChange) {  
    35.                 if (!strcmp(devtype, "disk")) {  
    36.                     handleDiskChanged(dp, evt);  
    37.                 } else {  
    38.                     handlePartitionChanged(dp, evt);  
    39.                 }  
    40.             } else {  
    41.                     SLOGW("Ignoring non add/remove/change event");  
    42.             }  
    43.   
    44.             return 0;  
    45.         }  
    46.     }  
    47.     errno = ENODEV;  
    48.     return -1;  
    49. }  

    mPaths我们在parse vold.fstab把相应的解析到的路径添加进去了,我们看下这个脚本:
    1. ev_mount sdcard /mnt/sdcard auto /devices/platform/hiusb-ehci.0 /devices/platform/hi_godbox-ehci.0  

    这里add的路径正好和上面 log打出来的路径相匹配,首先执行的handleDiskAdded,也就是在收到这样的消息的时候,提示有磁盘插入:
    1. void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {  
    2.     mDiskMajor = atoi(evt->findParam("MAJOR"));  
    3.     mDiskMinor = atoi(evt->findParam("MINOR"));  
    4.   
    5.     const char *tmp = evt->findParam("NPARTS");  
    6.     if (tmp) {  
    7.         mDiskNumParts = atoi(tmp);  
    8.     } else {  
    9.         SLOGW("Kernel block uevent missing 'NPARTS'");  
    10.         mDiskNumParts = 1;  
    11.     }  
    12.   
    13.     char msg[255];  
    14.   
    15.     int partmask = 0;  
    16.     int i;  
    17.     for (i = 1; i <= mDiskNumParts; i++) {  
    18.         partmask |= (1 << i);  
    19.     }  
    20.     mPendingPartMap = partmask;  
    21.   
    22.     if (mDiskNumParts == 0) {  
    23. #ifdef PARTITION_DEBUG  
    24.         SLOGD("Dv::diskIns - No partitions - good to go son!");  
    25. #endif  
    26.         setState(Volume::State_Idle);  
    27.     } else {  
    28. #ifdef PARTITION_DEBUG  
    29.         SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",  
    30.              mDiskNumParts, mPendingPartMap);  
    31. #endif  
    32.         setState(Volume::State_Pending);  
    33.     }  
    34.   
    35.     snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",  
    36.              getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);  
    37.     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,  
    38.                                              msg, false);  
    39. }  
    mDiskNumParts 不为0,将Volume的状态设置为State_Pending并向FrameWork层广播VolumeDiskInserted的消息,在setState函数中也会广播VolumeStateChange的消息给上层,接着就是handlePartitionAdded 这里是处理add /block/sda/sda*这样的消息的
    1. void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {  
    2.     int major = atoi(evt->findParam("MAJOR"));  
    3.     int minor = atoi(evt->findParam("MINOR"));  
    4.   
    5.     int part_num;  
    6.   
    7.     const char *tmp = evt->findParam("PARTN");  
    8.   
    9.     if (tmp) {  
    10.         part_num = atoi(tmp);  
    11.     } else {  
    12.         SLOGW("Kernel block uevent missing 'PARTN'");  
    13.         part_num = 1;  
    14.     }  
    15.   
    16.     if (part_num > mDiskNumParts) {  
    17.         mDiskNumParts = part_num;  
    18.     }  
    19.   
    20.     if (major != mDiskMajor) {  
    21.         SLOGE("Partition '%s' has a different major than its disk!", devpath);  
    22.         return;  
    23.     }  
    24. #ifdef PARTITION_DEBUG  
    25.     SLOGD("Dv:partAdd: part_num = %d, minor = %d ", part_num, minor);  
    26. #endif  
    27.     mPartMinors[part_num -1] = minor;  
    28.   
    29.     mPendingPartMap &= ~(1 << part_num);  
    30.     if (!mPendingPartMap) {  
    31. #ifdef PARTITION_DEBUG  
    32.         SLOGD("Dv:partAdd: Got all partitions - ready to rock!");  
    33. #endif  
    34.         if (getState() != Volume::State_Formatting) {  
    35.             setState(Volume::State_Idle);  
    36.         }  
    37.     } else {  
    38. #ifdef PARTITION_DEBUG  
    39.         SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);  
    40. #endif  
    41.     }  
    42. }  

    当mPendingPartMap减为0时,这时Volume的状态不为State_Formatting,将广播一条VolumeStateChange的消息。
    到这里,内核的消息基本就处理完了,当然这里讲的只是add的消息,还有remove,change消息等。。。这里就不做介绍了。
     
  • 相关阅读:
    MyBatis Generator自动生成MyBatis的映射代码
    ajax 文件上传,ajax
    【学生必备求职指南】好简历是怎样炼成的?毕业生简历实例点评版 转载
    FC8下备份linux系统
    Fedora 18 安装前指南
    Fedora 18安装Google输入法和云拼音
    mfc radio group 设置
    MySQL Server 安装
    【转载】COM组件设计与应用(四)——简单调用组件
    【转载】COM组件设计与应用(三)——数据类型
  • 原文地址:https://www.cnblogs.com/senior-engineer/p/4852079.html
Copyright © 2011-2022 走看看