一、Android热插拔事件处理流程图

Android热插拔事件处理流程如下图所示:

二、组成

1. NetlinkManager:

       全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp。该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。

2. VolumeManager:

      全称是VolumeManager.cpp位于Android 4.x源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。因为我们这里是SD的挂载,因此经过NetlinkManager处理过后的消息会分为五种,分别是:block,switch,usb_composite,battery,power_supply。这里SD卡挂载的事件是block。

3. DirectVolume:

       位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,Change,Noaction这四种。后文通过介绍Add事件展开。

4. Volume:

       位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。

5. CommandListener:

     该类位于位于/system/vold/CommandListener.cpp。通过vold socket与NativeDaemonConnector通信。

6. NativeDaemonConnector:

     该类位于frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递。

7.  MountService:

      位于frameworks/base/services/java/com.android.server/MountService.java。MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在Android系统中这是一个非常重要的类。

8. StorageManaer:

     位于frameworks/base/core/java/andriod/os/storage/StorageManager.java。在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有Storage相关项,同时Setting也注册了该类的监听器。而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获取SD卡状态。

三、典型流程描述 (SD卡挂载流程)

整个过程从Kernel检测到SD卡插入事件开始,之前的一些硬件中断的触发以及driver的加载这里并不叙述,一直到SD卡挂载消息更新到“Android——系统设置——存储”一项中。

       1.    Kernel发出SD卡插入uevent。

       2.    NetlinkHandler::onEvent()接收内核发出的uevent并进行解析。

       3.    VolumeManager::handlBlockEvent()处理经过第二步处理后的事件。

       4.    接下来调用DirectVolume:: handleBlockEvent()。

              在该方法中主要有两点需要注意:

              第一,程序首先会遍历mPath容器,寻找与event对应的sysfs_path是否存在与mPath容器中。

              第二,针对event中的action有4种处理方式:Add,Removed,Change,Noaction 。

              例如:在Add action中会有如下操作(因为我们这里所讲的是SD卡的挂载流程,因此以Add来说明),首先创建设备节点,其次对disk和partition两种格式的设备分别进行处理。SD卡属于disk类型。

       5.    经过上一步之后会调用DirectVolume::handleDiskAdded()方法,在该方法中会广播disk insert消息。

       6.    SocketListener::runListener会接收DirectVolume::handleDiskAdded()广播的消息。该方法主要完成对event中数据的获取,通过Socket。(PS:这里的SocketListener.cpp位于Android源码/system/core/libsysutils/src/中,后文的FramworkListener.cpp也是,之前自己找了很久 T_T)

       7.    调用FrameworkListener::onDataAvailable()方法处理接收到的消息内容。

       8.    FrameworkListener::dispatchCommand()该方法用于分发指令。

       9.    在FrameworkListener::dispatchCommand()方法中,通过runCommand()方法去调用相应的指令。

      10.   在/system/vold/CommandListener.cpp中有runCommand()的具体实现。在该类中可以找到这个方法:CommandListener::VolumeCmd::runCommand(),从字面意思上来看这个方法就是对Volume分发指令的解析。该方法中会执行“mount”函数:vm->mountVolume(arg[2])。

     11.    mountVolume(arg[2])在VolumeManager::mountVolume()中实现,在该方法中调用v->mountVol()。

     12.    mountVol()方法在Volume::mountVol()中实现,该函数是真正的挂载函数。(在该方法中,后续的处理都在该方法中,在Mount过程中会广播相应的消息给上层,通过setState()函数。)

     13.    setState(Volume::Checking);广播给上层,正在检查SD卡,为挂载做准备。

     14.    Fat::check();SD卡检查方法,检查SD卡是否是FAT格式。

     15.    Fat::doMount()挂载SD卡。

     至此,SD的挂载已算初步完成,接下来应该将SD卡挂载后的消息发送给上层,在13中也提到过,在挂载以及检查的过程中其实也有发送消息给上层的。

     16.    MountService的构造函数中会开启监听线程,用于监听来自vold的socket信息。

              Thread thread = new Thread(mConnector,VOLD_TAG); thread.start();

     17.    mConnector是NativeDaemonConnector的对象,NativeDaemonConnector继承了Runnable并Override了run方法。在run方法中通过一个while(true)调用ListenToSocket()方法来实现实时监听。

     18.    在ListenToSocket()中,首先建立与Vold通信的Socket Server端,然后调用MountService中的onDaemonConnected()方法。(PS:Java与Native通信可以通过JNI,那么Native与Java通信就需要通过Socket来实现了。Android中Native与Frameworks通信  这篇文章中有简介,感兴趣的朋友可以参考一下)

     19.    onDaemonConnected()方法是在接口INativeDaemonConnectorCallbacks中定义的,MountService实现了该接口并Override了onDaemonConnected()方法。该方法开启一个线程用于更新外置存储设备的状态,主要更新状态的方法也在其中实现。

     20.    然后回到ListenToSocket中,通过inputStream来获取Vold传递来的event,并存放在队列中。

     21.    然后这些event会在onDaemonConnected()通过队列的”队列.take()”方法取出。并根据不同的event调用updatePublicVolumeState()方法,在该方法中调用packageManagerService中的updateExteralState()方法来更新存储设备的状态。(注:这里不太理解packageManagerService中的unloadAllContainers(args)方法)

     22.    更新是通过packageHelper.getMountService().finishMediaUpdate()方法来实现的。

     23.    在updatePublicVolumeState()方法中,更新后会执行如下代码:

              bl.mListener.onStorageStateChanged();

              在Android源码/packages/apps/Settings/src/com.android.settings.deviceinfo/Memory.java代码中,实现了StorageEventListener 的匿名内部类,并Override了onStorageStateChanged();方法。因此在updatePublicVolumeState()中调用onStorageStateChanged();方法后,Memory.java中也会收到。在Memory.java中收到以后会在Setting界面进行更新,系统设置——存储中会更新SD卡的状态。从而SD卡的挂载从底层到达了上层。

