android log.d 参数,Android log 机制 - logd 总览

Android 早期版本使用的是一个 log 驱动,后来逐渐使用 logd 进程替代(具体哪个版本我就没有去探究了,至少在 Android 8.0 里,log 驱动已经被移除)。原有 log 驱动负责的功能,都由 logd 完成。此外,logd 还可以读取 Linux 内核 printk、selinux 的 log。

logd 的启动

logd 是由 init 进程启动的:

1

2

3

4

5

6

7

8

9

10

11

12# system/core/rootdir/init.rc

on post-fs

# Load properties from

# /system/build.prop,

# /odm/build.prop,

# /vendor/build.prop and

# /factory/factory.prop

load_system_props

# start essential services

start logd

start servicemanager

# ...

logd 的参数在 logd.rc 中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24# system/core/logd/logd.rc

service logd /system/bin/logd

socket logd stream 0666 logd logd

socket logdr seqpacket 0666 logd logd

socket logdw dgram 0222 logd logd

file /proc/kmsg r

file /dev/kmsg w

user logd

group logd system readproc

writepid /dev/cpuset/system-background/tasks

service logd-reinit /system/bin/logd --reinit

oneshot

disabled

user logd

group logd

writepid /dev/cpuset/system-background/tasks

on fs

write /dev/event-log-tags "# content owned by logd

"

chown logd logd /dev/event-log-tags

chmod 0644 /dev/event-log-tags

restorecon /dev/event-log-tags

可以看到,init 进程会帮 logd 创建 3 个 Unix 域 socket,分别为 /dev/socket/logd, /dev/socket/logdr, /dev/socket/logdw。

创建 socket 系统调用原型如下:

1

2

3

4#include

#include

int socket(int domain, int type, int protocol);

init.rc 中的 stream, seqpacket, dgram 用于设置 socket 函数的第二个参数 type,分别对应 SOCK_STREAM, SOCK_SEQPACKET, SOCK_DGRAM。

SOCK_STREAM 提供的是可靠的流数据(类比于 TCP),SOCK_SEQPACKET 则提供可靠的基于包的数据(可靠的UDP),SOCK_DGRAM 可以用 UDP 来类比,不可靠的包传输。详细信息可以查看 man page 了解。

当然,domain 参数是 PF_UNIX(估计写着代码的程序员比较老派,新的程序建议使用 AF_LOCAL,两者没有区别)。

这三个 socket 的功能如下:

socket logd 用于外接受控制命令

客户端通过 logdr 读取 log 数据。使用 seqpacket,可以让用户在可靠地读取数据的同时,一次读取一条 log

客户端通过 logdw 写入 log 数据。由于类型是 dgram,在非常繁忙的时候,log 可能会丢失。但是,这可以避免客户端阻塞在写 log 的调用上

logd 的初始化

init 进程启动 logd 后,和其他程序一样,首先执行的是 main 函数。main 函数的主要工作如下:

读取系统属性,判断是否需要读内核的 log (klog 和 selinux 的log)

初始化一些信号量,启动 reinit 线程

启动各个子服务,监听上面我们提到的几个 socket

如果需要读内核 log,也监听对应的 log

阻塞等待(main 函数不能退出,否则进程直接会退出,即便还有线程在运行)

下面我们一起看看他的 main 函数:

1

2

3

4

5

6

7

8

9// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// issue reinit command. KISS argument parsing.

if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {

return issueReinit();

}

// ...

}

在上面 init.rc 中,我们看到,正常启动 logd 是不带参数的,所以这里的 if 不会执行。当重新启动时,带 --reinit 参数。这里我们就假定是正常启动。

1

2

3

4# system/core/logd/logd.rc

service logd /system/bin/logd

service logd-reinit /system/bin/logd --reinit

接下来获取 /dev/kmsg 的描述符。这个文件用于跟内核的 log 系统通信。正常情况下,init 进程会帮我们打开。如果没有,我们自己打开它。

1

2

3

4

5

6

7

8

9

10

11

12// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

static const char dev_kmsg[] = "/dev/kmsg";

fdDmesg = android_get_control_file(dev_kmsg);

