Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15535807
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: LINUX

2009-02-08 20:45:46

浅析logcat驱动到应用的流程

===================================================
驱动driver层
drivers/android/logger.c
device_initcall(logger_init);
logger_init
==>ret = init_log(&log_main);
==>ret = init_log(&log_events);
==>ret = init_log(&log_radio);

#define LOGGER_LOG_MAIN        "log_main"    /* everything else */
DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
.minor = MISC_DYNAMIC_MINOR, \
.name = NAME,\
.fops = &logger_fops,

init_log
==>ret = misc_register(&log->misc);//发送创建/dev/misc/log_main节点的event

===================================================
应用init层
之后init进程
handle_device_fd
==>handle_device_event
==>
        if(!strncmp(uevent->path, "/class/misc/", 12) &&
                    !strncmp(name, "log_", 4)) {
            base = "/dev/log/";
            mkdir(base, 0755);//创建件/dev/log目录

            name += 4;//名字格式化成main,radio和events.

        }
        //然后创建/dev/log/main节点,/dev/log/radio节点和/dev/log/events节点.

===================================================
应用logcat层
#define LOGGER_LOG_MAIN "log/main"
main
==>char *log_device = strdup("/dev/"LOGGER_LOG_MAIN);
==>logfd = open(log_device, mode);
//执行驱动logger_fops.logger_open,获得相应DEFINE_LOGGER_DEVICE创建的buf2

//将打开驱动的应用程序作为一个reader挂接到DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)

//中定义的log->readers链表上[luther.gliethttp]

==>readLogLines(logfd);//从/proc/kmsg读取信息

#define KERNEL_LOG_SOURCE    "/proc/kmsg"
knlfd = open(KERNEL_LOG_SOURCE, O_RDONLY);
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
result = select(max_fd + 1, &rfds, NULL, NULL, NULL);//等待/proc/kmsg或者/dev/log/main数据的到来

//1./proc/kmsg数据来了,将kernel打印的数据读取出来,然后作保存处理,这样可以截获到应用程序空间和内核空间的全部log[luther.gliethttp].

/*
驱动driver层创建/proc/kmsg文件,这样应用程序可以获得由printk打印出来的log数据[luther.gliethttp].
start_kernel
==>proc_root_init
==>proc_misc_init
#ifdef CONFIG_PRINTK
    {
        struct proc_dir_entry *entry;
        entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//创建/proc/kmsg
        if (entry)
            entry->proc_fops = &proc_kmsg_operations;//kmsg操作方法
    }
#endif
const struct file_operations proc_kmsg_operations = {
    .read        = kmsg_read,
    .poll        = kmsg_poll,
    .open        = kmsg_open,
    .release    = kmsg_release,
};
static unsigned int kmsg_poll(struct file *file, poll_table *wait)
{
    poll_wait(file, &log_wait, wait);
    if (do_syslog(9, NULL, 0))
        return POLLIN | POLLRDNORM;
    return 0;
}
printk调用之后
==>release_console_sem
==>wake_up_klogd
==>wake_up_interruptible(&log_wait);
//下面函数就是实现实际数据读取了
static ssize_t kmsg_read(struct file *file, char __user *buf,
             size_t count, loff_t *ppos)
{
    if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
        return -EAGAIN;
    return do_syslog(2, buf, count);
}
        spin_lock_irq(&logbuf_lock);
        while (!error && (log_start != log_end) && i < len) {
            c = LOG_BUF(log_start);
            log_start++;
            spin_unlock_irq(&logbuf_lock);
            error = __put_user(c,buf);//推到用户空间
            buf++;
            i++;
            cond_resched();
            spin_lock_irq(&logbuf_lock);
        }
        spin_unlock_irq(&logbuf_lock);
*/

char knl_buffer[512];
ret = read(knlfd, knl_buffer, sizeof(knl_buffer));
if(g_outFD != STDOUT_FILENO) {
    //只有当g_outFD不是stdout标准输出时,才会对/proc/kmsg数据执行输出处理操作

    //stdout已经显示了由kernel执行printk输出的数据了[luther.gliethttp]

}
//2./dev/log/main数据来了

ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
entry->msg[entry->len] = '\0';
if (g_printBinary) {
    printBinary(entry);
} else {
    (void) processBuffer(entry);
}
processBuffer
==>android_log_processLogBuffer
==>android_log_filterAndPrintLogLine(
        g_logformat, g_outFD, &entry);//将数据打印到g_outFD

//#define STDOUT_FILENO    1

//默认情况下g_outFD = STDOUT_FILENO;