四、Vold

1. Vold简介

Vold的全称是volume daemon。主要负责系统对大容量存储设备(USB/SD)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔。自Android 2.2开始,Vold升级为vold 2.0,配置文件路径在Android 4.0之后变为/etc/vold.fstab。

2.Vold工作流程

Vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理。

(1)创建监听

创建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制SD卡的挂载与卸载,这里所说的链接也就是socket。在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:

Service vold /system/bin/vold

             Socket vold stream 0660 root mount

             Iprio be 2

这样系统会在启动的时候创建与上层通信的socket,此socket name为"vold"。

在Android 4.0源码/system/vold路径下的main.cpp<NetlinkManager::start():socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT) >中创建了与内核通信的socket。在main.cpp中通过实例化VolumeManager和NetlinkManager时创建。

(2)引导

Vold进程启动时候会对现有的外部存储设备进行检查。首先加载并解析vold.fstab,并检查挂载点是否已被挂载。然后执行SD卡的挂载,最后处理USB大容量存储。因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点。

vold.fatab中最重要的语句:

dev_mount sdcard /mnt/sdcard auto /devices/platform/rk29_sdmmc.0/mmc_host/mmc0

dev_mount       <lable>     <mount_point>           <part>                   <sysfs_path…>

挂载命令            标签                挂载点              第几个分区              设备的sysfs paths

注:

       第几个分区:如果为auto则表示第1个分区。

       参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的)。

       如果vold.fstab解析无误,VolueManager将创建DirectVolume,若vold.fstab解析不存在或者打开失败,Vold将会读取Linux内核中的参数,此时如果参数中存在SDCARD(也就是SD的默认路径),VolumeManager则会创建AutoVolume,如果不存在这个默认路径那么就不会创建。

(3)事件处理

通过对两个socket的监听,完成对事件的处理以及对上层应用的响应。

a) Kernel发出uevent

       NetlinkManager检测到kernel发出的uevent,解析后调用NetlinkHandler::onEvent()方法。该方法会分别处理不同的事件,这里重要的事件有:

       “block”事件主要指Volume的mount、unmount、createAsec等。由VolumeManager的handleBlockEvent(evt)来处理,根据多态性最终将会调用AutoVolume或者DirectVolume的handleBlockEvent方法来处理。

       “switch”事件主要指Volume的connet、disconnet等。根据相关操作,改变设备参数(设备类型、挂载点等)通过CommandListener告知FrameWork层。

b) FrameWork发出控制命令

       与a)相反,CommandListener检测到FrameWork层的命令(MountService发出的命令)调用VolumeManager的函数,VolumeManager找出对应的Volume,调用Volume函数去挂载/卸载操作。而Volume类中的相关操作最终通过调用Linux函数完成。

五、Vold用户态

1. NetlinkManager

NetlinkManager负责与Kernel交互,通过PF_NETLINK来现。

