开发者社区> code_xzh> 正文

Android热插拔事件处理详解

简介: 一、Android热插拔事件处理流程图 Android热插拔事件处理流程如下图所示:   二、组成 1. NetlinkManager:        全称是NetlinkManager.cpp位于Android 4.x 源码位置/system/vold/NetlinkManager.cpp。该类的主要通过引用NetlinkHandler类中的onEv
+关注继续查看

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

 

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



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

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

          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.       
  6.     // 从socket中读取kernel发送来的uevent消息  
  7.     count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_recv(socket, mBuffer, sizeof(mBuffer)));  
  8.     if (count < 0) {  
  9.         SLOGE("recvmsg failed (%s)", strerror(errno));  
  10.         return false;  
  11.     }  
  12.   
  13.     NetlinkEvent *evt = new NetlinkEvent();  
  14.     if (!evt->decode(mBuffer, count, mFormat)) {  
  15.         SLOGE("Error decoding NetlinkEvent");  
  16.     } else {  
  17.         onEvent(evt); //在NetlinkHandler中实现  
  18.     }  
  19.   
  20.     delete evt;  
  21.     return true;  
  22. }  

4. 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.         if(uEventOnOffFlag)  
  12.         {  
  13.             SLOGW("####netlink event  block ####");  
  14.             evt->dump();     
  15.         }  
  16.         vm->handleBlockEvent(evt);  
  17. #ifdef USE_USB_MODE_SWITCH  
  18.     } else if (!strcmp(subsys, "usb")  
  19.         || !strcmp(subsys, "scsi_device")) {  
  20.          SLOGW("subsystem found in netlink event");  
  21.         MiscManager *mm = MiscManager::Instance();  
  22.         mm->handleEvent(evt);  
  23. #endif  
  24.     }  
  25. }  

 

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.   
  18.     ssize_t n = recvmsg(socket, &hdr, 0);  
  19.     if (n <= 0) {  
  20.         return n;  
  21.     }  
  22.   
  23.     if (addr.nl_groups == 0 || addr.nl_pid != 0) {  
  24.         /* ignoring non-kernel or unicast netlink message */  
  25.         goto out;  
  26.     }  
  27.   
  28.     struct cmsghdr *cmsg = CMSG_FIRSTHDR(&hdr);  
  29.     if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {  
  30.         /* ignoring netlink message with no sender credentials */  
  31.         goto out;  
  32.     }  
  33.   
  34.     struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);  
  35.     if (cred->uid != 0) {  
  36.         /* ignoring netlink message from non-root user */  
  37.         goto out;  
  38.     }  
  39.   
  40.     return n;  
  41.   
  42. out:  
  43.     /* clear residual potentially malicious data */  
  44.     bzero(buffer, length);  
  45.     errno = EIO;  
  46.     return -1;  
  47. }  


 六、与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.   
  5.     ue_sk = kzalloc(sizeof(*ue_sk), GFP_KERNEL);  
  6.     if (!ue_sk)  
  7.         return -ENOMEM;  
  8.   
  9.     ue_sk->sk = netlink_kernel_create(net, NETLINK_KOBJECT_UEVENT,  
  10.                       1, NULL, NULL, THIS_MODULE);  
  11.     if (!ue_sk->sk) {  
  12.         printk(KERN_ERR  
  13.                "kobject_uevent: unable to create netlink socket!\n");  
  14.         kfree(ue_sk);  
  15.         return -ENODEV;  
  16.     }  
  17.     mutex_lock(&uevent_sock_mutex);  
  18.     list_add_tail(&ue_sk->list, &uevent_sock_list);  
  19.     mutex_unlock(&uevent_sock_mutex);  
  20.     return 0;  
  21. }  

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


 

  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.   
  11.     skb = netlink_trim(skb, allocation);  
  12.   
  13.     info.exclude_sk = ssk;  
  14.     info.net = net;  
  15.     info.pid = pid;  
  16.     info.group = group;  
  17.     info.failure = 0;  
  18.     info.delivery_failure = 0;  
  19.     info.congested = 0;  
  20.     info.delivered = 0;  
  21.     info.allocation = allocation;  
  22.     info.skb = skb;  
  23.     info.skb2 = NULL;  
  24.     info.tx_filter = filter;  
  25.     info.tx_data = filter_data;  
  26.   
  27.     /* While we sleep in clone, do not allow to change socket list */  
  28.   
  29.     netlink_lock_table();  
  30.          // 向nl_table[ssk->sk_protocol].mc_list中的每个sock发送此netlink消息  
  31.     sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)  
  32.         do_one_broadcast(sk, &info);   
  33.   
  34.     consume_skb(skb);  
  35.   
  36.     netlink_unlock_table();  
  37.   
  38.     if (info.delivery_failure) {  
  39.         kfree_skb(info.skb2);  
  40.         return -ENOBUFS;  
  41.     } else  
  42.         consume_skb(info.skb2);  
  43.   
  44.     if (info.delivered) {  
  45.         if (info.congested && (allocation & __GFP_WAIT))  
  46.             yield();  
  47.         return 0;  
  48.     }  
  49.     return -ESRCH;  
  50. }  

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


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.   
  6.     if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf &&  
  7.         !test_bit(0, &nlk->state)) {  
  8.         skb_set_owner_r(skb, sk);  
  9.         skb_queue_tail(&sk->sk_receive_queue, skb);  
  10.         sk->sk_data_ready(sk, skb->len);  
  11.         return atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf;  
  12.     }  
  13.     return -1;  
  14. }  

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【阿里巴巴搜索推荐事业部】招聘知识图谱、自然语言处理算法专家
我们旨在打造全球最大的中文电商知识图谱,支持包括淘宝、天猫乃至海外电商在内整个阿里集团的推荐、搜索、广告业务,每天服务上亿用户。从电商场景下的用户需求出发,打造一个连接商品,用户,知识,乃至各类开放领域知识、常识的大规模语义网络,并且在此基础上研究新一代基于知识图谱的智能搜索、推荐、问答技术。 我们已正式发布阿里电商知识图谱AliCoCo(Alibaba E-Commerce Cognitive Concept Net)于SIGMOD,也是阿里知识图谱首次在国际顶会上正式披露大规模领域知识图谱。
2023 0
【降价信息】云HBase X-Pack最高降价31%,再次释放大数据处理红利
阿里云中国站云HBase X-Pack中的Spark服务将于2019年7月16日进行价格下调。
739 0
Knative Eventing 中如何排查Event事件处理异常
在 Knative Eventing 如果事件处理过程出现异常,如何进行排查?本文通过一个实际中遇到的事件处理异常 issue 的角度, 介绍了事件排查处理方式。
1343 0
Ionic2 下处理 Android 设备下返回按钮的事件
原文发表于我的技术博客 本文分享了 Ionic2 下处理 Android 设备下返回按钮的事件,供参考。原文发表于我的技术博客 代码中我分享了如何捕捉 Ionic2 项目在 Android 设备下返回按钮事件,并在捕捉到的事件中可以灵活根据需求编写相关业务逻辑,如退出、返回等。
1035 0
Jquery 中为后生成或插入的 Html 元素先设定响应事件处理方法
Jquery 中为后生成或插入的 Html 元素先设定响应事件处理方法 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
759 0
手把手教你-----巧用Excel批量生成SQL语句,处理大量数据
<p><span style="font-size:18px">     <span style="font-family:KaiTi_GB2312">在做系统或者做项目的时候,经常会遇到这样的要求:用户给我们发过来一些数据,要求我们把这些数据导入到数</span></span></p> <p><span style="font-size:18px"><span style="font-
2609 0
手把手的numpy入门教程,从此数据处理不再慌
云栖号资讯:【点击查看更多行业资讯】在这里您可以找到不同行业的第一手的上云资讯,还在等什么,快来! 首先我们来看数组重塑,所谓的重塑本质上就是改变数组的shape。在保证数组当中所有元素不变的前提下,变更数组形状的操作。
752 0
+关注
code_xzh
对前端移动客户端技术比较擅长。著有《React Native移动开发实战》和《Kotlin入门与实战》和《Weex跨平台实战》,《React Native移动开发进阶》即将出版,正在努力完成《Flutter跨平台开发实战》
736
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载