以LOGV函数为例
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#define LOG(priority, tag, ...) \
    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#define LOG_PRI(priority, tag, ...) \
    android_printLog(priority, tag, __VA_ARGS__)
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
    va_list ap;
    char buf[LOG_BUF_SIZE];

    va_start(ap, fmt);
    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
    va_end(ap);

    return __android_log_write(prio, tag, buf);
}

static int __android_log_write(int prio, const char *tag, const char *msg)
{
    struct iovec vec[3];
    log_id_t log_id = LOG_ID_MAIN;//给main


    if (!tag)
        tag = "";

    if (!strcmp(tag, "HTC_RIL"))
        log_id = LOG_ID_RADIO;

    vec[0].iov_base = (unsigned char *) &prio;
    vec[0].iov_len = 1;
    vec[1].iov_base = (void *) tag;
    vec[1].iov_len = strlen(tag) + 1;
    vec[2].iov_base = (void *) msg;
    vec[2].iov_len = strlen(msg) + 1;

    return write_to_log(log_id, vec);//调用__write_to_log_kernel函数送给main

}

static int (*write_to_log)(log_id_t, struct iovec *vec) = __write_to_log_init;
static int __write_to_log_init(log_id_t log_id, struct iovec *vec)
{
    if (write_to_log == __write_to_log_init) {
        //类似于stdin,stdout和stderr,生成LOG_ID_MAIN和LOG_ID_RADIO

        log_fds[LOG_ID_MAIN] = open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);//打开/dev/log/main

        log_fds[LOG_ID_RADIO] = open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);//打开/dev/log/radio


        write_to_log = __write_to_log_kernel;//赋予其新方法


        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0) {
            close(log_fds[LOG_ID_MAIN]);
            close(log_fds[LOG_ID_RADIO]);
            log_fds[LOG_ID_MAIN] = -1;
            log_fds[LOG_ID_RADIO] = -1;
            write_to_log = __write_to_log_null;
        }
    }
}

static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec)
{
    ssize_t ret;
    int log_fd;

    if ((int)log_id >= 0 && (int)log_id < (int)LOG_ID_MAX) {
        log_fd = log_fds[(int)log_id];//取出__write_to_log_init生成的fd句柄

    } else {
        return EBADF;
    }

    do {
        ret = writev(log_fd, vec, 3);//传给driver函数logger_aio_write

    } while (ret < 0 && errno == EINTR);

    return ret;
}

logger_aio_write
==>do_write_log(log, &header, sizeof(struct logger_entry));
==>do_write_log_from_user(log, iov->iov_base, len);
log->w_off = logger_offset(log->w_off + count);//更新w_off偏移索引

wake_up_interruptible(&log->wq);//唤醒等待在wq上的用户程序


logger_poll
==>poll_wait(file, &log->wq, wait);

logger_read
==>prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
==>do_read_log_to_user
reader->r_off = logger_offset(reader->r_off + count);//更新r_off偏移索引



因为write操作是随时的,而read操作未必发生,那么就有可能出现
do_write_log(log, &header, sizeof(struct logger_entry));
覆盖下一个的现象,所以在执行do_write_log()之前,首先执行fix_up_readers来修正reader->r_off数值
fix_up_readers(log, sizeof(struct logger_entry) + header.len);
static void fix_up_readers(struct logger_log *log, size_t len)
{
    size_t old = log->w_off;//此次写偏移索引

    size_t new = logger_offset(old + len);//写入len数据后,偏移索引值

    struct logger_reader *reader;

    if (clock_interval(old, new, log->head))//表示如果执行new拷贝之后,将覆盖log->head,所以需要

//查询下一个,这里使用最大胆的长度len来获取下一个做head.

        log->head = get_next_entry(log, log->head, len);//感觉len太长了,应该可以缩短,

//当然如果出现数据覆盖,那么丢1个也是丢,多丢几个也是丢,所以传递len,只是会多丢几个罢了.


    list_for_each_entry(reader, &log->readers, list)
        if (clock_interval(old, new, reader->r_off))
//遍历reader链表,如果reader在覆盖范围内,那么调整当前reader位置到下一个log数据区[luther.gliethttp]

//因为write操作不论有没有open和read操作,所以,reader->r_off当open时,reader->r_off = log->head;

//以保证随时open和read,不至于出现不同步问题,因为如果open时reader->r_off=0,是绝对存在问题,所以

//log->head的出现就是为了解决这个问题[luther.gliethttp].

            reader->r_off = get_next_entry(log, reader->r_off, len);
}浅析logcat驱动到应用的流程

===================================================
驱动driver层
drivers/android/logger.c
device_initcall(logger_init);
logger_init
==>ret = init_log(&log_main);
==>ret = init_log(&log_events);
==>ret = init_log(&log_radio);