Vlod启动代码如下(/system/vold/main.cpp):

  1. int main() {
  2. VolumeManager *vm;
  3. CommandListener *cl;
  4. NetlinkManager *nm;
  5. SLOGI("Vold 2.1 (the revenge) firing up");
  6. mkdir("/dev/block/vold", 0755);
  7. /* Create our singleton managers */
  8. if (!(vm = VolumeManager::Instance())) {
  9. SLOGE("Unable to create VolumeManager");
  10. exit(1);
  11. };
  12. if (!(nm = NetlinkManager::Instance())) {
  13. SLOGE("Unable to create NetlinkManager");
  14. exit(1);
  15. };
  16. cl = new CommandListener();
  17. vm->setBroadcaster((SocketListener *) cl);
  18. nm->setBroadcaster((SocketListener *) cl);
  19. if (vm->start()) {
  20. SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
  21. exit(1);
  22. }
  23. /* 解析/etc/vold.fstab文件,
  24. 读取type, label, mount_point, part
  25. 1) 构建DirectVolume对象 :如果part为auto, 则调用dv = new DirectVolume(vm, label, mount_point, -1);
  26. 2) 添加vold.fstab中定义的某一挂载项对应的sysfs_path到 DirectVolume对象的mPaths容器  dv->addPath(sysfs_path);
  27. 3) 将这个DirectVolume 对象添加到 VolumeManager对象的容器mVolumes中   vm->addVolume(dv);
  28. */
  29. if (process_config(vm)) {
  30. SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
  31. }
  32. /*会调用NetlinkManager类的start()方法,它创建PF_NETLINK socket,
  33. 并开启线程从此socket中读取数据*/
  34. if (nm->start()) {
  35. SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
  36. exit(1);
  37. }
  38. #ifdef USE_USB_MODE_SWITCH
  39. SLOGE("Start Misc devices Manager...");
  40. MiscManager *mm;
  41. if (!(mm = MiscManager::Instance())) {
  42. SLOGE("Unable to create MiscManager");
  43. exit(1);
  44. };
  45. mm->setBroadcaster((SocketListener *) cl);
  46. if (mm->start()) {
  47. SLOGE("Unable to start MiscManager (%s)", strerror(errno));
  48. exit(1);
  49. }
  50. G3Dev* g3 = new G3Dev(mm);
  51. g3->handleUsb();
  52. mm->addMisc(g3);
  53. #endif
  54. coldboot("/sys/block"); // 冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add\n” 字符也可以触发sysfs事件,相当执行了一次热插拔。
  55. //    coldboot("/sys/class/switch");
  56. /*
  57. * Now that we're up, we can respond to commands
  58. */
  59. if (cl->startListener()) {
  60. SLOGE("Unable to start CommandListener (%s)", strerror(errno));
  61. exit(1);
  62. }
  63. // Eventually we'll become the monitoring thread
  64. while(1) {
  65. sleep(1000);
  66. }
  67. SLOGI("Vold exiting");
  68. exit(0);
  69. }

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

2. NetlinkManager::start()

  1. int NetlinkManager::start() {
  2. struct sockaddr_nl nladdr;
  3. int sz = 64 * 1024;
  4. int on = 1;
  5. memset(&nladdr, 0, sizeof(nladdr));
  6. nladdr.nl_family = AF_NETLINK;
  7. nladdr.nl_pid = getpid();
  8. nladdr.nl_groups = 0xffffffff;
  9. // 创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件
  10. if ((mSock = socket(PF_NETLINK,
  11. SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
  12. SLOGE("Unable to create uevent socket: %s", strerror(errno));
  13. return -1;
  14. }
  15. if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
  16. SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));
  17. return -1;
  18. }
  19. if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
  20. SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
  21. return -1;
  22. }
  23. if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
  24. SLOGE("Unable to bind uevent socket: %s", strerror(errno));
  25. return -1;
  26. }
  27. // 利用新创建的socket实例化一个NetlinkHandler类对象,NetlinkHandler继承了类NetlinkListener,
  28. // NetlinkListener又继承了类SocketListener
  29. mHandler = new NetlinkHandler(mSock);
  30. if (mHandler->start()) {  //启动NetlinkHandler
  31. SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
  32. return -1;
  33. }
  34. return 0;
  35. }