if (fdDmesg < 0) {

fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));

}

// ...

}

接着,读取系统属性,判断是否需要读内核的 log。如果需要,就打开 /proc/kmsg。这个文件用于读取内核 log。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

int fdPmesg = -1;

bool klogd = __android_logger_property_get_bool(

"logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |

BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);

if (klogd) {

static const char proc_kmsg[] = "/proc/kmsg";

fdPmesg = android_get_control_file(proc_kmsg);

if (fdPmesg < 0) {

fdPmesg = TEMP_FAILURE_RETRY(

open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));

}

if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);

}

// ...

}

默认情况下,会读取内核 log。这里我们就直接假设 klogd 为 true。

跟着,启动 reinit 线程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38// system/core/logd/main.cpp

static sem_t uidName;

static uid_t uid;

static char* name;

static sem_t reinit;

static bool reinit_running = false;

static LogBuffer* logBuf = nullptr;

static sem_t sem_name;

int main(int argc, char* argv[]){

// ...

// Reinit Thread

sem_init(&reinit, 0, 0);

sem_init(&uidName, 0, 0);

sem_init(&sem_name, 0, 1);

pthread_attr_t attr;

if (!pthread_attr_init(&attr)) {

struct sched_param param;

memset(&param, 0, sizeof(param));

pthread_attr_setschedparam(&attr, &param);

pthread_attr_setschedpolicy(&attr, SCHED_BATCH);

if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {

pthread_t thread;

reinit_running = true;

if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {

reinit_running = false;

}

}

pthread_attr_destroy(&attr);

}

// ...

}

sem_init 用于初始化 POSIX 信号量,它的原型如下:

1

2include

int sem_init(sem_t *sem, int pshared, unsigned int value);

pshared 参数控制是否在多个进程间共享。这里我们只是用于进程内部的通信,所以传入 0。

在读这段代码的时候,有一个值得注意的是,pthread 在成功的时候返回 0,失败则返回一个非 0 的错误码(这一点跟一般的系统调用不同。一般的系统调用,失败的情况下会返回 -1)。

举例来说,中间用于判断 pthread_create 是否成功的 if 语句,只有在线程创建失败的时候才会执行。

1

2

3if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {

reinit_running = false;

}

关于 reinit_thread_start 函数,后面我们遇到了再看它的具体内容。

下面判断是否需要读 selinux 的 log。然后,调用 drop_privs 函数根据是否读取内核 log 设置一些权限。关于 drop_privs 函数,有兴趣的读者可以自行阅读源码,这部分并不会影响 logd 的逻辑。

1

2

3

4

5

6

7

8

9

10

11

12// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

bool auditd =

__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);

if (drop_privs(klogd, auditd) != 0) {

return -1;

}

// ...

}

下面,实例化 LogBuffer 并注册信号处理器。所有的 log 都会写入这个 LogBuffer;当客户端需要读取 log 的时候,也从这个 LogBuffer 读取。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

// Serves the purpose of managing the last logs times read on a

// socket connection, and as a reader lock on a range of log

// entries.

LastLogTimes* times = new LastLogTimes();

// LogBuffer is the object which is responsible for holding all

// log entries.

logBuf = new LogBuffer(times);

signal(SIGHUP, reinit_signal_handler);

if (__android_logger_property_get_bool(

"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |

BOOL_DEFAULT_FLAG_ENG |

BOOL_DEFAULT_FLAG_SVELTE)) {

logBuf->enableStatistics();

}

// ...

}

现在,各种准备工作都完成了,启动实际的工作线程:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

// LogReader listens on /dev/socket/logdr. When a client

// connects, log entries in the LogBuffer are written to the client.

LogReader* reader = new LogReader(logBuf);

if (reader->startListener()) {

exit(1);

}

// LogListener listens on /dev/socket/logdw for client

// initiated log messages. New log entries are added to LogBuffer

// and LogReader is notified to send updates to connected clients.

LogListener* swl = new LogListener(logBuf, reader);

// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value

if (swl->startListener(600)) {

exit(1);

}

// Command listener listens on /dev/socket/logd for incoming logd