#define LOGGER_LOG_MAIN        "log_main"    /* everything else */
DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)
#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \
.minor = MISC_DYNAMIC_MINOR, \
.name = NAME,\
.fops = &logger_fops,

init_log
==>ret = misc_register(&log->misc);//发送创建/dev/misc/log_main节点的event

===================================================
应用init层
之后init进程
handle_device_fd
==>handle_device_event
==>
        if(!strncmp(uevent->path, "/class/misc/", 12) &&
                    !strncmp(name, "log_", 4)) {
            base = "/dev/log/";
            mkdir(base, 0755);//创建件/dev/log目录

            name += 4;//名字格式化成main,radio和events.

        }
        //然后创建/dev/log/main节点,/dev/log/radio节点和/dev/log/events节点.

===================================================
应用logcat层
#define LOGGER_LOG_MAIN "log/main"
main
==>char *log_device = strdup("/dev/"LOGGER_LOG_MAIN);
==>logfd = open(log_device, mode);
//执行驱动logger_fops.logger_open,获得相应DEFINE_LOGGER_DEVICE创建的buf2

//将打开驱动的应用程序作为一个reader挂接到DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN, 64*1024)

//中定义的log->readers链表上[luther.gliethttp]

==>readLogLines(logfd);//从/proc/kmsg读取信息

#define KERNEL_LOG_SOURCE    "/proc/kmsg"
knlfd = open(KERNEL_LOG_SOURCE, O_RDONLY);
unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
result = select(max_fd + 1, &rfds, NULL, NULL, NULL);//等待/proc/kmsg或者/dev/log/main数据的到来

//1./proc/kmsg数据来了,将kernel打印的数据读取出来,然后作保存处理,这样可以截获到应用程序空间和内核空间的全部log[luther.gliethttp].

/*
驱动driver层创建/proc/kmsg文件,这样应用程序可以获得由printk打印出来的log数据[luther.gliethttp].
start_kernel
==>proc_root_init
==>proc_misc_init
#ifdef CONFIG_PRINTK
    {
        struct proc_dir_entry *entry;
        entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);//创建/proc/kmsg
        if (entry)
            entry->proc_fops = &proc_kmsg_operations;//kmsg操作方法
    }
#endif
const struct file_operations proc_kmsg_operations = {
    .read        = kmsg_read,
    .poll        = kmsg_poll,
    .open        = kmsg_open,
    .release    = kmsg_release,
};
static unsigned int kmsg_poll(struct file *file, poll_table *wait)
{
    poll_wait(file, &log_wait, wait);
    if (do_syslog(9, NULL, 0))
        return POLLIN | POLLRDNORM;
    return 0;
}
printk调用之后
==>release_console_sem
==>wake_up_klogd
==>wake_up_interruptible(&log_wait);
//下面函数就是实现实际数据读取了
static ssize_t kmsg_read(struct file *file, char __user *buf,
             size_t count, loff_t *ppos)
{
    if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
        return -EAGAIN;
    return do_syslog(2, buf, count);
}
        spin_lock_irq(&logbuf_lock);
        while (!error && (log_start != log_end) && i < len) {
            c = LOG_BUF(log_start);
            log_start++;
            spin_unlock_irq(&logbuf_lock);
            error = __put_user(c,buf);//推到用户空间
            buf++;
            i++;
            cond_resched();
            spin_lock_irq(&logbuf_lock);
        }
        spin_unlock_irq(&logbuf_lock);
*/

char knl_buffer[512];
ret = read(knlfd, knl_buffer, sizeof(knl_buffer));
if(g_outFD != STDOUT_FILENO) {
    //只有当g_outFD不是stdout标准输出时,才会对/proc/kmsg数据执行输出处理操作

    //stdout已经显示了由kernel执行printk输出的数据了[luther.gliethttp]

}
//2./dev/log/main数据来了

ret = read(logfd, entry, LOGGER_ENTRY_MAX_LEN);
entry->msg[entry->len] = '\0';
if (g_printBinary) {
    printBinary(entry);
} else {
    (void) processBuffer(entry);
}
processBuffer
==>android_log_processLogBuffer
==>android_log_filterAndPrintLogLine(
        g_logformat, g_outFD, &entry);//将数据打印到g_outFD

//#define STDOUT_FILENO    1

//默认情况下g_outFD = STDOUT_FILENO;



以LOGV函数为例
#define LOGV(...) ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
#define LOG(priority, tag, ...) \
    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)
#define LOG_PRI(priority, tag, ...) \
    android_printLog(priority, tag, __VA_ARGS__)
#define android_printLog(prio, tag, fmt...) \
    __android_log_print(prio, tag, fmt)