把socket作为参数创建了NetlinkHandler对象,然后启动NetlinkHandler。

  1. int NetlinkHandler::start() {
  2. return this->startListener();
  3. }
  4. int SocketListener::startListener() {
  5. if (!mSocketName && mSock == -1) {
  6. SLOGE("Failed to start unbound listener");
  7. errno = EINVAL;
  8. return -1;
  9. } else if (mSocketName) {
  10. if ((mSock = android_get_control_socket(mSocketName)) < 0) {
  11. SLOGE("Obtaining file descriptor socket '%s' failed: %s",
  12. mSocketName, strerror(errno));
  13. return -1;
  14. }
  15. }
  16. if (mListen && listen(mSock, 4) < 0) {
  17. SLOGE("Unable to listen on socket (%s)", strerror(errno));
  18. return -1;
  19. } else if (!mListen)
  20. mClients->push_back(new SocketClient(mSock, false));
  21. if (pipe(mCtrlPipe)) {
  22. SLOGE("pipe failed (%s)", strerror(errno));
  23. return -1;
  24. }
  25. if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
  26. SLOGE("pthread_create (%s)", strerror(errno));
  27. return -1;
  28. }
  29. return 0;
  30. }
  31. void *SocketListener::threadStart(void *obj) {
  32. SocketListener *me = reinterpret_cast<SocketListener *>(obj);
  33. me->runListener();
  34. pthread_exit(NULL);
  35. return NULL;
  36. }
  37. void SocketListener::runListener() {
  38. SocketClientCollection *pendingList = new SocketClientCollection();
  39. while(1) { // 死循环,一直监听
  40. SocketClientCollection::iterator it;
  41. fd_set read_fds;
  42. int rc = 0;
  43. int max = -1;
  44. FD_ZERO(&read_fds); //清空文件描述符集read_fds
  45. if (mListen) {
  46. max = mSock;
  47. FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds
  48. }
  49. FD_SET(mCtrlPipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds
  50. if (mCtrlPipe[0] > max)
  51. max = mCtrlPipe[0];
  52. pthread_mutex_lock(&mClientsLock); //对容器mClients的操作需要加锁
  53. for (it = mClients->begin(); it != mClients->end(); ++it) {
  54. int fd = (*it)->getSocket();
  55. FD_SET(fd, &read_fds); ////遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
  56. if (fd > max)
  57. max = fd;
  58. }
  59. pthread_mutex_unlock(&mClientsLock);
  60. // 等待文件描述符中某一文件描述符或者说socket有数据到来
  61. if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
  62. if (errno == EINTR)
  63. continue;
  64. SLOGE("select failed (%s)", strerror(errno));
  65. sleep(1);
  66. continue;
  67. } else if (!rc)
  68. continue;
  69. if (FD_ISSET(mCtrlPipe[0], &read_fds))
  70. break;
  71. if (mListen && FD_ISSET(mSock, &read_fds)) { //监听套接字处理
  72. struct sockaddr addr;
  73. socklen_t alen;
  74. int c;
  75. do {
  76. alen = sizeof(addr);
  77. c = accept(mSock, &addr, &alen); //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,将其添加到mClient容器
  78. } while (c < 0 && errno == EINTR);
  79. if (c < 0) {
  80. SLOGE("accept failed (%s)", strerror(errno));
  81. sleep(1);
  82. continue;
  83. }
  84. pthread_mutex_lock(&mClientsLock);
  85. mClients->push_back(new SocketClient(c, true));
  86. pthread_mutex_unlock(&mClientsLock);
  87. }
  88. /* Add all active clients to the pending list first */
  89. pendingList->clear();
  90. pthread_mutex_lock(&mClientsLock);
  91. for (it = mClients->begin(); it != mClients->end(); ++it) {
  92. int fd = (*it)->getSocket();
  93. if (FD_ISSET(fd, &read_fds)) {
  94. pendingList->push_back(*it);
  95. }
  96. }
  97. pthread_mutex_unlock(&mClientsLock);
  98. /* Process the pending list, since it is owned by the thread,
  99. * there is no need to lock it */
  100. while (!pendingList->empty()) { //非监听套接字处理
  101. /* Pop the first item from the list */
  102. it = pendingList->begin();
  103. SocketClient* c = *it;
  104. pendingList->erase(it);
  105. /* Process it, if false is returned and our sockets are
  106. * connection-based, remove and destroy it */
  107. // ****** onDataAvailable在NetlinkListener中实现*********
  108. if (!onDataAvailable(c) && mListen) {
  109. /* Remove the client from our array */
  110. pthread_mutex_lock(&mClientsLock);
  111. for (it = mClients->begin(); it != mClients->end(); ++it) {
  112. if (*it == c) {
  113. mClients->erase(it);
  114. break;
  115. }
  116. }
  117. pthread_mutex_unlock(&mClientsLock);
  118. /* Remove our reference to the client */
  119. c->decRef();
  120. }
  121. }
  122. }
  123. delete pendingList;
  124. }

SocketListener::runListener是线程真正执行的函数:mListen成员用来判定是否监听套接字,Netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用函数onDataAvailable读取数据。

3. NetlinkListener::onDataAvailable

  1. bool NetlinkListener::onDataAvailable(SocketClient *cli)
  2. {
  3. int socket = cli->getSocket();
  4. ssize_t count;
  5. // 从socket中读取kernel发送来的uevent消息
  6. count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer)));
  7. if (count < 0) {
  8. SLOGE("recvmsg failed (%s)", strerror(errno));
  9. return false;
  10. }
  11. NetlinkEvent *evt = new NetlinkEvent();
  12. if (!evt->decode(mBuffer, count, mFormat)) {
  13. SLOGE("Error decoding NetlinkEvent");
  14. } else {
  15. onEvent(evt); //在NetlinkHandler中实现
  16. }
  17. delete evt;
  18. return true;
  19. }

4. NetlinkHandler::onEvent

  1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {
  2. VolumeManager *vm = VolumeManager::Instance();
  3. const char *subsys = evt->getSubsystem();
  4. if (!subsys) {
  5. SLOGW("No subsystem found in netlink event");
  6. return;
  7. }
  8. if (!strcmp(subsys, "block")) {
  9. if(uEventOnOffFlag)
  10. {
  11. SLOGW("####netlink event  block ####");
  12. evt->dump();
  13. }
  14. vm->handleBlockEvent(evt);
  15. #ifdef USE_USB_MODE_SWITCH
  16. } else if (!strcmp(subsys, "usb")
  17. || !strcmp(subsys, "scsi_device")) {
  18. SLOGW("subsystem found in netlink event");
  19. MiscManager *mm = MiscManager::Instance();
  20. mm->handleEvent(evt);
  21. #endif
  22. }
  23. }