// administrative commands.

CommandListener* cl = new CommandListener(logBuf, reader, swl);

if (cl->startListener()) {

exit(1);

}

// LogAudit listens on NETLINK_AUDIT socket for selinux

// initiated log messages. New log entries are added to LogBuffer

// and LogReader is notified to send updates to connected clients.

LogAudit* al = nullptr;

if (auditd) {

al = new LogAudit(logBuf, reader,

__android_logger_property_get_bool(

"ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)

? fdDmesg

: -1);

}

LogKlog* kl = nullptr;

if (klogd) {

kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);

}

readDmesg(al, kl);

// failure is an option ... messages are in dmesg (required by standard)

if (kl && kl->startListener()) {

delete kl;

}

if (al && al->startListener()) {

delete al;

}

// ...

}

在 logd 进程启动的时候,内核很可能已经有 log 数据存在,readDmesg() 把已有的 log 读出来放到 logBuffer 中。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42// system/core/logd/main.cpp

static void readDmesg(LogAudit* al, LogKlog* kl){

if (!al && !kl) {

return;

}

int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);

if (rc <= 0) {

return;

}

// Margin for additional input race or trailing nul

ssize_t len = rc + 1024;

std::unique_ptr buf(new char[len]);

rc = klogctl(KLOG_READ_ALL, buf.get(), len);

if (rc <= 0) {

return;

}

if (rc < len) {

len = rc + 1;

}

buf[--len] = '\0';

if (kl && kl->isMonotonic()) {

kl->synchronize(buf.get(), len);

}

ssize_t sublen;

for (char *ptr = nullptr, *tok = buf.get();

(rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));

tok = nullptr) {

if ((sublen <= 0) || !*tok) continue;

if (al) {

rc = al->log(tok, sublen);

}

if (kl) {

rc = kl->log(tok, sublen);

}

}

}

klogctl 是 Linux 内核特有的系统调用,用于读取、设置内核 log,原型如下,详情可以查看 man page:

1

2#include

int klogctl(int type, char *bufp, int len);

第一个 klogctl 的 type 为 KLOG_SIZE_BUFFER,该调用返回内核 log 缓冲的总长度。

值得注意的是,查看 man page 时,man page 里对应的常量为 KLOG_ACTION_**。这些常量跟 KLOG_** 是一一对应的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14// platform/bionic/libc/include/sys/klog.h

/* These correspond to the kernel's SYSLOG_ACTION_whatever constants. */

#define KLOG_CLOSE 0

#define KLOG_OPEN 1

#define KLOG_READ 2

#define KLOG_READ_ALL 3

#define KLOG_READ_CLEAR 4

#define KLOG_CLEAR 5

#define KLOG_CONSOLE_OFF 6

#define KLOG_CONSOLE_ON 7

#define KLOG_CONSOLE_LEVEL 8

#define KLOG_SIZE_UNREAD 9

#define KLOG_SIZE_BUFFER 10

第二个 klogctl 使用 KLOG_READ_ALL 读取所有的 log。随后,使用 LogAudit, LogKlog 讲读取到的 log 写入 LogBuffer。

最后,main 函数在 pause() 上永久等待。这是因为,如果 main 函数退出,进程就会退出(即使没有最后那个 exit(0),main 函数返回也会导致进程退出)。

1

2

3

4

5

6

7

8

9

10// system/core/logd/main.cpp

int main(int argc, char* argv[]){

// ...

TEMP_FAILURE_RETRY(pause());

exit(0);

// ...

}

logd 的启动到这里就结束了,实际的 log 读写逻辑,后面再一一分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python的requests模块功能_【python】requests模块初探(一)

一、写在前面 Requests 是用Python语言编写&#xff0c;基于 urllib&#xff0c;采用 Apache2 Licensed 开源协议的 HTTP 库。它比 urllib 更加方便&#xff0c;可以节约我们大量的工作&#xff0c;完全满足 HTTP 测试需求。Requests 的哲学是以 PEP 20 的习语为中心开发的&…

麒麟980可以升级鸿蒙吗,这五部麒麟980手机确定能升鸿蒙OS,其中有你吗?

