Android 7.0 存储系统—Vold与MountService分析(三)(转 Android 9.0 分析)
Android的存储系统(三)
| 回顾:前帖分析了Vold的main()函数和NetlinkManager的函数调用流程,截止到NetlinkHandler的创建和start()调用,本帖继续分析源码 |
1、处理block类型的uevent
main()函数创建了CommandListener对象,NetlinkManager的start()函数又创建了NetlinkHandler对象,如果将CommandListener类和NetlinkHandler类的继承关系图画出来,会发现它们都是从SocketListener类派生出来的,如下图所示:

图1 NetlinkHandler和CommandListener的继承关系
原理:处于最底层的SocketListener类的作用是监听socket的数据,接收到数据后分别交给FrameworkListener类和NetlinkListener类的函数,并分别对来自Framework和驱动的数据进行分析,分析后根据命令再分别调用CommandListener和NetlinkHandler中的函数。
观察NetlinkHandler类的构造方法,代码如下:
NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
}
这个构造方法很简单,再看看它的start()方法,代码如下:
int NetlinkHandler::start() {
return this->startListener();
}
可以发现,start()方法调用了SocketListener的startListener()函数,代码如下:
int SocketListener::startListener(int backlog) {
) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
;
} else if (mSocketName) { // 只有CommandListener中会设置mSocketName
) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",mSocketName, strerror(errno));
;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
}
) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
if (pipe(mCtrlPipe)) { // 创建管道,用于退出监听线程
SLOGE("pipe failed (%s)", strerror(errno));
;
}
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) { // 创建一个监听线程
SLOGE("pthread_create (%s)", strerror(errno));
;
}
;
}
startListener()函数开始监听socket,这个函数在NetlinkHandler中会被调用,在CommandListener也会被调用。
startListener()函数首先判断变量mSocketName是否有值,只有CommandListener对象会对这个变量赋值,它的值就是在init.rc中定义的socket字符串。
调用函数 android_get_control_socket()的目的是从环境变量中取得socket的值,这样CommandListener对象得到了它需要监听的socket,
而对于NetlinkHandler对象而言,它的mSocket不为NULL,前面已经创建了socket。
startListener()函数接下来会根据成员变量mListener的值来判断是否需要调用Listen()函数来监听socket。这个mListen的值在对象构造时根据参数来初始化。
对于CommandListener对象,mListener的值为ture,对于NetlinkHandler对象,mListener的值为false,这是因为CommandListener对象和SystemServer通信,需要监听socket连接,而NetlinkHandler对象则不用。
接下来startListener()函数会创建一个管道,这个管道的作用是通知线程停止监听,这个线程就是startListener()函数最后创建的监听线程,它的运行函数是threadStart(),在前贴的NetlinkManager家族图系中我们可以清晰的发现,其代码如下:
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
me->runListener(); // 调用runListener()方法
pthread_exit(NULL);
return NULL;
}
threadStart()中又调用了runListener()函数,代码如下:
void SocketListener::runListener() {
SocketClientCollection pendingList;
) { // 无限循环,一直监听
SocketClientCollection::iterator it;
fd_set read_fds;
;
;
FD_ZERO(&read_fds); // 清空文件描述符集read_fds
if (mListen) { // 如果需要监听
max = mSock;
FD_SET(mSock, &read_fds); // 把mSock加入到read_fds
}
FD_SET(mCtrlPipe[], &read_fds); // 把管道mCtrlPipe[0]也加入到read_fds
] > max)
max = mCtrlPipe[];
pthread_mutex_lock(&mClientsLock); // 对容器mClients的操作需要加锁
for (it = mClients->begin(); it != mClients->end(); ++it) { // mClient中保存的是NetlinkHandler对象的socket,或者CommandListener接入的socket
int fd = (*it)->getSocket();
FD_SET(fd, &read_fds); // 遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
if (fd > max) { // 也加入到read_fds
max = fd;
}
}
pthread_mutex_unlock(&mClientsLock);
SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
, &read_fds, NULL, NULL, NULL)) < ) { // 执行select调用,开始等待socket上的数据到来
if (errno == EINTR) // 因为中断退出select,继续
continue;
SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
sleep(); // select出错,休眠1秒后继续
continue;
} else if (!rc)
continue; // 如果fd上没有数据到达,继续
], &read_fds)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[], &c, ));
if (c == CtrlPipe_Shutdown) {
break;
}
continue;
}
if (mListen && FD_ISSET(mSock, &read_fds)) { // 如果是CommandListener对象上有连接请求
struct sockaddr addr;
socklen_t alen;
int c;
do {
alen = sizeof(addr);
c = accept(mSock, &addr, &alen); // 接入连接请求
SLOGV("%s got %d from accept", mSocketName, c);
} && errno == EINTR); // 如果是中断导致失败,重新接入
) {
SLOGE("accept failed (%s)", strerror(errno));
sleep();
continue; // 接入发生错误,继续循环
}
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c, true, mUseCmdNum)); // 把接入的socket连接加入到mClients,这样再循环时就会监听到它的数据到达
pthread_mutex_unlock(&mClientsLock);
}
/* Add all active clients to the pending list first */
pendingList.clear();
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
SocketClient* c = *it;
int fd = c->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList.push_back(c); // 如果mClients中的某个socket上有数据了,把它加入到pendingList列表中
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
/* Process the pending list, since it is owned by the thread,* there is no need to lock it */
while (!pendingList.empty()) { // 处理pendingList列表
/* Pop the first item from the list */
it = pendingList.begin();
SocketClient* c = *it;
pendingList.erase(it); // 把处理了的socket从pendingList列表中删除
/* Process it, if false is returned, remove from list */
if (!onDataAvailable(c)) {
release(c, false); // 调用release()函数-->调用onDataAvailable()方法
}
c->decRef();
}
}
}
SocketListener::runListener是线程真正执行的函数。
以上runListener()函数虽然比较长,但这是一段标准的处理混合socket连接的代码,对于我们编写socket的程序大有帮助,这里先做简单了解。
<--------接下来,我们继续分析......-------->
runListener()函数收到从驱动传递的数据或者MountService传递的数据后,调用onDataAvailable()函数来处理,FrameworkListener类和NetlinkListener类都会重载这个函数。
首先来分析一下NetlinkListener类的onDataAvailable()函数是如何实现的!
直接上代码:
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
uid_t uid = -;
/*从socket中读取kernel发送来的uevent消息*/
count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid));
) { // 如果count<0,进行错误处理
)
LOG_EVENT_INT(, uid);
return false;
}
NetlinkEvent *evt = new NetlinkEvent(); // 创建NetlinkEvent对象
if (evt->decode(mBuffer, count, mFormat)) { // 调用decode()函数
} else if (mFormat != NETLINK_FORMAT_BINARY) {
SLOGE("Error decoding NetlinkEvent");
}
delete evt;
return true;
}
NetlinkListener类的onDataAvailable()函数首先调用uevent_kernel_multicast_uid_recv()函数来接收uevent消息。
接收到消息后,会创建NetlinkEvent对象,然后调用它的decode()函数对消息进行解码,然后用得到的消息数据给NetlinkEvent对象的成员变量赋值。
最后onDataAvailable()函数调用了onEvent()函数继续处理消息,onEvent()函数的代码如下:
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
VolumeManager *vm = VolumeManager::Instance();
const char *subsys = evt->getSubsystem();
if (!subsys) {
SLOGW("No subsystem found in netlink event");
return;
}
if (!strcmp(subsys, "block")) {
vm->handleBlockEvent(evt); // 调用VolumeManager的handleBlockEvent()函数来处理
}
}
NetlinkHandler的onEvent()函数中会判断event属于哪个子系统的,如果属于“block”(SD热插拔),则调用VolumeManager的handleBlockEvent()函数来处理,代码如下:
void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {
const char *devpath = evt->findParam("DEVPATH");
VolumeCollection::iterator it;
bool hit = false;
for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
if (!(*it)->handleBlockEvent(evt)) { // 对每个DirectVolume对象,调用它handleBlockEvent来处理这个event
hit = true; // 如果某个Volume对象处理了Event,则返回
break;
}
}
.....
}
|
总结:本帖的源码分析先到这里为止,下一贴再分析DirectVolume对象的handleBlockEvent()函数以及CommandListener对象如何处理从MountService发送的命令数据,即我们之前还没有讨论的关于FrameworkListener的onDataAvailable()函数的代码! PS:希望对Android手机开发、IOS、以及游戏(纯兴趣,白菜)和Java EE感兴趣的码友们互粉,这样我也能及时的看到你们的大神之作和经验之贴,感谢感谢! |
Android 7.0 存储系统—Vold与MountService分析(三)(转 Android 9.0 分析)的更多相关文章
- Android 7.0 存储系统—Vold与MountService分析(二)(转 Android 9.0 分析)
Android的存储系统(二) 回顾:前贴主要分析了Android存储系统的架构和原理图,简要的介绍了整个从Kernel-->Vold-->上层MountService之间的数据传输流程, ...
- Android 7.0 存储系统—Vold与MountService分析(一)(转 Android 9.0 分析)
Android的存储系统(一) 看了很长时间Vold存储模块的相关知识,也死扣了一段时间的Android源码,发现Android存储系统所涉及的函数调用,以及Kernel与上层之间的Socket传输真 ...
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析 【转】
本文转载自:http://blog.chinaunix.net/uid-21558711-id-3959287.html 分类: LINUX 原文地址:Linux I2C驱动分析(三)----i2c_ ...
- 【Android开发日记】之入门篇(三)——Android目录结构
本来的话,这一章想要介绍的是Android的系统架构,毕竟有了这些知识的储备,再去看实际的项目时才会更清楚地理解为什么要这样设计,同时在开发中遇到难题,也可以凭借着对Android的了解,尽快找出哪些 ...
- Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析
代码地址:https://github.com/showkawa/spring-annotation/tree/master/src/main/java/com/brian AnnotationCon ...
- Activity的绘制流程简单分析(基于android 4.0源码进行分析)
要明白这个流程,我们还得从第一部开始,大家都知道 在activity里面 setcontentview 调用结束以后 就可以看到程序加载好我们的布局文件了,从而让我们在手机上看到这个画面. 那么我们来 ...
- Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案
我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如L ...
- Android 核心分析之十三Android GWES之Android窗口管理
Android GWES之Android窗口管理1基本构架原理 Android的窗口管理是C/S模式的.Android中的Window是表示Top Level等顶级窗口的概念.DecorView是Wi ...
- Android内存机制分析1——了解Android堆和栈
//----------------------------------------------------------------------------------- Android内存机制分析1 ...
随机推荐
- MySql foreach属性
foreach属性 属性 描述 item 循环体中的具体对象.支持属性的点路径访问,如item.age,item.info.details.具体说明:在list和数组中是其中的对象,在map中是val ...
- 大型三甲医院管理系统源码PACS超声科室源码DICOM影像工作站
详情点击查看 开发环境 :VS2008 + C# + SQL2000 功能简介 1.患者登记工作站 集中登记患者基本信息和检查信息,包括就诊方式.患者来源.检查类型.检查部位.申请科室.申请医生等.可 ...
- AUTOSAR的前期开源实现Arctic Core
AUTOSAR (AUTomotive Open System ARchitecture) is a worldwide development partnership of vehicle manu ...
- 【Java】运用泽勒一致性计算某天是星期几
/** * Created by liangjiahao on 2017/2/26. * 运用泽勒一致性计算某天是星期几? * 公式: * h = (q + 26(m+1)/10 + k +k/4 + ...
- 编程之美2.18 数组分割 原创解O(nlogn)的时间复杂度求解:
题目:有一个无序.元素个数为2n的正整数组,要求:如何能把这个数组分割为元素个数为n的两个数组,并使两个子数组的和最接近? 1 1 2 -> 1 1 vs 2 看题时,解法的时间复杂度一般都大 ...
- zabbix 3.4 ubuntu 16 用腾讯企业邮箱作为告警邮箱
最近一直在研究zabbix监控系统,今天调试了腾讯企业邮箱作为告警邮箱的设置,本次方式是用内置email组件. 第一步: 选择Administration-->Media Types--> ...
- 洛谷 P1879 解题报告
P1879 [USACO06NOV]玉米田Corn Fields 题目描述 农场主\(John\)新买了一块长方形的新牧场,这块牧场被划分成\(M\)行\(N\)列\((1 ≤ M ≤ 12; 1 ≤ ...
- DDD实战进阶第一波(十):开发一般业务的大健康行业直销系统(实现经销商登录仓储与逻辑)
上一篇文章主要讲了经销商注册的仓储和领域逻辑的实现,我们先把应用服务协调完成经销商注册这部分暂停一下,后面文章统一讲. 这篇文章主要讲讲经销商登录的仓储和相关逻辑的实现. 在现代应用程序前后端分离的实 ...
- Java并发之ReentrantLock
一.ReentrantLock简介 ReentrantLock字面意义上理解为可重入锁.那么怎么理解可重入这个概念呢?或者说和我们经常用的synchronized又什么区别呢? ReentrantLo ...
- SpringMVC中Json数据格式转换
1 @RequestBody 作用: @RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为 ...