5. uevent_kernel_multicast_recv

  1. /**
  2. * Like recv(), but checks that messages actually originate from the kernel.
  3. */
  4. ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length) {
  5. struct iovec iov = { buffer, length };
  6. struct sockaddr_nl addr;
  7. char control[CMSG_SPACE(sizeof(struct ucred))];
  8. struct msghdr hdr = {
  9. &addr,
  10. sizeof(addr),
  11. &iov,
  12. 1,
  13. control,
  14. sizeof(control),
  15. 0,
  16. };
  17. ssize_t n = recvmsg(socket, &hdr, 0);
  18. if (n <= 0) {
  19. return n;
  20. }
  21. if (addr.nl_groups == 0 || addr.nl_pid != 0) {
  22. /* ignoring non-kernel or unicast netlink message */
  23. goto out;
  24. }
  25. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);
  26. if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
  27. /* ignoring netlink message with no sender credentials */
  28. goto out;
  29. }
  30. struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
  31. if (cred->uid != 0) {
  32. /* ignoring netlink message from non-root user */
  33. goto out;
  34. }
  35. return n;
  36. out:
  37. /* clear residual potentially malicious data */
  38. bzero(buffer, length);
  39. errno = EIO;
  40. return -1;
  41. }

六、与Vold相关的Kernel态

  • 用户态创建的netlink sock被kernel保存在:nl_table[sk->sk_protocol].mc_list
  • Kernel态创建的netlink sock被kernel保存在:uevent_sock_list,上面的sk->sk_protocol为uevent_sock_list的协议, 二者只有协议一致才可以发送。

1. 创建kernel态sock

  • 在用户态的socket创建方式(/system/vold/NetlinkManager.cpp):
  1. if ((mSock = socket(PF_NETLINK,
  2. SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
  3. SLOGE("Unable to create uevent socket: %s", strerror(errno));
  4. return -1;
  5. }
  • 在Kernel的socket创建方式(/kernel/lib/kobject_uevent.c):
  1. static int uevent_net_init(struct net *net)
  2. {
  3. struct uevent_sock *ue_sk;
  4. ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);
  5. if (!ue_sk)
  6. return -ENOMEM;
  7. ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,
  8. 1, NULL, NULL, THIS_MODULE);
  9. if (!ue_sk->sk) {
  10. printk(KERN_ERR
  11. "kobject_uevent: unable to create netlink socket!\n");
  12. kfree(ue_sk);
  13. return -ENODEV;
  14. }
  15. mutex_lock(&uevent_sock_mutex);
  16. list_add_tail(&ue_sk->list, &uevent_sock_list);
  17. mutex_unlock(&uevent_sock_mutex);
  18. return 0;
  19. }

从上面的代码可知,此sock被创建之后,被增加到全局变量uevent_sock_list列表中,下面的分析围绕此列表进行。

  • netlink_kernel_create函数原型:
  1. struct sock *netlink_kernel_create(struct net *net, int unit, unsigned int groups,
  2. void (*input)(struct sk_buff *skb),
  3. struct mutex *cb_mutex, struct module *module)

1) struct net *net:是一个网络名字空间namespace,在不同的名字空间里面可以有自己的转发信息库,有自己的一套net_device等等。默认情况下都是使用init_net这个全局变量

2) int unit: 表示netlink协议类型,如 NETLINK_KOBJECT_UEVENT

3)  unsigned int groups: 组类型

4) void (*input)(struct sk_buff *skb):参数input则为内核模块定义的netlink消息处理函数,当有消息到达这个netlink socket时,该input函数指针就会被调用。函数指针input的参数skb实际上就是函数netlink_kernel_create返回的 struct sock指针,sock实际是socket的一个内核表示数据结构,用户态应用创建的socket在内核中也会有一个struct
sock结构来表示。

5) struct mutex *cb_mutex: 互斥销

6) struct module *module: 一般为THIS_MODULE

  • struct sock

用户态socket在kernel中的表示。

2. 相关数据结构

相关数据结构如下图所示:

3. 发送消息给用户空间

3.1 发送消息流程图

