10

使用strace探索tail -f的工作机制

 3 years ago
source link: https://www.lujun9972.win/blog/2019/09/26/%E4%BD%BF%E7%94%A8strace%E6%8E%A2%E7%B4%A2tail--f%E7%9A%84%E5%B7%A5%E4%BD%9C%E6%9C%BA%E5%88%B6/index.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用strace探索tail -f的工作机制

Julia Evans 写了一篇文章,使用 strace 探索 killall 的工作机制. 我也依葫芦画瓢,尝试一下用 strace 探索一下 tail -f 的工作机制。

下面是执行 strace -yy -tt -o strace.log tail -f data.log 的输出结果。期间追加了两次内容。

18:52:38.300588 execve("/usr/bin/tail", ["tail", "-f", "data.log"], 0x7fffd96ca0f0 /* 24 vars */) = 0
18:52:38.308541 brk(NULL)               = 0x7fffdda86000
18:52:38.310225 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
18:52:38.311734 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
18:52:38.313166 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3</etc/ld.so.cache>
18:52:38.314902 fstat(3</etc/ld.so.cache>, {st_mode=S_IFREG|0644, st_size=62985, ...}) = 0
18:52:38.316373 mmap(NULL, 62985, PROT_READ, MAP_PRIVATE, 3</etc/ld.so.cache>, 0) = 0x7fda1db90000
18:52:38.317783 close(3</etc/ld.so.cache>) = 0
18:52:38.319315 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
18:52:38.320763 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3</lib/x86_64-linux-gnu/libc-2.27.so>
18:52:38.322554 read(3</lib/x86_64-linux-gnu/libc-2.27.so>, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
18:52:38.323971 fstat(3</lib/x86_64-linux-gnu/libc-2.27.so>, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
18:52:38.325310 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fda1db80000
18:52:38.326690 mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3</lib/x86_64-linux-gnu/libc-2.27.so>, 0) = 0x7fda1d400000
18:52:38.328228 mprotect(0x7fda1d5e7000, 2097152, PROT_NONE) = 0
18:52:38.329655 mmap(0x7fda1d7e7000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3</lib/x86_64-linux-gnu/libc-2.27.so>, 0x1e7000) = 0x7fda1d7e7000
18:52:38.331180 mmap(0x7fda1d7ed000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fda1d7ed000
18:52:38.332608 close(3</lib/x86_64-linux-gnu/libc-2.27.so>) = 0
18:52:38.334070 arch_prctl(ARCH_SET_FS, 0x7fda1db81540) = 0
18:52:38.335616 mprotect(0x7fda1d7e7000, 16384, PROT_READ) = 0
18:52:38.337093 mprotect(0x7fda1de0f000, 4096, PROT_READ) = 0
18:52:38.338396 mprotect(0x7fda1da27000, 4096, PROT_READ) = 0
18:52:38.339662 munmap(0x7fda1db90000, 62985) = 0
18:52:38.341119 brk(NULL)               = 0x7fffdda86000
18:52:38.342395 brk(0x7fffddaa7000)     = 0x7fffddaa7000
18:52:38.343720 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3</usr/lib/locale/locale-archive>
18:52:38.345392 fstat(3</usr/lib/locale/locale-archive>, {st_mode=S_IFREG|0644, st_size=3481584, ...}) = 0
18:52:38.346708 mmap(NULL, 3481584, PROT_READ, MAP_PRIVATE, 3</usr/lib/locale/locale-archive>, 0) = 0x7fda1d0ae000
18:52:38.348051 close(3</usr/lib/locale/locale-archive>) = 0
18:52:38.349445 openat(AT_FDCWD, "data.log", O_RDONLY) = 3</tmp/data.log>
18:52:38.352538 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.353896 lseek(3</tmp/data.log>, 0, SEEK_CUR) = 0
18:52:38.355148 lseek(3</tmp/data.log>, 0, SEEK_END) = 4
18:52:38.356474 lseek(3</tmp/data.log>, 0, SEEK_SET) = 0
18:52:38.357764 read(3</tmp/data.log>, "1\n2\n", 4) = 4
18:52:38.359157 lseek(3</tmp/data.log>, 0, SEEK_SET) = 0
18:52:38.360401 read(3</tmp/data.log>, "1\n2\n", 4) = 4
18:52:38.361862 fstat(1</dev/pts/3>, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
18:52:38.363334 write(1</dev/pts/3>, "1\n", 2) = 2
18:52:38.364674 write(1</dev/pts/3>, "2\n", 2) = 2
18:52:38.366987 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.368533 fstatfs(3</tmp/data.log>, {f_type=0x53464846, f_bsize=4096, f_blocks=26214399, f_bfree=14077756, f_bavail=14077756, f_files=999, f_ffree=1000000, f_fsid={val=[1, 0]}, f_namelen=255, f_frsize=4096, f_flags=ST_VALID|ST_NOATIME}) = 0
18:52:38.369942 fstat(1</dev/pts/3>, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
18:52:38.371294 lstat("data.log", {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.372616 inotify_init()          = 4
18:52:38.374016 inotify_add_watch(4</unknown>, "data.log", IN_MODIFY) = 1
18:52:38.375391 stat("data.log", {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.376807 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.378130 select(5, [4</unknown>], NULL, NULL, NULL) = 1 (in [4])
--------------------------- 这里执行 seq 3 4 >/tmp/data.log-----------------------------------------------------------------------------------
18:52:58.852296 read(4</unknown>, "\1\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0", 25) = 16
18:52:58.855040 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=8, ...}) = 0
18:52:58.857313 read(3</tmp/data.log>, "3\n4\n", 8192) = 4
18:52:58.859427 write(1</dev/pts/3>, "3\n4\n", 4) = 4
18:52:58.861290 read(3</tmp/data.log>, "", 8192) = 0
18:52:58.863148 select(5, [4</unknown>], NULL, NULL, NULL) = 1 (in [4])
--------------------------- 这里执行 seq 5 6 >/tmp/data.log-----------------------------------------------------------------------------------
18:53:04.599560 read(4</unknown>, "\1\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0", 25) = 16
18:53:04.601325 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=12, ...}) = 0
18:53:04.602809 read(3</tmp/data.log>, "5\n6\n", 8192) = 4
18:53:04.604310 write(1</dev/pts/3>, "5\n6\n", 4) = 4
18:53:04.605677 read(3</tmp/data.log>, "", 8192) = 0
18:53:04.607089 select(5, [4</unknown>], NULL, NULL, NULL) = ? ERESTARTNOHAND (To be restarted if no handler)
--------------------------- 这里按下Ctrl-C-----------------------------------------------------------------------------------------------------
18:53:16.593133 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
18:53:16.599328 +++ killed by SIGINT +++
18:52:38.300588 execve("/usr/bin/tail", ["tail", "-f", "data.log"], 0x7fffd96ca0f0 /* 24 vars */) = 0
........
18:52:38.348051 close(3</usr/lib/locale/locale-archive>) = 0

这么一大段,不过是在读取各种动态库而已。可以忽略不看。

18:52:38.352538 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.353896 lseek(3</tmp/data.log>, 0, SEEK_CUR) = 0
18:52:38.355148 lseek(3</tmp/data.log>, 0, SEEK_END) = 4
18:52:38.356474 lseek(3</tmp/data.log>, 0, SEEK_SET) = 0

通过 lseek 读出文件结尾的字节数

18:52:38.357764 read(3</tmp/data.log>, "1\n2\n", 4) = 4
18:52:38.359157 lseek(3</tmp/data.log>, 0, SEEK_SET) = 0
18:52:38.360401 read(3</tmp/data.log>, "1\n2\n", 4) = 4
18:52:38.361862 fstat(1</dev/pts/3>, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
18:52:38.363334 write(1</dev/pts/3>, "1\n", 2) = 2
18:52:38.364674 write(1</dev/pts/3>, "2\n", 2) = 2

读出文件内容并且一行一行的输出,从中可以看到,文件中原始包含的内容有两行,分别是 12.

后面有多次使用 fstatlstat 获取文件状态,老实说不确定什么目的,估计是检测文件是否被删除,或者改名吧。

18:52:38.372616 inotify_init()          = 4
18:52:38.374016 inotify_add_watch(4</unknown>, "data.log", IN_MODIFY) = 1
18:52:38.375391 stat("data.log", {st_mode=S_IFREG|0666, st_size=4, ...}) = 0
18:52:38.376807 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=4, ...}) = 0

这里用 inotify 机制创建一个watch,

18:52:38.378130 select(5, [4</unknown>], NULL, NULL, NULL) = 1 (in [4])

通过 select 进行阻塞,只有当文件发生修改的情况下才会继续往下执行

后面是多个 read,fstat,write,select 语句的循环

18:52:58.852296 read(4</unknown>, "\1\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0", 25) = 16
18:52:58.855040 fstat(3</tmp/data.log>, {st_mode=S_IFREG|0666, st_size=8, ...}) = 0
18:52:58.857313 read(3</tmp/data.log>, "3\n4\n", 8192) = 4
18:52:58.859427 write(1</dev/pts/3>, "3\n4\n", 4) = 4
18:52:58.861290 read(3</tmp/data.log>, "", 8192) = 0
18:52:58.863148 select(5, [4</unknown>], NULL, NULL, NULL) = 1 (in [4])

也就是先读出 watch 到的事件,然后在当前文件的位置将新增的内容读出来,再一行一行输出。

这里我没有试验重写文件内容会怎样,不过估计会先重置文件读写位置再读写文件内容吧。

18:53:16.593133 --- SIGINT {si_signo=SIGINT, si_code=SI_KERNEL} ---
18:53:16.599328 +++ killed by SIGINT +++

很明显是捕获到了 SIGINT 信号然后退出了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK