***************************************************************************************************************************
作者:EasyWave                                                                                 时间:2012.07.29

类别:Android系统源码分析                                                            声明:转载,请保留链接

注意:如有错误,欢迎指正。这些是我学习的日志文章......

***************************************************************************************************************************

在我的博文基于goldfish和android2.3.5学习之:开天辟地Android启动机制[一]中,介绍了整个android系统的启动机制,这次将更深入的详细分析android的uevent的机制以及如何android是如何透过内核传递过来的数据通过uevent建立设备节点以及一些hotplug事件。在Andorid2.3.5源码system/core/init.c函数中ueventd_main()函数。详细的代码如下:

    if (!strcmp(basename(argv[0]), "ueventd")) //得到运行程序ueventd.rc的全路径名下的ueventdreturn ueventd_main(argc, argv);    //如果可以找到ueventd.rc,则执行ueventd_main函数

这里有个函数不得不说,这个函数就是basename函数,这个函数的主要作用就是得到ueventd.rc文件所在路径下的文件名,即ueventd。这个函数的具体的代码如下:

#include <sys/cdefs.h>
#include <errno.h>
#include <libgen.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>char* basename(const char*  path)
{static char*  bname = NULL;int           ret;if (bname == NULL) {bname = (char *)malloc(MAXPATHLEN);if (bname == NULL)return(NULL);}ret = basename_r(path, bname, MAXPATHLEN);return (ret < 0) ? NULL : bname;
}

上面这个最终是调用basename_r函数来解析 ueventd.rc所在的路径下的ueventd的文件名。这个函数式在bionic/libc/bionic/basename.c中。具体的实现函数却是在basename_r函数中,这个函数也是在bionic/libc/bionic/basename_r.c中来实现的,具体的代码如下:

#include <libgen.h>
#include <errno.h>
#include <string.h>
#include <sys/param.h>int basename_r(const char* path, char*  buffer, size_t  bufflen)//返回路径中文件名的安全函数
{const char *endp, *startp;int         len, result;char        temp[2];/* Empty or NULL string gets treated as "." */if (path == NULL || *path == '\0') {startp  = ".";len     = 1;goto Exit;}/* Strip trailing slashes */endp = path + strlen(path) - 1;while (endp > path && *endp == '/')endp--;/* All slashes becomes "/" */if (endp == path && *endp == '/') {startp = "/";len    = 1;goto Exit;}/* Find the start of the base */startp = endp;while (startp > path && *(startp - 1) != '/')startp--;len = endp - startp +1;Exit:result = len;if (buffer == NULL) {return result;}if (len > (int)bufflen-1) {len    = (int)bufflen-1;result = -1;errno  = ERANGE;}if (len >= 0) {memcpy( buffer, startp, len );buffer[len] = 0;}return result;
}

透过上面的basename_r函数我们可以看出来主要是将目录路径"/"一个一个找出来并且丢弃,最终找到ueventd与strcmp(basename(argv[0]), "ueventd")进行比较,如果找到文件,则进入ueventd_main(argc, argv);进行uevent事件的主循环的处理中。在android2.3.5源码中system/core/rootdir/中ueventd.rc文件如下:

