Android 帧布局 菜鸟,ANDROID中的(udev)VOLD分析
现在可能很少有人会用mknod这个命令了,也很少有使用它的机会,但就在几年前,这还是一项linux工程师的必备技能,在制作文件系统前或加载新的驱动前,我们必须小心翼翼的创建设备节点。
不需要使用mknod并不是他消失了,而是我们有了更好更智能的方法。
linux对于热插拔的支持并不是生来就有的,而是经历了一个复杂而有戏剧性的过程,全球linux爱好者用脚投出了他们保贵的一票,udev最终成为事实上的标准。
在 android中,取代udev的是vold,我们这里不去过多的讨论为什么android不继续使用udev,但要知道vold的机制和udev是一样 的,理解了udev,也就理解了vold。android一出生就没有尊守传统linux的许多标准,当然也不能指望udev能很好的服务于 android。android社区的选择是别起炉灶,为android定做一套udev,这就是vold了。
无论是udev还是vold,都是基于sysfs的,sysfs为内核与用户层的通讯提供了一种全新的方式,并将这种方式加以规范。
kernel 层能检测到有新的设备接入,并能为之加载相应的驱动,但如何通知用户层呢?这就是sysfs的工作,内核中的sysfs机制要求当有新的驱动加载时给用户 层发送相应的event.但这些event只尽告知的义务,具体怎么处理,这就是vold(或者udev)的事了。
对于用户层而言,我们无需关心sysfs的细节,只要知道sysfs能向用户层提供什么就行了。
首先,我们要知道如何接收来自内核的event.
Netlink socket大家应该不会陌生吧,socket这套东西不仅能用于网络间的通讯,也用能用于进程间的通讯,像这种内核态与用户沟通的活,自然也少不了它。
下面的内容摘自vold(NetlinkManager.cpp)
if ((mSock = socket(PF_NETLINK,
SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
SLOGE("Unable to create uevent socket: %s", strerror(errno));
return -1;
}
if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
SLOGE("Unable to set uevent socket options: %s", strerror(errno));
return -1;
}
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
return -1;
}
这就是监听sysfs的uevent的socket的关键设置,有网络编程背影的人很容易理解上面这段代码。
下面紧接着的问题就是这个socket通路会给我们什么消息:
我们进入/sys/block/mmcblk0(也可以是/sys/block下的其它目录),执行:
cat *
MAJOR=179
MINOR=0
DEVNAME=mmcblk0
DEVTYPE=disk
PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234
PHYSDEVBUS=mmc
PHYSDEVDRIVER=mmcblk
NPARTS=1
......
我们通过socket从内核处到的envent中所包含的信息也与此相似,是一个包含这些信息的文本,可能是如下格式的
add@/block/PHYSDEVDRIVER=mmcblk
PHYSDEVPATH=/class/mmc_host/mmc0/mmc0:1234
DEVNAME=mmcblk0
MAJOR=179
MINOR=0
PHYSDEVDRIVER=mmcblk
......
sysfs传上来的是一个多行的文本(这点要特别注意,并不只是add@/block/PHYSDEVDRIVER=mmcblk
这一行),vold要对这个多行文档进行解析,然后决定怎么做。vold会把解协出来的消息再通可别一个vold的socket传到其它的进程,同时接收其它进程的反馈。
向sysfs目录(或子目录)下面的uevent文件写入”add/n”字符也会触发内核上发这些uevent,相当于重新执行了一次热插拔。
例:echo "add" > /sys/block/mmcblk0/uevent
系统启动时vold错过了的消息可以用这个特性重新触发。
分析vold的源码,要有一定的C++的基础和设计模式的知识,习惯过程式设计的程序员在读vold时会有很大的困难,不过幸好vold代码不多。另外,与vold相关的大量机密都在libsysutils中,千万不要漏掉这个库。
先看看下图SocketListener这套架构,监听sysfs与其它进程的消息,全仰仗这套框架。
在netLinkListener中,VOLD的重点是OnEvent这个虚接口的实现,而CommandSistener中,VOLD处理的重点则是分发VoldCommand类。VoldCommand是由FrameworkCommand派生出的,而VolumeCmd,ShareCmd等子类则是各种操作的封装。
先看看对 NetlinkHandler::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);
} else if (!strcmp(subsys, "switch")) {
vm->handleSwitchEvent(evt);
} else if (!strcmp(subsys, "battery")) {
} else if (!strcmp(subsys, "power_supply")) {
}
}
这里主要是通过基类解析的uevent消息分别调用不同的处理。如果看了上面的图还有人问NetlinkHandler::onEvent是在什么时候调用的,那就要补一下C++了,这不是一两句话能说得清楚的。
VoldCommand主要是实现对runcommand动作的封装,在 FrameworkListener会根据收到的消息选择相应的派生类。
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[255];
int len;
if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return errno;
} else if (!len)
return false;
int offset = 0;
int i;
for (i = 0; i < len; i++) {
if (buffer[i] == '/0') {
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
return true;
}
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
FrameworkCommandCollection::iterator i;
int argc = 0;
char *argv[FrameworkListener::CMD_ARGS_MAX];
char tmp[255];
char *p = data;
char *q = tmp;
bool esc = false;
bool quote = false;
int k;
memset(argv, 0, sizeof(argv));
memset(tmp, 0, sizeof(tmp));
while(*p) {
if (*p == '//') {
if (esc) {
*q++ = '//';
esc = false;
} else
esc = true;
p++;
continue;
} else if (esc) {
if (*p == '"')
*q++ = '"';
else if (*p == '//')
*q++ = '//';
else {
cli->sendMsg(500, "Unsupported escape sequence", false);
goto out;
}
p++;
esc = false;
continue;
}
if (*p == '"') {
if (quote)
quote = false;
else
quote = true;
p++;
continue;
}
*q = *p++;
if (!quote && *q == ' ') {
*q = '/0';
argv[argc++] = strdup(tmp);
memset(tmp, 0, sizeof(tmp));
q = tmp;
continue;
}
q++;
}
argv[argc++] = strdup(tmp);
#if 0
for (k = 0; k < argc; k++) {
SLOGD("arg[%d] = '%s'", k, argv[k]);
}
#endif
if (quote) {
cli->sendMsg(500, "Unclosed quotes error", false);
goto out;
}
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
if (!strcmp(argv[0], c->getCommand())) {
if (c->runCommand(cli, argc, argv)) {//调用派生类的接口。
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}
cli->sendMsg(500, "Command not recognized", false);
out:
int j;
for (j = 0; j < argc; j++)
free(argv[j]);
return;
}
Volume定义了各种磁盘的操作,属于工具类。
要将这些类是如何组织在一起的呢,关键是下面两个工厂类。
从上图可以看出,VolumeManager和NetlinkManager将整 个系统组织在一 起,NetlinkManager负责翻译sysfs的uevent事件并传递给其它的进程,VolumeManager则负责接收其它进程反馈的消息,并分发给VoldCommand类作相应的处理。
最后,我们分析一下vold是如何初始化这些类的:
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
SLOGI("Vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {//实例化
SLOGE("Unable to create VolumeManager");
exit(1);
};
if (!(nm = NetlinkManager::Instance())) {//实例化
SLOGE("Unable to create NetlinkManager");
exit(1);
};
cl = new CommandListener(); //创建vold socket,用于向其它进程转发解析的sysfs event,并接收其进程的命令。
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
if (process_config(vm)) { //解析配置
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
if (nm->start()) {//创建监听sysfs的socket
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
coldboot("/sys/block"); //冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add/n” 字符也可以触发sysfs事件,相当执行了一次热插拔。
/*
* Switch uevents are broken.
* For now we manually bootstrap
* the ums switch
*/
{
FILE *fp;
char state[255];
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) { //监听上层的反馈
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
etc/ vold.fstab的配置文件
例:(每一行的结束不能有空格等任何字符,vold对这个地方的处理有bug.)
dev_mount sdcard /data/disk auto /block/sda
格式是:
type label mount_point part sysfs_path sysfs_path
sysfs_path可以有多个,但最后不要有空格,否则会解析错误
part指定分区个数,如果是auto则只有一个分区
Android 帧布局 菜鸟,ANDROID中的(udev)VOLD分析相关推荐
- Android帧布局(Frame Layout)
Android帧布局(Frame Layout) FrameLayout是最简单的一个布局管理器.FrameLayout为每个加入其中的组件创建一个空白区域(一帧),这些组件根据layout_grav ...
- android中帧布局效果,Android开发实现布局帧布局霓虹灯效果示例
本文实例讲述了android开发实现布局帧布局霓虹灯效果.分享给大家供大家参考,具体如下: 效果图: 实现方式: framelayout中,设置8个textview,在主函数中,设计颜色数组,通过有序 ...
- android的帧布局,七、Android帧布局FrameLayout和霓虹灯效果
帧布局容器为每个加入其中的组件创建一个空白的区域(称为一帧),所有每个子组件占据一帧,这些帧都会根据gravity属性执行自动对齐. FrameLayout的常用XML属性和相关方法 XML属性 相关 ...
- android 获取布局textview,android – 获取TextView中文本的位置
看看几个Paint方法: getTextBounds()和 measureText.我们可以使用它们来确定TextView中文本的偏移量.确定TextView中的偏移后,我们可以将其添加到TextVi ...
- android 线性布局位置,android – 如何在线性布局中更改视图的位置.
所以我有一个已经填充了子项的线性布局.有没有办法改变其中一个孩子的位置? 如果有任何帮助的话,我正试图交换他们之间的观点. final LinearLayout parrent = (LinearLa ...
- android ui布局开发,Android UI设计初步(基本布局)
1.线性布局(LinearLayout) 线性布局是最简单,Android开发者使用得最多的布局类型之一,开发者用它来组织你们的用户界面上的控件.线性布局的作用就像它的名字一样:它将控件组织在一个垂直 ...
- android 减少布局层级,Android 布局优化
布局优化主要从以下几点进行着手 减少布局层次 和 复杂度 优化绘制流程 按需加载布局 减少布局层次 和 复杂度 首先我们可以通过以下工具分析界面布局的结构 查看布局树工具:Hierarchy View ...
- android表格布局bottom,Android基础_2 Activity线性布局和表格布局
在activity的布局中,线性布局和表格布局是最简单的,这次分别从线性布局,表格布局以及线性布局和表格混合布局做了实验,实验中只需要编写 相应的xml的代码,java代码不需要更改,因为我们这里只是 ...
- android局部布局替换,Android 局部布局替换的实现方式
最近再搞远程视频的功能,其中要实现加载视频.加载视频失败.加载成功的局部布局替换,查阅相关资料,找到一种投机取巧的方式. 首先分别写这三种效果的子布局,分别为 top_remotetreate.xml ...
最新文章
- 26期20180716 iptables规则备份恢复 firewalld zone
- socket recv 服务端阻塞 python_网络编程(基于socket编程)
- ASP.NET Core微服务(六)——【redis操作】
- 每天一道LeetCode-----在字符方格中查找某个单词
- Spring,Reactor和ElasticSearch:使用伪造的测试数据进行标记
- 如何在博客内添加音乐
- office安装出现windows installer服务不能更新一个或多个受保护的windows文件
- ActionBarSherlock学习笔记 第一篇——部署
- Centos 7.9系统安装网卡驱动
- 小鹏G3/G3i/P7/P5维修手册电路图用户手册维修诊断拆装技术资料
- SPSS卡方检验笔记
- matlab创建数组对象,如何在MATLAB中创建一个抽象类对象数组?
- 用java编写圆锥,java编写圆锥形体积
- 考研数学 第7讲 零点问题和微分不定式
- Java摇骰子比大小_android 摇骰子比大小app源码
- 解决QT-mingw32编译“ too many sections(...)”问题
- 程序媛眼中的 PingCAP:无法抗拒的五大吸引力 | PingCAP 招聘季
- Linux内存管理(二十六):slub 分配器初始化
- 简易图书管理系统(主要是jsp+servlet的练习),基于jsp+servlet的图书管理系统
- Linux九阴真经之无影剑残卷2(磁盘存储和文件系统)