int __android_log_print(int prio, const char *tag, const char *fmt, ...)
{
    va_list ap;
    char buf[LOG_BUF_SIZE];

    va_start(ap, fmt);
    vsnprintf(buf, LOG_BUF_SIZE, fmt, ap);
    va_end(ap);

    return __android_log_write(prio, tag, buf);
}

static int __android_log_write(int prio, const char *tag, const char *msg)
{
    struct iovec vec[3];
    log_id_t log_id = LOG_ID_MAIN;//给main


    if (!tag)
        tag = "";

    if (!strcmp(tag, "HTC_RIL"))
        log_id = LOG_ID_RADIO;

    vec[0].iov_base = (unsigned char *) &prio;
    vec[0].iov_len = 1;
    vec[1].iov_base = (void *) tag;
    vec[1].iov_len = strlen(tag) + 1;
    vec[2].iov_base = (void *) msg;
    vec[2].iov_len = strlen(msg) + 1;

    return write_to_log(log_id, vec);//调用__write_to_log_kernel函数送给main

}

static int (*write_to_log)(log_id_t, struct iovec *vec) = __write_to_log_init;
static int __write_to_log_init(log_id_t log_id, struct iovec *vec)
{
    if (write_to_log == __write_to_log_init) {
        //类似于stdin,stdout和stderr,生成LOG_ID_MAIN和LOG_ID_RADIO

        log_fds[LOG_ID_MAIN] = open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);//打开/dev/log/main

        log_fds[LOG_ID_RADIO] = open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);//打开/dev/log/radio


        write_to_log = __write_to_log_kernel;//赋予其新方法


        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0) {
            close(log_fds[LOG_ID_MAIN]);
            close(log_fds[LOG_ID_RADIO]);
            log_fds[LOG_ID_MAIN] = -1;
            log_fds[LOG_ID_RADIO] = -1;
            write_to_log = __write_to_log_null;
        }
    }
}

static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec)
{
    ssize_t ret;
    int log_fd;

    if ((int)log_id >= 0 && (int)log_id < (int)LOG_ID_MAX) {
        log_fd = log_fds[(int)log_id];//取出__write_to_log_init生成的fd句柄

    } else {
        return EBADF;
    }

    do {
        ret = writev(log_fd, vec, 3);//传给driver函数logger_aio_write

    } while (ret < 0 && errno == EINTR);

    return ret;
}

logger_aio_write
==>do_write_log(log, &header, sizeof(struct logger_entry));
==>do_write_log_from_user(log, iov->iov_base, len);
log->w_off = logger_offset(log->w_off + count);//更新w_off偏移索引

wake_up_interruptible(&log->wq);//唤醒等待在wq上的用户程序


logger_poll
==>poll_wait(file, &log->wq, wait);

logger_read
==>prepare_to_wait(&log->wq, &wait, TASK_INTERRUPTIBLE);
==>do_read_log_to_user
reader->r_off = logger_offset(reader->r_off + count);//更新r_off偏移索引



因为write操作是随时的,而read操作未必发生,那么就有可能出现
do_write_log(log, &header, sizeof(struct logger_entry));
覆盖下一个的现象,所以在执行do_write_log()之前,首先执行fix_up_readers来修正reader->r_off数值
fix_up_readers(log, sizeof(struct logger_entry) + header.len);
static void fix_up_readers(struct logger_log *log, size_t len)
{
    size_t old = log->w_off;//此次写偏移索引

    size_t new = logger_offset(old + len);//写入len数据后,偏移索引值

    struct logger_reader *reader;

    if (clock_interval(old, new, log->head))//表示如果执行new拷贝之后,将覆盖log->head,所以需要

//查询下一个,这里使用最大胆的长度len来获取下一个做head.

        log->head = get_next_entry(log, log->head, len);//感觉len太长了,应该可以缩短,

//当然如果出现数据覆盖,那么丢1个也是丢,多丢几个也是丢,所以传递len,只是会多丢几个罢了.


    list_for_each_entry(reader, &log->readers, list)
        if (clock_interval(old, new, reader->r_off))
//遍历reader链表,如果reader在覆盖范围内,那么调整当前reader位置到下一个log数据区[luther.gliethttp]

//因为write操作不论有没有open和read操作,所以,reader->r_off当open时,reader->r_off = log->head;

//以保证随时open和read,不至于出现不同步问题,因为如果open时reader->r_off=0,是绝对存在问题,所以

//log->head的出现就是为了解决这个问题[luther.gliethttp].

            reader->r_off = get_next_entry(log, reader->r_off, len);
}

阅读(6299) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-05-18 11:06:23

小伙子文章写得还是不错 赞一个