基於android2.3.5系統:開天闢地Android啟動機制[二]

來源:互聯網
上載者:User

***************************************************************************************************************************
作者: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的全路徑名下的ueventd        return 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、stderr    log_init(); //開啟log檔案    INFO("starting ueventd\n");    get_hardware_name(hardware, &revision); //透過/proc/cpuinfo得到當前項目的hardware以及revision    ueventd_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); //重新導向stdin            dup2(fd, 1); //重新導向stdout            dup2(fd, 2); //重新導向stderr            if (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]賦值給attr        args++;        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; //設定使用者id    ret = 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的id    add_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);    else        list_add_tail(&dev_perms, &node->plist);    return 0;}

還未完,具體的分析在《基於android2.3.5學習之:開天闢地Android啟動機制[三]》 來分析。。。。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.