当前位置: 首页 > 工具软件 > Liblog > 使用案例 >

Android L liblog

胡承悦
2023-12-01

       Android L 的log系统相比之前的Android版本有了很大改变,本文将探讨下L版本的liblog与之前的不同。

       首先还是从__android_log_buf_write()函数看起,这个函数和之前的版本基本一样。

int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
{
    struct iovec vec[3];
    char tmp_tag[32];

    if (!tag)
        tag = "";

    /* XXX: This needs to go! */
    if ((bufID != LOG_ID_RADIO) &&
         (!strcmp(tag, "HTC_RIL") ||
        !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
        !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
        !strcmp(tag, "AT") ||
        !strcmp(tag, "GSM") ||
        !strcmp(tag, "STK") ||
        !strcmp(tag, "CDMA") ||
        !strcmp(tag, "PHONE") ||
        !strcmp(tag, "SMS"))) {
            bufID = LOG_ID_RADIO;
            /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
            snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
            tag = tmp_tag;
    }

    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(bufID, vec, 3);
}

       函数返回write_to_log() ,write_to_log函数是以宏定义的形式出现的:

static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
   

      可以看到,write_to_log初始化为指向__write_to_log_init()函数,至此,函数结构与之前的版本均保持一致,从__write_to_log_init()开始出现不同。__write_to_log_init()函数源码如下:

static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#ifdef HAVE_PTHREADS
    pthread_mutex_lock(&log_init_lock);
#endif

    if (write_to_log == __write_to_log_init) {
        int ret;

        ret = __write_to_log_initialize();
        if (ret < 0) {
#ifdef HAVE_PTHREADS
            pthread_mutex_unlock(&log_init_lock);
#endif
            return ret;
        }

        write_to_log = __write_to_log_kernel;
    }

#ifdef HAVE_PTHREADS
    pthread_mutex_unlock(&log_init_lock);
#endif

    return write_to_log(log_id, vec, nr);
}
      

        可以看到,如果是第一次调用write_to_log,此时write_to_log指向__write_to_log_init,此时会首先调用__write_to_log_initialize(),__write_to_log_initialize()函数源码如下:            

static int __write_to_log_initialize()
{
    int i, ret = 0;

#if FAKE_LOG_DEVICE
    for (i = 0; i < LOG_ID_MAX; i++) {
        char buf[sizeof("/dev/log_system")];
        snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));
        log_fds[i] = fakeLogOpen(buf, O_WRONLY);
    }
#else
    if (logd_fd >= 0) {
        i = logd_fd;
        logd_fd = -1;
        close(i);
    }

    i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
    if (i < 0) {
        ret = -errno;
        write_to_log = __write_to_log_null;
    } else if (fcntl(i, F_SETFL, O_NONBLOCK) < 0) {
        ret = -errno;
        close(i);
        i = -1;
        write_to_log = __write_to_log_null;
    } else {
        struct sockaddr_un un;
        memset(&un, 0, sizeof(struct sockaddr_un));
        un.sun_family = AF_UNIX;
        strcpy(un.sun_path, "/dev/socket/logdw");

        if (connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un)) < 0) {
            ret = -errno;
            close(i);
            i = -1;
        }
    }
    logd_fd = i;
#endif

    return ret;
}


         在这个函数中,重点关注这句:i = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);此处使用了socket函数,socket函数原型为int socket(int domain,int type,int protocol),参数domain指定使用何种的地址类型,参数type表示套接口的类型,参数protocol表示使用的传输协议。对于此处的socket函数,PF_UNIX表示UNIX进程通信协议,SOCK_DGRAM|SOCK_CLOEXEC表示使用不连续不可信赖的数据包连接并且去掉子进程对父进程的继承,0表示协议编号可忽略。函数成功返回套接字描述符,失败返回-1.然后将该套接字的性质设置为非阻塞,即要么成功要么失败而不会被阻塞。若设置成功,则进行后续操作,若失败,修改write_to_log的指向到__write_to_log_null

      如果成功,会创建结构体变量struct sockaddr_un un,其中sockaddr_un表示UNIX Domain Socket,是一种IPC机制,有别于网络socket。UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口 号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径,这个socket文件由bind()调用创建,如果调用bind()时该文件已存 在,则bind()错误返回。使 用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。

     结构体成员变量配置完成后,使用connect函数建立socket i与地址/dev/socket/lodw的连接。成功返回0,失败返回-1。

     然后修改write_to_log的指向到__write_to_log_kernel。关于__write_to_log_kernel后续继续分析。

 类似资料:

相关阅读

相关文章

相关问答