您的位置:首页 > 移动开发 > Android开发

Android开机启动分析(一)logo的显示

2011-03-02 15:24 295 查看
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打交道了。分析完毕!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: