android的開機動畫是如何啓動的

 在SurfaceFlinger::readyToRun()的最後啓動開機動畫

SurfaceFlinger::readyToRun()

{  ......

    // start boot animation
    property_set("ctl.start", "bootanim");

}

然後,init.rc裏面記錄的bootanim進程被創建,開機動畫由這個進程放出來。

那麼bootanim進程是如何被創建出來的呢?他是藉助Android的屬性系統(Android Property System)被創建的。

屬性系統是android的一個重要特性。它作爲一個服務運行,管理系統配置和狀態。所有這些配置和狀態都是屬性。每個屬性是一個鍵值對(key/value pair),其類型都是字符串。

屬性系統的上層架構如下圖所示:

圖中有3個進程、一組永久屬性文件和一塊共享內存區域。共享內存區域是所有屬性記錄的存儲所在。只有屬性服務進程纔可以寫入共享內存區域,它負責從永久文件中加載屬性記錄並將它們保存在共享內存中。

         consumer進程將共享內存加載到其自身的虛擬地址空間並直接訪問這些屬性。setter進程同樣將共享內存加載到其自身的虛擬地址空間,但其不能直接寫該內存。當setter試圖增加或者更新一個屬性時,它將該屬性通過unix domain socket發送至屬性服務。屬性服務代表setter進程將該屬性寫入共享內存和永久文件中。

屬性服務運行於init進程中。init進程首先創建一個共享內存區域,並保存一個指向該區域的描述符fdinit進程將該區域通過使用了MAP_SHARED標誌的mmap映射至它自身的虛擬地址空間,這樣,任何對於該區域的更新對於所有進程都是可見的。fd和區域大小被存儲在一個名爲ANDROID_PROPERTY_WORKSPACE的變量中。任何其他進程,比如consumersetter將使用這個變量來獲得fd和尺寸,這樣它們就能mmap這個區域到它們自身的虛擬地址空間中。

下一步是啓動屬性服務。在這一步中,一個unix domain socket服務被創建。此socket的路徑是/dev/socket/property_service,該路徑對於其他客戶端進程是熟知的。最後,init進程調用poll來等待該socket上的連接事件。

         在這裏

SurfaceFlinger::readyToRun()

{  ......

    // start boot animation
    property_set("ctl.start", "bootanim");

}

然後調到system/core/libcutils/properties.c裏面去

int property_set(const char *key, const char *value)
{

write(gPropFd, sendBuf, sizeof(sendBuf)) ; //寫到Unix domain socket裏面去

}

在system/core/init/init.c裏面,init進程在等待unix domain socket,接到這個消息之後,就開始創建bootanim進程了

system/core/init/init.c

int main()

{

struct pollfd ufds[4];

 

if (!property_set_fd_init && get_property_set_fd() > 0) { //爲property創建一個socket
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }

 

nr = poll(ufds, fd_count, timeout); //等待socket事件到來

for (i = 0; i < fd_count; i++) {
            if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())  //處理property設置事件
                    handle_property_set_fd();
                else if (ufds[i].fd == get_keychord_fd())
                    handle_keychord();
                else if (ufds[i].fd == get_signal_fd())
                    handle_signal();
            }
        }

}

handle_property_set_fd()的實現在system/core/init/property_service.c

void handle_property_set_fd()
{

 r = recv(s, &msg, sizeof(msg), 0);

 switch(msg.cmd) {
    case PROP_MSG_SETPROP:
        msg.name[PROP_NAME_MAX-1] = 0;
        msg.value[PROP_VALUE_MAX-1] = 0;

        if(memcmp(msg.name,"ctl.",4) == 0) {
            if (check_control_perms(msg.value, cr.uid, cr.gid)) {
                handle_control_message((char*) msg.name + 4, (char*) msg.value);
            } else {
                ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",
                        msg.name + 4, msg.value, cr.uid, cr.pid);
            }
        }

}

}

然後又掉init.c的handle_control_message()

void handle_control_message(const char *msg, const char *arg)
{
    if (!strcmp(msg,"start")) {
        msg_start(arg);
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);
    } else {
        ERROR("unknown control msg '%s'\n", msg);
    }
}

我們這裏是"ctl.start", 所以調msg_start(arg),它的實現也在init.c中

void msg_start(const char *name)

{

if (svc) {
        service_start(svc, args);
    }

}

 

void service_start(struct service *svc, const char *dynamic_args)
{

pid = fork();

execve(svc->args[0], (char**) svc->args, (char**) ENV) //至此,則bootanim進程被創建並開始執行

}

 

參考

http://blog.csdn.net/jackyu613/article/details/6136620

http://blog.csdn.net/kobeyxyx/article/details/6593976

發佈了27 篇原創文章 · 獲贊 7 · 訪問量 28萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章