資料來源: Android開機啟動分析(一)logo的顯示
Android開機啟動的時候會有一個logo出現,它對應的源代碼位於/system/core/init/目錄下的logo.c中:
下面是我註釋過的代碼:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <linux/fb.h> #include <linux/kd.h> #include "init.h" #ifdef ANDROID #include <cutils/memory.h> #else void android_memset16(void *_ptr, unsigned short val, unsigned count) { unsigned short *ptr = _ptr; count >>= 1; while(count--) *ptr++ = val; } #endif struct FB { unsigned short *bits; unsigned size; int fd; struct fb_fix_screeninfo fi; struct fb_var_screeninfo vi; }; #define fb_width(fb) ((fb)->vi.xres) #define fb_height(fb) ((fb)->vi.yres) #define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) static int fb_open(struct FB *fb)//打開framebuffer設備 { //fd對應設備文件為 /dev/graphics/fb0 fb->fd = open("/dev/graphics/fb0", O_RDWR); if (fb->fd < 0) return -1; if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) goto fail; if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) goto fail; //對fd對應的Framebuffer大小進行mmap fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, MAP_SHARED, fb->fd, 0); if (fb->bits == MAP_FAILED) goto fail; return 0; fail: close(fb->fd); return -1; } static void fb_close(struct FB *fb)//關閉framebuffer { munmap(fb->bits, fb_size(fb)); close(fb->fd); } /* there's got to be a more portable way to do this ... */ static void fb_update(struct FB *fb) { fb->vi.yoffset = 1; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); fb->vi.yoffset = 0; ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); } //設置終端顯示模式:Graphics or Text? static int vt_set_mode(int graphics) { int fd, r; fd = open("/dev/tty0", O_RDWR | O_SYNC); if (fd < 0) return -1; //設置圖片還是文字 r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); close(fd); return r; } /* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ int load_565rle_image(char *fn) { struct FB fb; struct stat s; unsigned short *data, *bits, *ptr; unsigned count, max; int fd; if (vt_set_mode(1)) //打開Graphics顯示模式 return -1; //判斷是否能夠打開INIT_IMAGE_FILE fd = open(fn, O_RDONLY); if (fd < 0) { ERROR("cannot open '%s'/n", fn); //如果不行就恢復Text顯示模式 goto fail_restore_text; } if (fstat(fd, &s) < 0) { goto fail_close_file; } /*在用戶空間調用mmap,相當於在進程中開闢了文件的存儲IO映射 data為返回的映射地址,把圖片文件內容映射到存儲區 */ data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); if (data == MAP_FAILED) goto fail_close_file; if (fb_open(&fb)) goto fail_unmap_data; max = fb_width(&fb) * fb_height(&fb); ptr = data; count = s.st_size; bits = fb.bits; while (count > 3) { unsigned n = ptr[0]; if (n > max) break; //將ptr對應的圖片寫到bits對應的framebuffer中,細節不追究了 android_memset16(bits, ptr[1], n << 1); bits += n; max -= n; ptr += 2; count -= 4; } munmap(data, s.st_size); fb_update(&fb); fb_close(&fb); close(fd); unlink(fn); //刪除fn ? return 0; fail_unmap_data: //munmap釋放申請的內存映射IO munmap(data, s.st_size); fail_close_file: close(fd); fail_restore_text: vt_set_mode(0); //返回-1,於是就直接顯示"Android"字樣! return -1; }
分析如下:
Android開機的第一個進程為init,在它的實現文件,即init.c中,會通過調用函數load_565rle_image(INIT_IMAGE_FILE)來實現開機啟動顯示logo的。而上述這個函數是在同目錄下的logo.c中實現的。
在load_565rle_image()這個主函數中先調用vt_set_mode()來設定終端的顯示方式,默認是使用終端的graphics顯示方式,通過對開/dev/tty0設備文件調用ioctl系統調用來設定,但是如果不能打開load_565rle_image(INIT_IMAGE_FILE)中指定的INIT_IMAGE_FILE圖片文件時,就直接將/dev/tty0設置為Text顯示模式,然後直接返回,執行init.c中後面的代碼(直接向/dev/tty0設備寫入"Android"字樣的logo,這也就是默認的啟動logo,很沒有創意,哈哈!)
但是如果存在INIT_IMAGE_FILE這個圖片文件的話,就會在load_565rle_image中通過調用mmap,將圖片文件映射到當前進程的存儲空間中,接著就又調用fb_open()函數來打開linux framebuufer對應的設備文件 /dev/graphics/fb0 ,並把framebuffer也通過mmap映射到存儲空間,於是就在後面調用android_memset16()來寫入圖片,從android_memset16()這樣函數的參數就可以看出,程序的目的就是要把ptr對應的圖片存儲空間地址所對應的字節,寫到bits對應的framebuffer對應的存儲空間中。寫Framebuffer具體細節就不追究了,可能牽涉到Framebuffer原理的一些知識。
而在init.h文件中:
#define INIT_IMAGE_FILE "/initlogo.rle"
說明了圖片文件的位置和默認的文件名,這樣就可以自定義開機顯示畫面了,哈哈!!!
總結:顯示開機logo,如果使用圖片的話就是通過mmap,將圖片內容寫到Framebuffer存儲映射空間來完成的;而如果只是顯示Text的話,就是直接和/dev/tty0打交道了。分析完畢!!
No comments:
Post a Comment