3.2 kobject_uevent_env

  1. /**
  2. * kobject_uevent_env - send an uevent with environmental data
  3. *
  4. * @action: action that is happening
  5. * @kobj: struct kobject that the action is happening to
  6. * @envp_ext: pointer to environmental data
  7. *
  8. * Returns 0 if kobject_uevent_env() is completed with success or the
  9. * corresponding error when it fails.
  10. */
  11. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
  12. char *envp_ext[])
  13. {
  14. struct kobj_uevent_env *env;
  15. const char *action_string = kobject_actions[action];
  16. const char *devpath = NULL;
  17. const char *subsystem;
  18. struct kobject *top_kobj;
  19. struct kset *kset;
  20. const struct kset_uevent_ops *uevent_ops;
  21. u64 seq;
  22. int i = 0;
  23. int retval = 0;
  24. #ifdef CONFIG_NET
  25. struct uevent_sock *ue_sk;
  26. #endif
  27. pr_debug("kobject: '%s' (%p): %s\n",
  28. kobject_name(kobj), kobj, __func__);
  29. /* search the kset we belong to */
  30. top_kobj = kobj;
  31. while (!top_kobj->kset && top_kobj->parent)
  32. top_kobj = top_kobj->parent;
  33. if (!top_kobj->kset) {
  34. pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
  35. "without kset!\n", kobject_name(kobj), kobj,
  36. __func__);
  37. return -EINVAL;
  38. }
  39. kset = top_kobj->kset;
  40. uevent_ops = kset->uevent_ops;
  41. /* skip the event, if uevent_suppress is set*/
  42. if (kobj->uevent_suppress) {
  43. pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
  44. "caused the event to drop!\n",
  45. kobject_name(kobj), kobj, __func__);
  46. return 0;
  47. }
  48. /* skip the event, if the filter returns zero. */
  49. if (uevent_ops && uevent_ops->filter)
  50. if (!uevent_ops->filter(kset, kobj)) {
  51. pr_debug("kobject: '%s' (%p): %s: filter function "
  52. "caused the event to drop!\n",
  53. kobject_name(kobj), kobj, __func__);
  54. return 0;
  55. }
  56. /* originating subsystem */
  57. if (uevent_ops && uevent_ops->name)
  58. subsystem = uevent_ops->name(kset, kobj);
  59. else
  60. subsystem = kobject_name(&kset->kobj);
  61. if (!subsystem) {
  62. pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
  63. "event to drop!\n", kobject_name(kobj), kobj,
  64. __func__);
  65. return 0;
  66. }
  67. /* environment buffer */
  68. env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
  69. if (!env)
  70. return -ENOMEM;
  71. /* complete object path */
  72. devpath = kobject_get_path(kobj, GFP_KERNEL);
  73. if (!devpath) {
  74. retval = -ENOENT;
  75. goto exit;
  76. }
  77. /* default keys */
  78. retval = add_uevent_var(env, "ACTION=%s", action_string);
  79. if (retval)
  80. goto exit;
  81. retval = add_uevent_var(env, "DEVPATH=%s", devpath);
  82. if (retval)
  83. goto exit;
  84. retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
  85. if (retval)
  86. goto exit;
  87. /* keys passed in from the caller */
  88. if (envp_ext) {
  89. for (i = 0; envp_ext[i]; i++) {
  90. retval = add_uevent_var(env, "%s", envp_ext[i]);
  91. if (retval)
  92. goto exit;
  93. }
  94. }
  95. /* let the kset specific function add its stuff */
  96. if (uevent_ops && uevent_ops->uevent) {
  97. retval = uevent_ops->uevent(kset, kobj, env);
  98. if (retval) {
  99. pr_debug("kobject: '%s' (%p): %s: uevent() returned "
  100. "%d\n", kobject_name(kobj), kobj,
  101. __func__, retval);
  102. goto exit;
  103. }
  104. }
  105. /*
  106. * Mark "add" and "remove" events in the object to ensure proper
  107. * events to userspace during automatic cleanup. If the object did
  108. * send an "add" event, "remove" will automatically generated by
  109. * the core, if not already done by the caller.
  110. */
  111. if (action == KOBJ_ADD)
  112. kobj->state_add_uevent_sent = 1;
  113. else if (action == KOBJ_REMOVE)
  114. kobj->state_remove_uevent_sent = 1;
  115. /* we will send an event, so request a new sequence number */
  116. spin_lock(&sequence_lock);
  117. seq = ++uevent_seqnum;
  118. spin_unlock(&sequence_lock);
  119. retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
  120. if (retval)
  121. goto exit;
  122. #if defined(CONFIG_NET)
  123. /* send netlink message */
  124. mutex_lock(&uevent_sock_mutex);
  125. list_for_each_entry(ue_sk, &uevent_sock_list, list) {
  126. struct sock *uevent_sock = ue_sk->sk;
  127. struct sk_buff *skb;
  128. size_t len;
  129. /* allocate message with the maximum possible size */
  130. len = strlen(action_string) + strlen(devpath) + 2;
  131. skb = alloc_skb(len + env->buflen, GFP_KERNEL);
  132. if (skb) {
  133. char *scratch;
  134. /* add header */
  135. scratch = skb_put(skb, len);
  136. sprintf(scratch, "%s@%s", action_string, devpath); //action_string+devpath
  137. /* copy keys to our continuous event payload buffer */
  138. for (i = 0; i < env->envp_idx; i++) {
  139. len = strlen(env->envp[i]) + 1;
  140. scratch = skb_put(skb, len);
  141. strcpy(scratch, env->envp[i]);
  142. }
  143. NETLINK_CB(skb).dst_group = 1;
  144. retval = netlink_broadcast_filtered(uevent_sock, skb,
  145. 0, 1, GFP_KERNEL,
  146. kobj_bcast_filter,
  147. kobj);
  148. /* ENOBUFS should be handled in userspace */
  149. if (retval == -ENOBUFS)
  150. retval = 0;
  151. } else
  152. retval = -ENOMEM;
  153. }
  154. mutex_unlock(&uevent_sock_mutex);
  155. #endif
  156. /* call uevent_helper, usually only enabled during early boot */
  157. if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
  158. char *argv [3];
  159. argv [0] = uevent_helper;
  160. argv [1] = (char *)subsystem;
  161. argv [2] = NULL;
  162. retval = add_uevent_var(env, "HOME=/");
  163. if (retval)
  164. goto exit;
  165. retval = add_uevent_var(env,
  166. "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
  167. if (retval)
  168. goto exit;
  169. retval = call_usermodehelper(argv[0], argv,
  170. env->envp, UMH_WAIT_EXEC);
  171. }
  172. exit:
  173. kfree(devpath);
  174. kfree(env);
  175. return retval;
  176. }
  1. /**
  2. * kobject_uevent - notify userspace by sending an uevent
  3. *
  4. * @action: action that is happening
  5. * @kobj: struct kobject that the action is happening to
  6. *
  7. * Returns 0 if kobject_uevent() is completed with success or the
  8. * corresponding error when it fails.
  9. */
  10. int kobject_uevent(struct kobject *kobj, enum kobject_action action)
  11. {
  12. return kobject_uevent_env(kobj, action, NULL);
  13. }

