<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                我們知道,Windows平臺上有一個叫注冊表的東西。注冊表可以存儲一些類似key/value的鍵值對。一般而言,系統或某些應用程序會把自己的一些屬性存儲在注冊表中,即使下次系統重啟或應用程序重啟,它還能夠根據之前在注冊表中設置的屬性,進行相應的初始化工作。Android平臺也提供了一個類型機制,可稱之為屬性服務(property service)。應用程序可通過這個屬性機制,查詢或設置屬性。讀者可以用adb shell登錄到真機或模擬器上,然后用getprop命令查看當前系統中有哪些屬性。即如我的HTC G7測試結果,如圖3-2所示:(圖中只顯示了部分屬性) :-: ![](http://img.blog.csdn.net/20150802095033071?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 圖3-2 HTC G7屬性示意圖 這個屬性服務是怎么實現的呢?下面來看代碼,其中與init.c和屬性服務有關的代碼有下面兩行: ~~~ property_init(); property_set_fd = start_property_service(); ~~~ 分別來看看它們。 1. 屬性服務初始化 (1)創建存儲空間 先看property_init函數,代碼如下所示: **property_service.c** ~~~ void property_init(void) { init_property_area();//初始化屬性存儲區域 //加載default.prop文件 load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); } ~~~ 在properyty_init函數中,先調用init_property_area函數,創建一塊用于存儲屬性的存儲區域,然后加載default.prop文件中的內容。再看init_property_area是如何工作的,它的代碼如下所示: **property_service.c** ~~~ static int init_property_area(void) { prop_area *pa; if(pa_info_array) return -1; /* 初始化存儲空間,PA_SIZE是這塊存儲空間的總大小,為32768字節,pa_workspace 為workspace類型的結構體,下面是它的定義: typedef struct { void *data; //存儲空間的起始地址 size_tsize; //存儲空間的大小 int fd; //共享內存的文件描述符 } workspace; init_workspace函數調用Android系統提供的ashmem_create_region函數創建一塊 共享內存。關于共享內存的知識我們在第7章會接觸,這里,只需把它當做一塊普通的內存就 可以了。 */ if(init_workspace(&pa_workspace, PA_SIZE)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); //在32768個字節的存儲空間中,有PA_INFO_START(1024)個字節用來存儲頭部信息 pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START); pa =pa_workspace.data; memset(pa, 0, PA_SIZE); pa->magic = PROP_AREA_MAGIC; pa->version = PROP_AREA_VERSION; //__system_property_area__這個變量由bionic libc庫輸出,有什么用呢? __system_property_area__ = pa; return0; } ~~~ 上面的內容比較簡單,不過最后的賦值語句可是大有來頭。__system_property_area__是bionic libc庫中輸出的一個變量,為什么這里要給它賦值呢? 原來,雖然屬性區域是由init進程創建,但Android系統希望其他進程也能讀取這塊內存里的東西。為做到這一點,它便做了以下兩項工作: - 把屬性區域創建在共享內存上,而共享內存是可以跨進程的。這一點,已經在上面的代碼中見到了,init_workspace函數內部將創建這個共享內存。 - 如何讓其他進程知道這個共享內存呢?Android利用了gcc的constructor屬性,這個屬性指明了一個__libc_prenit函數,當bionic libc庫被加載時,將自動調用這個__libc_prenit,這個函數內部就將完成共享內存到本地進程的映射工作。 (2)客戶端進程獲取存儲空間 關于上面的內容,來看相關代碼: **libc_init_dynamic.c** ~~~ //constructor屬性指示加載器加載該庫后,首先調用__libc_prenit函數。這一點和Windows上 //動態庫的DllMain函數類似 void __attribute__((constructor))__libc_prenit(void); void __libc_prenit(void) { ...... __libc_init_common(elfdata); //調用這個函數 ...... } ~~~ __libc_init_common函數為: **libc_init_common.c** ~~~ void __libc_init_common(uintptr_t *elfdata) { ...... __system_properties_init();//初始化客戶端的屬性存儲區域 } ~~~ **system_properties.c** ~~~ int __system_properties_init(void) { prop_area *pa; int s,fd; unsigned sz; char*env; ..... //還記得在啟動zygote一節中提到的添加環境變量的地方嗎?屬性存儲區域的相關信息 //就是在那兒添加的,這里需要取出來使用了。 env =getenv("ANDROID_PROPERTY_WORKSPACE"); //取出屬性存儲區域的文件描述符。關于共享內存的知識,第7章中將會進行介紹。 fd =atoi(env); env =strchr(env, ','); if(!env) { return -1; } sz =atoi(env + 1); //映射init創建的那塊內存到本地進程空間,這樣本地進程就可以使用這塊共享內存了。 //注意,映射的時候指定了PROT_READ屬性,所以客戶端進程只能讀屬性,而不能設置屬性。 pa =mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0); if(pa== MAP_FAILED) { return -1; } if((pa->magic != PROP_AREA_MAGIC) || (pa->version !=PROP_AREA_VERSION)) { munmap(pa, sz); return -1; } __system_property_area__ = pa; return0; } ~~~ 上面代碼中很多地方和共享內存有關,在第7章中會對與共享內存有關問題進行介紹,讀者也可先行學習有關共享內存的知識。 總之,通過這種方式,客戶端進程可以直接讀取屬性空間,但沒有權限設置屬性。客戶端進程又是如何設置屬性呢? 2. 啟動屬性服務器 (1)啟動屬性服務器 init進程會啟動一個屬性服務器,而客戶端只能通過和屬性服務器交互才能設置屬性。先來看屬性服務器的內容,它由start_property_service函數啟動,代碼如下所示: **Property_servie.c** ~~~ int start_property_service(void) { intfd; /* 加載屬性文件,其實就是解析這些文件中的屬性,然后把它設置到屬性空間中去。Android系統 一共提供了四個存儲屬性的文件,它們分別是: #definePROP_PATH_RAMDISK_DEFAULT "/default.prop" #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" */ load_properties_from_file(PROP_PATH_SYSTEM_BUILD); load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT); load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE); //有一些屬性是需要保存到永久介質上的,這些屬性文件則由下面這個函數加載,這些文件 //存儲在/data/property目錄下,并且這些文件的文件名必須以persist.開頭。這個函數 //很簡單,讀者可自行研究。 load_persistent_properties(); //創建一個socket,用于IPC通信。 fd =create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); if(fd< 0) return -1; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); returnfd; } ~~~ 屬性服務創建了一個用來接收請求的socket,可這個請求在哪里被處理呢?事實上,在init中的for循環那里已經進行相關處理了。 (2)處理設置屬性請求 接收請求的地方是在init進程中,代碼如下所示: **init.c::main函數片斷** ~~~ if (ufds[1].revents == POLLIN) handle_property_set_fd(property_set_fd); ~~~ 當屬性服務器收到客戶端請求時,init會調用handle_property_set_fd進行處理。這個函數的代碼如下所示: **property_service.c** ~~~ void handle_property_set_fd(int fd) { prop_msg msg; int s; int r; intres; structucred cr; structsockaddr_un addr; socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); //先接收TCP連接 if ((s= accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } //取出客戶端進程的權限等屬性。 if(getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { ...... return; } //接收請求數據 r = recv(s,&msg, sizeof(msg), 0); close(s); ...... switch(msg.cmd) { casePROP_MSG_SETPROP: msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; /* 如果是ctl開頭的消息,則認為是控制消息,控制消息用來執行一些命令,例如用 adb shell登錄后,輸入setprop ctl.start bootanim就可以查看開機動畫了, 關閉的話就輸入setpropctl.stop bootanim,是不是很有意思呢? */ 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 { //檢查客戶端進程是否有足夠的權限 if (check_perms(msg.name, cr.uid, cr.gid)) { //然后調用property_set設置。 property_set((char*) msg.name, (char*) msg.value); } ...... } break; default: break; } } ~~~ 當客戶端的權限滿足要求時,init就調用property_set進行相關處理,這個函數比較簡單,代碼如下所示: **property_service.c** ~~~ int property_set(const char *name, const char*value) { prop_area *pa; prop_info *pi; intnamelen = strlen(name); intvaluelen = strlen(value); ...... //從屬性存儲空間中尋找是否已經存在該屬性 pi =(prop_info*) __system_property_find(name); if(pi!= 0) { //如果屬性名以ro.開頭,則表示是只讀的,不能設置,所以直接返回。 if(!strncmp(name, "ro.", 3)) return -1; pa= __system_property_area__; //更新該屬性的值 update_prop_info(pi, value, valuelen); pa->serial++; __futex_wake(&pa->serial, INT32_MAX); }else { //如果沒有找到對應的屬性,則認為是增加屬性,所以需要新創建一項。注意,Android支持 //最多247項屬性,如果目前屬性的存儲空間中已經有247項,則直接返回。 pa= __system_property_area__; if(pa->count == PA_COUNT_MAX) return -1; pi= pa_info_array + pa->count; pi->serial = (valuelen << 24); memcpy(pi->name, name, namelen + 1); memcpy(pi->value, value, valuelen +1); pa->toc[pa->count] = (namelen << 24) | (((unsigned) pi) - ((unsigned) pa)); pa->count++; pa->serial++; __futex_wake(&pa->serial, INT32_MAX); } //有一些特殊的屬性需要特殊處理,這里,主要是以net.change開頭的屬性。 if(strncmp("net.", name, strlen("net.")) == 0) { if(strcmp("net.change", name) == 0) { return 0; } property_set("net.change", name); } elseif (persistent_properties_loaded && strncmp("persist.", name,strlen("persist.")) == 0) { //如果屬性名以persist.開頭,則需要把這些值寫到對應文件中去。 write_persistent_property(name, value); } /* 還記得init.rc中的下面這句話嗎? on property:persist.service.adb.enable=1 startadbd 當persist.service.adb.enable屬性置為1后,就會執行start adbd這個command, 這是通過property_changed函數來完成的,它非常簡單,讀者可以自己閱讀。 */ property_changed(name, value); return0; } ~~~ 好,屬性服務端的工作已經了解了,下面看客戶端是如何設置屬性的。 (3)客戶端發送請求 客戶端通過property_set發送請求,property_set由libcutils庫提供,代碼如下所示: **properties.c** ~~~ int property_set(const char *key, const char*value) { prop_msg msg; unsigned resp; ...... msg.cmd = PROP_MSG_SETPROP;//設置消息碼為PROP_MSG_SETPROP。 strcpy((char*) msg.name, key); strcpy((char*) msg.value, value); //發送請求 returnsend_prop_msg(&msg); } static int send_prop_msg(prop_msg *msg) { int s; int r; //建立和屬性服務器的socket連接 s =socket_local_client(PROP_SERVICE_NAME, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); if(s< 0) return -1; //通過socket發送出去 while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) { if((errno == EINTR) || (errno == EAGAIN)) continue; break; } if(r== sizeof(prop_msg)) { r= 0; } else{ r= -1; } close(s); returnr; } ~~~ 至此,屬性服務器就介紹完了。總體來說,還算比較簡單。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看