原标题&#xff1a;这五部麒麟980手机确定能升鸿蒙OS&#xff0c;其中有你吗&#xff1f;就在今年九月的华为开发者大会上&#xff0c;余承东“一不小心”吐露出EMUI 11明年能升级鸿蒙OS 2.0的事实&#xff0c;这让许多华为用户兴奋不已&#xff0c;但首先你的手机必须要先能升…

python没有指针如何实现链表_Java、Python中没有指针,怎么实现链表、图等数据结构?...

Java、Python中没有指针&#xff0c;怎么实现链表、图等数据结构&#xff1f; (2016-10-06 11:08:19) 分类&#xff1a; C 其实Java, python完全可以看成在语言层用语法糖隐藏了指针。JAVA中到处充满指针&#xff0c;只不过把C中的指针封装成了对象的引用了&#xff0c;引用就相…

android语言列表+波斯,Android 多语言入门及实战

右键res&#xff0c;选择 New Resource Directory ,选择 locale ,选择自己想要适配的语言类型设置应用内多语言Android 7.0以下/*** 设置多语言*/public void setConfiguration(LocaleList locales, Context context) {Configuration configuration context.getResources().ge…

python接入excel_通过Python导入Excel数据,实现自动化数据测试

一、安装openpyxl pip install openpyxl 二、获取Excel表格数据 以下是我的表格数据 #导入load_workbook工作表 from openpyxl import load_workbook #创建粘贴工作表数据的类 class ParseExcel(): #工作表路径及工作表的名字 def __init__(self,excelPath,sheetName): #加载表…

html长图转换成pdf,将长 html 导入拆分 PDF

我的情况是&#xff1a;单击按钮时&#xff0c;将 html 上的数据导入 PDF 文件。由于此 PDF 必须具有一些复杂的必需样式&#xff0c;因此我的第一步是使用 html2canvas.js 将此页面转换为图像&#xff0c;然后使用 jsPDF.js 将此图像导入 PDF当数据太大时&#xff0c;必须拆分…

python 魔法方法常用_python 常用的魔法方法