/dev/null                 0666   root       root
/dev/zero                 0666   root       root
/dev/full                 0666   root       root
/dev/ptmx                 0666   root       root
/dev/tty                  0666   root       root
/dev/random               0666   root       root
/dev/urandom              0666   root       root
/dev/ashmem               0666   root       root
/dev/binder               0666   root       root# logger should be world writable (for logging) but not readable
/dev/log/*                0662   root       log# the msm hw3d client device node is world writable/readable.
/dev/msm_hw3dc            0666   root       root# gpu driver for adreno200 is globally accessible
/dev/kgsl                 0666   root       root# these should not be world writable
/dev/diag                 0660   radio      radio
/dev/diag_arm9            0660   radio      radio
/dev/android_adb          0660   adb        adb
/dev/android_adb_enable   0660   adb        adb
/dev/ttyMSM0              0600   bluetooth  bluetooth
/dev/ttyHS0               0600   bluetooth  bluetooth
/dev/uinput               0660   system     bluetooth
/dev/alarm                0664   system     radio
/dev/tty0                 0660   root       system
/dev/graphics/*           0660   root       graphics
/dev/msm_hw3dm            0660   system     graphics
/dev/input/*              0660   root       input
/dev/eac                  0660   root       audio
/dev/cam                  0660   root       camera
/dev/pmem                 0660   system     graphics
/dev/pmem_adsp*           0660   system     audio
/dev/pmem_camera*         0660   system     camera
/dev/oncrpc/*             0660   root       system
/dev/adsp/*               0660   system     audio
/dev/snd/*                0660   system     audio
/dev/mt9t013              0660   system     system
/dev/msm_camera/*         0660   system     system
/dev/akm8976_daemon       0640   compass    system
/dev/akm8976_aot          0640   compass    system
/dev/akm8973_daemon       0640   compass    system
/dev/akm8973_aot          0640   compass    system
/dev/bma150               0640   compass    system
/dev/cm3602               0640   compass    system
/dev/akm8976_pffd         0640   compass    system
/dev/lightsensor          0640   system     system
/dev/msm_pcm_out*         0660   system     audio
/dev/msm_pcm_in*          0660   system     audio
/dev/msm_pcm_ctl*         0660   system     audio
/dev/msm_snd*             0660   system     audio
/dev/msm_mp3*             0660   system     audio
/dev/audience_a1026*      0660   system     audio
/dev/tpa2018d1*           0660   system     audio
/dev/msm_audpre           0660   system     audio
/dev/msm_audio_ctl        0660   system     audio
/dev/htc-acoustic         0660   system     audio
/dev/vdec                 0660   system     audio
/dev/q6venc               0660   system     audio
/dev/snd/dsp              0660   system     audio
/dev/snd/dsp1             0660   system     audio
/dev/snd/mixer            0660   system     audio
/dev/smd0                 0640   radio      radio
/dev/qemu_trace           0666   system     system
/dev/qmi                  0640   radio      radio
/dev/qmi0                 0640   radio      radio
/dev/qmi1                 0640   radio      radio
/dev/qmi2                 0640   radio      radio
/dev/bus/usb/*            0660   root       usb
/dev/usb_accessory        0660   root       usb# CDMA radio interface MUX
/dev/ts0710mux*           0640   radio      radio
/dev/ppp                  0660   radio      vpn
/dev/tun                  0640   vpn        vpn# sysfs properties
/sys/devices/virtual/input/input*   enable      0660  root   input
/sys/devices/virtual/input/input*   poll_delay  0660  root   input
/sys/devices/virtual/usb_composite/*   enable      0664  root   system

而ueventd_main的代码见下面,现在更深入的来分析下这个函数。这个函数式在system/core/init/ueventd.c

int ueventd_main(int argc, char **argv)
{struct pollfd ufd;int nr;char tmp[32];open_devnull_stdio(); //打开/dev/__null,并且重定向stdin、stdout、stderrlog_init(); //打开log文件INFO("starting ueventd\n");get_hardware_name(hardware, &revision); //透过/proc/cpuinfo得到当前项目的hardware以及revisionueventd_parse_config_file("/ueventd.rc"); //解析ueventd.rc文件snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);ueventd_parse_config_file(tmp); //解析ueventd.xxxxxx.rc文件,比如:goldfish为ueventd.goldfish.rc文件device_init();ufd.events = POLLIN;ufd.fd = get_device_fd();while(1) {ufd.revents = 0;nr = poll(&ufd, 1, -1);if (nr <= 0)continue;if (ufd.revents == POLLIN)handle_device_fd();}
}

首先来分析下open_devnull_stdio();这个函数,这个主要是打开/dev/__null__,同时将stdin,stdout,stderr进行重定向,具体的代码如下:

void open_devnull_stdio(void)
{int fd;static const char *name = "/dev/__null__";if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) { //设置节点属性,S_IFCHR:也就是说这是一个字符设备。0600是文件的属性。fd = open(name, O_RDWR); //打开/dev/__null__设备unlink(name); //删除一个文件的目录项并减少它的链接数if (fd >= 0) {  //打开成功dup2(fd, 0); //重定向stdindup2(fd, 1); //重定向stdoutdup2(fd, 2); //重定向stderrif (fd > 2) { //关闭文件并且返回成功close(fd);}return;}}//否则返回失败exit(1);
}

而log_init();主要是建立一个msg的log文件以记录上面的操作的记录文件。其函数代码如下[这个函数就不深究了,很容易看得懂。相信大家都应该还记得这个命令:cat /proc/kmsg,你自己可以在开发板上打下这个命令,看看会出现什么信息出来。。代码如下:

void log_init(void)
{static const char *name = "/dev/__kmsg__";if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {log_fd = open(name, O_WRONLY);fcntl(log_fd, F_SETFD, FD_CLOEXEC); //给文件强制加锁,设置文件为FD_CLOEXEC,这标志大家可以到网络搜索具体的意思,主要跟exec()函数有关的。unlink(name);}
}

同时这个函数需要说下,这个函数就是get_hardware_name(hardware, &revision);这个是透过/proc/cpuinfo得到当前硬件的名称和版本,相信做linux和android内核移植和驱动开发的,应该都知道这个命令:cat /proc/cpuinfo 吧,这个可以在自己的开发板上去测试下,看看会出现什么内容。现在重点分析ueventd_parse_config_file("/ueventd.rc");这个函数。这个函数式ueventd的重点之重,函数代码如下:

int ueventd_parse_config_file(const char *fn)
{char *data;data = read_file(fn, 0);if (!data) return -1;parse_config(fn, data);DUMP();return 0;
}

这里面的read_file(fn,0)主要是读取ueventd.rc的文件大小,并且存储在内存中。而parse_config(fn, data);这个才是重点,这是解析ueventd.rc文件的,这个函数是在system/core/init/ueventd_parser.c中,现在来深入的分析这个函数。

static void parse_config(const char *fn, char *s)
{struct parse_state state;char *args[UEVENTD_PARSER_MAXARGS];int nargs;nargs = 0;state.filename = fn;state.line = 1;state.ptr = s;state.nexttoken = 0;state.parse_line = parse_line_device; //具体的解析device,这是一个回调函数for (;;) {int token = next_token(&state); //循环查找ueventd.rc中的/dev/null, /dev/zero等switch (token) {case T_EOF:state.parse_line(&state, 0, 0);  //到了ueventd.rc的末尾的话,做最后的解析,并直接返回return;case T_NEWLINE:     //如果到了换了一行的时候,去做ueventd.rc的解析工作if (nargs) {state.parse_line(&state, nargs, args);nargs = 0;}break;case T_TEXT: //如果正在解析本行的数据,详细见ueventd.rc文件if (nargs < UEVENTD_PARSER_MAXARGS) {args[nargs++] = state.text;}break;}}
}

这个函数在/dev/的都是只有四个可选参数,而只有在/sys/下的时候多了一个attr的选项,也就是5个选项。具体的解析在 parse_line_device函数中的set_device_permission()有详细的处理。具体的代码如下:

void set_device_permission(int nargs, char **args)
{char *name;char *attr = 0;mode_t perm;uid_t uid;gid_t gid;int prefix = 0;char *endptr;int ret;char *tmp = 0;if (nargs == 0)return;if (args[0][0] == '#')return;name = args[0]; //就拿ueventd.rc文件中/dev/null     0666   root    root 和 /sys/devices/virtual/input/input*   enable    0660  root   input 来说吧if (!strncmp(name,"/sys/", 5) && (nargs == 5)) { //查找/sys/的设备,一般都是有5个参数的。INFO("/sys/ rule %s %s\n",args[0],args[1]);attr = args[1]; //因此将args[1]赋值给attrargs++;nargs--;}if (nargs != 4) { //如果参数不对,则直接退出,不解析了,因为出错了。ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);return;}/* If path starts with mtd@ lookup the mount number. */if (!strncmp(name, "mtd@", 4)) { //查找是否有mtd分区,如果有这mount mtd分区int n = mtd_name_to_number(name + 4);if (n >= 0)asprintf(&tmp, "/dev/mtd/mtd%d", n);name = tmp;} else { //否则就做常规的设备解析int len = strlen(name);if (name[len - 1] == '*') {prefix = 1;// 如果设备中'*'字符,这告诉add_dev_perms有前缀符号,比如:/dev/input/*  也就是这个有可能会有0,1,2,3等多个。name[len - 1] = '\0';}}perm = strtol(args[1], &endptr, 8); //将args[1]转换为8进制,比如字符0666转换为真正的整形。if (!endptr || *endptr != '\0') {ERROR("invalid mode '%s'\n", args[1]);free(tmp);return;}ret = get_android_id(args[2]); //得到android_id定义的name,具体见:android_ids表格。if (ret < 0) {ERROR("invalid uid '%s'\n", args[2]);free(tmp);return;}uid = ret; //设置用户idret = get_android_id(args[3]); //得到android_id定义的id,具体见:android_ids表格,下一节会深入分析if (ret < 0) {ERROR("invalid gid '%s'\n", args[3]);free(tmp);return;}gid = ret; //设置group的idadd_dev_perms(name, attr, perm, uid, gid, prefix); //添加到链表中。free(tmp);
}