3.3 netlink_broadcast_filtered

  1. int netlink_broadcast_filtered(struct sock *ssk, struct sk_buff *skb, u32 pid,
  2. u32 group, gfp_t allocation,
  3. int (*filter)(struct sock *dsk, struct sk_buff *skb, void *data),
  4. void *filter_data)
  5. {
  6. struct net *net = sock_net(ssk);
  7. struct netlink_broadcast_data info;
  8. struct hlist_node *node;
  9. struct sock *sk;
  10. skb = netlink_trim(skb, allocation);
  11. info.exclude_sk = ssk;
  12. info.net = net;
  13. info.pid = pid;
  14. info.group = group;
  15. info.failure = 0;
  16. info.delivery_failure = 0;
  17. info.congested = 0;
  18. info.delivered = 0;
  19. info.allocation = allocation;
  20. info.skb = skb;
  21. info.skb2 = NULL;
  22. info.tx_filter = filter;
  23. info.tx_data = filter_data;
  24. /* While we sleep in clone, do not allow to change socket list */
  25. netlink_lock_table();
  26. // 向nl_table[ssk->sk_protocol].mc_list中的每个sock发送此netlink消息
  27. sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)
  28. do_one_broadcast(sk, &info);
  29. consume_skb(skb);
  30. netlink_unlock_table();
  31. if (info.delivery_failure) {
  32. kfree_skb(info.skb2);
  33. return -ENOBUFS;
  34. } else
  35. consume_skb(info.skb2);
  36. if (info.delivered) {
  37. if (info.congested && (allocation & __GFP_WAIT))
  38. yield();
  39. return 0;
  40. }
  41. return -ESRCH;
  42. }

static struct netlink_table *nl_table;是全局变量,它维护了用户态创建的所有netlink sock,按协议分类,每种协议一个链表mc_list。它在函数netlink_proto_init中被初始化,向nl_table[sk->sk_protocol].mc_list中增加sock的调用流程如下(kernel/net/netlink/af_netlink.c):

3.4 do_one_broadcast

  1. static inline int do_one_broadcast(struct sock *sk,
  2. struct netlink_broadcast_data *p)
  3. {
  4. struct netlink_sock *nlk = nlk_sk(sk);
  5. int val;
  6. if (p->exclude_sk == sk)
  7. goto out;
  8. if (nlk->pid == p->pid || p->group - 1 >= nlk->ngroups ||
  9. !test_bit(p->group - 1, nlk->groups))
  10. goto out;
  11. if (!net_eq(sock_net(sk), p->net))
  12. goto out;
  13. if (p->failure) {
  14. netlink_overrun(sk);
  15. goto out;
  16. }
  17. sock_hold(sk);
  18. if (p->skb2 == NULL) {
  19. if (skb_shared(p->skb)) {
  20. p->skb2 = skb_clone(p->skb, p->allocation);
  21. } else {
  22. p->skb2 = skb_get(p->skb);
  23. /*
  24. * skb ownership may have been set when
  25. * delivered to a previous socket.
  26. */
  27. skb_orphan(p->skb2);
  28. }
  29. }
  30. if (p->skb2 == NULL) {
  31. netlink_overrun(sk);
  32. /* Clone failed. Notify ALL listeners. */
  33. p->failure = 1;
  34. if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
  35. p->delivery_failure = 1;
  36. } else if (p->tx_filter && p->tx_filter(sk, p->skb2, p->tx_data)) {
  37. kfree_skb(p->skb2);
  38. p->skb2 = NULL;
  39. } else if (sk_filter(sk, p->skb2)) {
  40. kfree_skb(p->skb2);
  41. p->skb2 = NULL;
  42. } else if ((val = netlink_broadcast_deliver(sk, p->skb2)) < 0) {
  43. netlink_overrun(sk);
  44. if (nlk->flags & NETLINK_BROADCAST_SEND_ERROR)
  45. p->delivery_failure = 1;
  46. } else {
  47. p->congested |= val;
  48. p->delivered = 1;
  49. p->skb2 = NULL;
  50. }
  51. sock_put(sk);
  52. out:
  53. return 0;
  54. }

