**************************************** **************************************** **************************************** ***
Author: EasyWave time: 2012.07.29
Category: Android source code analysis statement: reprinted. Please keep the link
NOTE: If any error occurs, please correct it. These are my Learning Log articles ......
**************************************** **************************************** **************************************** ***
My blog post is based on goldfish and android2.3.5: the launch mechanism of the entire Android system is introduced in [1, this time, we will give a more in-depth analysis of the uevent mechanism of android and how android uses the data transmitted through the kernel to establish device nodes and some hotplug events through uevent. In the Andorid2.3.5 source code system/core/init. c function, the ueventd_main () function. The detailed code is as follows:
If (! Strcmp (basename (argv [0]), "ueventd") // obtain the running program ueventd. ueventd return ueventd_main (argc, argv) under the full path name of rc; // if you can find ueventd. run the ueventd_main function.
There is a function that has to be said here. This function is the basename function. The main function is to get the file name in the path where the ueventd. rc file is located, that is, ueventd. The code for this function is as follows:
#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;}
In the end, we call the basename_r function to parse the ueventd file name in the path where ueventd. rc is located. This function is in bionic/libc/bionic/basename. c. The specific implementation function is implemented in the basename_r function, which is also implemented in bionic/libc/bionic/basename_r.c. The specific code is as follows:
# Include <libgen. h> # include <errno. h> # include <string. h> # include <sys/param. h> int basename_r (const char * path, char * buffer, size_t bufflen) // security function of the file name in the returned path {const char * endp, * startp; int len, result; char temp [2];/* Empty or NULL string gets treated ". "*/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 ;}
Through the above basename_r function, we can see that we can find the directory path "/" one by one and discard it, and finally find the ueventd and strcmp (basename (argv [0]), "ueventd") for comparison. If the file is found, the ueventd_main (argc, argv); process the main loop of the uevent event. In android2.3.5 source code, the ueventd. rc file in system/core/rootdir/is as follows:
/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
The ueventd_main code can be found below. Now we can analyze this function more deeply. This function is used in system/core/init/ueventd. c.
Int ueventd_main (int argc, char ** argv) {struct pollfd ufd; int nr; char tmp [32]; open_devnull_stdio (); // open/dev/_ null, and redirects stdin, stdout, and stderr log_init (); // open the log file INFO ("starting ueventd \ n"); get_hardware_name (hardware, & revision ); // obtain the hardware and revision ueventd_parse_config_file ("/ueventd. rc "); // parse ueventd. rc file snprintf (tmp, sizeof (tmp), "/ueventd. % s. rc ", hardware); ueventd_parse_config_file (tmp); // parse ueventd. xxxxxx. rc file, for example, goldfish is ueventd. goldfish. rc file 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 ();}}
First, we will analyze open_devnull_stdio (); this function mainly enables/dev/_ null __and redirects stdin, stdout, and stderr. The specific code is as follows:
Void open_devnull_stdio (void) {int fd; static const char * name = "/dev/_ null _"; if (mknod (name, S_IFCHR | 0600, (1 <8) | 3) = 0) {// set the node attribute, S_IFCHR: that is, this is a character device. 0600 is the property of the file. Fd = open (name, O_RDWR); // open/dev/_ null _ DEVICE unlink (name ); // Delete the directory entry of a file and reduce the number of links to it. if (fd> = 0) {// open dup2 (fd, 0 ); // redirect stdin dup2 (fd, 1); // redirect stdout dup2 (fd, 2); // redirect stderr if (fd> 2) {// close the file and return close (fd);} return ;}// otherwise, return 'failed' exit (1 );}
Log_init (); creates a msg log file to record the above operation record files. The function code is as follows [this function is easy to understand. I believe everyone should remember this command: cat/proc/kmsg. You can run this command on the Development Board to see what information will appear .. The Code is as follows:
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); // force lock the file and set the file to FD_CLOEXEC, this indicates that you can search for specific meanings on the network, mainly related to the exec () function. Unlink (name );}}
At the same time, this function is get_hardware_name (hardware, & revision). The name and version of the current hardware are obtained through/proc/cpuinfo, I believe that for linux and android kernel transplantation and driver development, we should all know this command: cat/proc/cpuinfo. You can test it on your own Development Board to see what will happen. Now we will focus on the ueventd_parse_config_file ("/ueventd. rc"); function. The function code is as follows:
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;}
Here, read_file (fn, 0) is mainly used to read the file size of ueventd. rc and is stored in the memory. Parse_config (fn, data); this is the focus, which is to parse ueventd. rc file. This function is in system/core/init/ueventd_parser.c. Now we will analyze this function in depth.
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; // The specific parsing device. This is a callback function for (;) {int token = next_token (& state); // query ueventd cyclically. switch (token) {case T_EOF: state. parse_line (& state, 0, 0); // to ueventd. at the end of rc, perform the final parsing and directly return; case T_NEWLINE: // If a line is changed, perform ueventd. rc parsing work if (nargs) {state. parse_line (& state, nargs, args); nargs = 0;} break; case T_TEXT: // If the row's data is being parsed, see ueventd for details. rc file if (nargs <UEVENTD_PARSER_MAXARGS) {args [nargs ++] = state. text ;}break ;}}}
This function has only four optional parameters in/dev/, and only one attr option is added in/sys/, that is, five options. The specific Parsing is detailed in set_device_permission () in the parse_line_device function. The specific code is as follows:
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]; // you can use ueventd. in the rc file,/dev/null 0666 root and/sys/devices/virtual/input * enable 0660 root input. if (! Strncmp (name, "/sys/", 5) & (nargs = 5) {// find/sys/. Generally, there are five parameters. INFO ("/sys/rule % s \ n", args [0], args [1]); attr = args [1]; // Therefore, assign args [1] to attr args ++; nargs --;} if (nargs! = 4) {// If the parameter is incorrect, the system exits directly and does not parse it because of an error. 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) {// check whether mtd partition exists. If the mount mtd partition exists, int n = mtd_name_to_number (name + 4 ); if (n> = 0) asprintf (& tmp, "/dev/mtd % d", n); name = tmp ;} else {// otherwise, the general device parsing int len = strlen (name); if (name [len-1] = '*') {prefix = 1; // if the device contains the '*' character, this indicates that add_dev_perms has a prefix. For example,/dev/input/* indicates that there may be multiple characters, such as 0, 1, and 2. Name [len-1] = '\ 0';} perm = strtol (args [1], & endptr, 8 ); // convert args [1] to an octal format. For example, convert character 0666 to an integer. If (! Endptr | * endptr! = '\ 0') {ERROR ("invalid mode' % s' \ n", args [1]); free (tmp); return ;} ret = get_android_id (args [2]); // obtain the name defined by android_id. For details, see the android_ids table. If (ret <0) {ERROR ("invalid uid '% s' \ n", args [2]); free (tmp); return;} uid = ret; // Set User id ret = get_android_id (args [3]); // obtain the id defined by android_id. For details, see the android_ids table. The next section will analyze if (ret <0) {ERROR ("invalid gid '% s' \ n", args [3]); free (tmp); return;} gid = ret; // set the group id add_dev_perms (name, attr, perm, uid, gid, prefix); // Add it to the linked list. Free (tmp );}
In the add_dev_perms () function, add/dev and/sys/to different linked lists respectively. The specific function implementation is as follows:
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); else list_add_tail(&dev_perms, &node->plist); return 0;}
The analysis is completed inLearning from android2.3.5: Android startup mechanism [3]To analyze ....