在add_dev_perms()函数中,主要将/dev和/sys/ 分别添加到不同的链表中,具体的函数的实现如下:

int add_dev_perms(const char *name, const char *attr,mode_t perm, unsigned int uid, unsigned int gid,unsigned short prefix) {struct perm_node *node = calloc(1, sizeof(*node));if (!node)return -ENOMEM;node->dp.name = strdup(name);if (!node->dp.name)return -ENOMEM;if (attr) {node->dp.attr = strdup(attr);if (!node->dp.attr)return -ENOMEM;}node->dp.perm = perm;node->dp.uid = uid;node->dp.gid = gid;node->dp.prefix = prefix;if (attr)list_add_tail(&sys_perms, &node->plist);elselist_add_tail(&dev_perms, &node->plist);return 0;
}

还未完,具体的分析在 《基于android2.3.5学习之:开天辟地Android启动机制[三]》 来分析。。。。

基于android2.3.5系统:开天辟地Android启动机制[二]相关推荐

  1. 基于android2.3.5系统:开天辟地Android启动机制[一]

    **************************************************************************************************** ...

  2. 基于android2.3.5系统:源码下载及android体系架构

    **************************************************************************************************** ...

  3. 小米9开发版已开启Android,小米9迎来最后一个基于安卓9的系统,即将启动安卓q开发版内测...

    原标题:小米9迎来最后一个基于安卓9的系统,即将启动安卓q开发版内测 近来小米9的开发版系统发布了在过渡到安卓q前的最终版本(9.7.22),论坛公告也当放出了即将启动内测的消息,这样一来,小米对于系 ...

  4. linux系统如何选择内核启动,Linux系统的快速启动机制(内核切换)

    快速启动机制:允许通过已经运行的Linux内核的上下文启动另一个Linux内核,不需要经过BIOS.BIOS可能会消耗很多时间,特别是带有众多数量的外设的大型服务器.这种办法可以为经常启动机器的开发者 ...

  5. 基于Centos6.2 X64系统下的邮件系统(二)

    7.安装病毒扫描与垃圾邮件过滤 Amavisd-new 及 ClamAV 可从RPMForge软件仓库安装 首先,安装amavisd-new.clamav及spamassassin: [root@ma ...

  6. Android 进程间通信机制(二) mmap 原理

    一. 前言 Binder中一次拷贝的实现就是利用mmap(memory mapping)内存映射机制,我们来看看它的工作原理. 二. 参考文章 下面这几篇文章建议先好好阅读一下,都是总结的很好的文章, ...

  7. android130 android启动

    Android的编译环境只支持Linux和Mac OS,google推荐使用64位操作系统,### android启动> #### 当引导程序启动Linux内核后,会加载各种驱动和数据结构,当有 ...

  8. 基于STM32F103的智能门锁系统

    基于STM32F103的智能门锁系统 直接说明实现了什么效果 1 指纹解锁(基于AS608) 2 RFID解锁(基于RC522) 3 密码解锁 (基于LCD电容屏触摸控制) 4 蓝牙解锁 (基于HC- ...

  9. 基于SaaS的教务系统平台设计构想

    本篇是一篇自然科学论文,仅供参考. 大学挑战杯复赛没过,放博客纪念. 大佬们小点喷! 基于SaaS的教务系统平台设计构想 Ⅰ 中文摘要 Ⅱ 英文摘要 1. SaaS的介绍 1.1 SaaS的特性 1. ...

最新文章

  1. vim block vim_如何不再害怕Vim
  2. 40 万年薪招应届生?OPPO 狂揽芯片人才,应届生招聘行情究竟如何?
  3. 安全审计产品发展现状与趋势分析
  4. 【运筹学】表上作业法 ( 最优解判别 | 初始基可行解 | 运费修改可行性方案 | 闭回路法 )
  5. Oracle内部错误:ORA-00600:[4097]一例
  6. python生成热度图_Python数据可视化 热力图
  7. sublime text3 Package Control 插件安装及推荐(MAC)
  8. Flutter游戏:启动时的欢迎页
  9. Ubuntu之查看依赖软件
  10. 悟空CRM客户关系管理系统测试
  11. iPhone 如何将图片转换为文字
  12. Lebesgue可测但非Borel可测的函数
  13. 请告诉孩子:努力读书,是为将来拥有更多选择的权利,而不是被迫谋生
  14. 3d Max修改器中英文对照表
  15. 教程|电脑PC微信多开
  16. html 鼠标形状箭头,CSS各种鼠标样式介绍
  17. HDU 6691 Minimum Spanning Trees
  18. android图片上传后台后旋转的关键原因:ExifInterface
  19. Android打造带透明圆弧的ImageView
  20. VUE router 导航重复点击报错的问题解决两种方案

热门文章

  1. flex LCDS 整合 java 增删改查
  2. 每日安全简讯20160719
  3. 微信小程序实战教程1503---生成二维码
  4. 微信清理内置浏览器缓存
  5. Android刷机脚本——updater-script
  6. 一眼“看穿破绽”,3D视觉成锂电池“智造”降本增效利器
  7. 诗经 - 小雅 - 南有嘉鱼
  8. Seed, Expand and Constrain: Three Principles for Weakly-Supervised Image Segmentation(2016 ECCV)
  9. 华为智慧屏鸿蒙os系统体验,华为智慧屏S Pro体验:告诉你鸿蒙OS有多优秀?
  10. Java8 stream特性之一:List转Map方案(返回某个属性或对象本身)