1. 构造方法 __new__(cls,[...) 对象实例化时第一个调用的方法&#xff0c;它只取下 cls 参数&#xff0c;并把其他参数传给 __init__ 。 __init__(self,[...]) 使用传入的参数来初始化实例&#xff0c;不能返回除了None的任何值。 __del__(self) 定义了当对象被垃圾回收时的行…

c# 从地址拷贝byte_面试必备的 “零拷贝” 问题!从头给你说!

本文作者&#xff1a;ksfzhaohui来源&#xff1a;juejin.im/post/5cad6f1ef265da039f0ef5df前言I/O概念1.缓冲区2.虚拟内存3.mmapwrite方式4.sendfile方式Java零拷贝1.MappedByteBuffer2.DirectByteBuffer3.Channel-to-Channel传输Netty零拷贝其他零拷贝总结前言从字面意思理解…

html css实现登录注册页面,基于HTML5+css+JS_的精美登陆注册界面

【实例简介】基于HTML5cssJS的精美登陆注册界面-------------------------------【实例截图】【核心代码】login4├── index.html├── resources│ ├── images│ │ ├── arrow.png│ │ ├── arrow-p.png│ │ ├── bg.png│ │ ├── btn.pn…

lda 可以处理中文_中文分词(jieba)和语料库制作(gensim)

本文的内容为以下两个部分&#xff1a;文本分词&#xff08;jieba&#xff09;语料库制作&#xff08;gensim&#xff09;结巴&#xff08;jieba&#xff09;分词在自然语言处理领域中&#xff0c;分词和提取关键词都是对文本处理时通常要进行的步骤。用Python语言对英文文本进…

js时间搓化为今天明天_秋冬国产搓背神器!360°无死角,让你搓背不求人,太舒服了...

秋冬国产搓背神器&#xff01;360无死角&#xff0c;让你搓背不求人&#xff0c;太舒服了&#xff01;夏季悄然离去&#xff0c;秋季快步走来&#xff0c;距离冬季也不远了~天气变冷以后&#xff0c;下班、放学回家了最惬意不过的就是洗个澡&#xff0c;如果泡完澡再搓个背&…

用html制作广告图片切换效果,基于jquery实现图片广告轮换效果代码

效果图:实现代码:hotmarquee*{margin: 0;padding: 0;}body{font: 12px;padding-top: 50px;padding-right: 200px;padding-bottom: 100px;padding-left: 200px;}ul{list-style: none;}img{padding: 2px;border: 1px solid #eee;}a{outline: none;}#imgs{width: 410px;margin-rig…

matplotlib柱状图上方显示数据_Python数据分析matplotlib可视化之绘图!

Matplotlib是一个基于python的2D画图库&#xff0c;能够用python脚本方便的画出折线图&#xff0c;直方图&#xff0c;功率谱图&#xff0c;散点图等常用图表&#xff0c;而且语法简单。Python中通过matplotlib模块的pyplot子库来完成绘图。Matplotlib可用于创建高质量的图表和…

应付账款账龄分析模板_企业财务报表分析论文应如何着手?

首先是企业的选择&#xff0c;最好选取上市公司进行分析&#xff0c;上市公司的财务数据比较透明&#xff0c;完全可以从新浪财经、中国证券网等平台获得详尽的报表数据资料&#xff0c;一般选取近三到五个年度。有了数据就可以着手分析了&#xff0c;我们可以从下面几个方法入…

dbeaver导入excel文件_PyQT5练习:制作Excel文件导入MySQL窗口

本文环境配置&#xff1a;系统>windows10&#xff1a;64位工具>PyCharm&#xff1a;2018.3.1语言>Python&#xff1a;3.7.1第三方库PyQT5&#xff1a;5.11.3pyqt5-tools&#xff1a;5.11.3.1.4PyMySQL&#xff1a;0.9.3openpyxl&#xff1a;2.5.12练手&#xff1a;制作…

编程中的蛇形填空问题_PCB—蛇形线的作用,这次真的是把你搞懂了

经常能看到论坛里有人在问蛇形线的问题。平时我们能看到蛇形线的地方大都是一些高速高密度板&#xff0c;好像带有蛇形线的板子就更高级&#xff0c;会画蛇形线就是高手了。网上关于蛇形线的文章也有很多&#xff0c;总感觉有些帖子的内容会误导新手&#xff0c;给人们带来困扰…

html5控制gif速度,gif加速软件 教你加快GIF图片的播放速度

一位狸友在编辑GIF动态图片时碰到了一个问题&#xff0c;就是不知怎么把GIF动画的播放速度加快(或减慢)。如果你也正巧碰到此类疑问或想知道GIF加速的方法&#xff0c;可以接着往下看哦。其实先理解了GIF动画的原理&#xff0c;并找对合适的gif编辑软件&#xff0c;解决这问题并…

html文本弹性,HTML5 很有趣的文本蹦床/弹性弯曲动效

CSS语言&#xff1a;CSSSCSS确定import url(http://fonts.googleapis.com/css?familyOpenSans:400,800,300);html,body {height: 100%;}body {display: -webkit-box;display: -webkit-flex;display: -ms-flexbox;display: flex;-webkit-box-pack: center;-webkit-justify-cont…

android studio mvvm模板生成_使用Vue快速生成shape背景图

写在前面在日常的Android开发之中&#xff0c;我们通常都会根据UI图去手动创建shape或者selector背景图&#xff0c;虽说创建起来很简单&#xff0c;但是未免也会感到繁琐&#xff0c;因此也研究了一些这方面的知识&#xff0c;包括自定义shapedrawable、dataBinding&#xff0…

python 句子中没有中文_人生感悟经典句子,生活中可以没有诗歌,但不能没有诗意...

也曾有过一些热忱和勇气&#xff0c;为了自以为的命中注定颠沛流离&#xff0c;本想有一段花好月圆&#xff0c;却未曾想过就此天各一方。不是不爱&#xff0c;不是不会爱&#xff0c;只是少了坚持走下去的勇气。想必这是很多人的心声。错过抑或过错&#xff0c;都是爱情里常有…