3.5 netlink_broadcast_deliver

  1. static inline int netlink_broadcast_deliver(struct sock *sk,
  2. struct sk_buff *skb)
  3. {
  4. struct netlink_sock *nlk = nlk_sk(sk);
  5. if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&
  6. !test_bit(0, &nlk->state)) {
  7. skb_set_owner_r(skb, sk);
  8. skb_queue_tail(&sk->sk_receive_queue, skb);
  9. sk->sk_data_ready(sk, skb->len);
  10. return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf;
  11. }
  12. return -1;
  13. }

Android热插拔事件处理详解的更多相关文章

  1. android java 设计模式详解 Demo

    android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...

  2. android:ToolBar详解

    android:ToolBar详解(手把手教程) 泡在网上的日子 发表于 2014-11-18 12:49 第 124857 次阅读 ToolBar 42 来源 http://blog.mosil.b ...

  3. Android之canvas详解

    首先说一下canvas类: Class Overview The Canvas class holds the "draw" calls. To draw something, y ...

  4. 【转】Android Canvas绘图详解(图文)

    转自:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2012/1212/703.html Android Canvas绘图详解(图文) 泡 ...

  5. Android 核心分析 之八Android 启动过程详解

    Android 启动过程详解 Android从Linux系统启动有4个步骤: (1) init进程启动 (2) Native服务启动 (3) System Server,Android服务启动 (4) ...

  6. Android GLSurfaceView用法详解(二)

    输入如何处理       若是开发一个交互型的应用(如游戏),通常需要子类化 GLSurfaceView,由此可以获取输入事件.下面有个例子: java代码: package eoe.ClearTes ...

  7. iOS开发——实用技术OC篇&事件处理详解

    事件处理详解 一:事件处理 事件处理常见属性: 事件类型 @property(nonatomic,readonly) UIEventType     type; @property(nonatomic ...

  8. Android编译过程详解(一)

    Android编译过程详解(一) 注:本文转载自Android编译过程详解(一):http://www.cnblogs.com/mr-raptor/archive/2012/06/07/2540359 ...

  9. android屏幕适配详解

    android屏幕适配详解 官方地址:http://developer.android.com/guide/practices/screens_support.html 一.关于布局适配建议 1.不要 ...

随机推荐

  1. Node.js 模块系统

    为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统. 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的.换言之,一个 Node.js 文件就是一个模块, ...

  2. [Gradle系列]Gradle发布module库到jCenter, 并构建自己的企业Maven私服

    Tamic 作者: http://blog.csdn.net/sk719887916/article/details/53224544 前言 andorid开发者经常会看到xx公司发布了xx项目,xx ...

  3. Vulkan API基本概念

    设备初始化 Instance --> GPU --> Device Instance表示具体的Vulkan应用.在一个应用程序中可以创建多个实例,这些实例之间相互独立,互不干扰. 当调用A ...

  4. Thread Pools

    许多程序会动态创建数十个设置上百个线程.举个例子,一个web服务器可能在每一个新到来的请求时创建一个新线程,然后在请求完成后将其终止. 然而,创建一个新线程将会带来一定的耗费:它需要在内核中创建自身必 ...

  5. Ajax PHP JavaScript MySQL实现简易的无刷新在线聊天室

    思路 消息显示区 发消息 板块 消息显示 消息发送 优化 显示非重复性的数据 优化显示 加上滚动条 每次都显示最新消息 完整代码 前端代码 数据库表结构 服务器端代码 总结与展望 总结 展望 为更好的 ...

  6. Linux 环境下的一些常用命令(三)

    转载自 http://www.oschina.net/translate/20-advanced-commands-for-middle-level-linux-users 21. 命令: Find ...

  7. mysql进阶(二十六)MySQL 索引类型(初学者必看)

    mysql进阶(二十六)MySQL 索引类型(初学者必看)   索引是快速搜索的关键.MySQL 索引的建立对于 MySQL 的高效运行是很重要的.下面介绍几种常见的 MySQL 索引类型.   在数 ...

  8. 通过一个例子了解MapReduce

    写MapReduce程序的步骤: 把问题转化为MapReduce模型: 设置运行参数: 写map类: 写reduce类: 例子:统计单词个数 Map的任务是将内容用" "分开,然后 ...

  9. Universal-Image-Loader 图片异步加载类库的使用

    这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影. 可是有的人并不知道如何去使用这库如何进行配置,网上查到的信息对于刚 ...

  10. RxJava在Android中使用场景详解

    RxJava 系列文章 <一,RxJava create操作符的用法和源码分析> <二,RxJava map操作符用法详解> <三,RxJava flatMap操作符用法 ...