操作系统-课堂笔记-strace的使用(南航)

文章目录

  • 系统调用-strace的使用
    • 声明
    • 简介
    • 使用strace
      • 一个简单的例子
      • 再来一个简单的例子
      • 如果绑定到进程
      • strace的统计功能
      • 研究fread/fwrite的内部实现
    • 课外讨论

系统调用-strace的使用

声明

本章和考试完全无关,老师讲strace的目的可以简单理解成帮助我们提升技术视野,篇幅不长,可略读

简介

strace是什么?

  • 一个简单的跟踪系统调用执行的工具
  • 可以从开始到结束跟踪二进制的执行
  • 在进程的生命周期中输出:系统调用名称、系统调用的参数和返回值

看了定义大概能猜到叭:其作用是Debug和分析应用程序!!!

strace有什么功能?

  • 基于特定的系统调用或系统调用组进行过滤
  • 统计特定系统调用:使用次数、所花费时间、系统调用的成功和错误数量
  • 可以通过pid附加到任何正在运行的进程

现在明确了,其就是用来分析应用程序或者Debug的,比如程序运行很慢,那可以统计下程序在执行过程中有多少次和IO相关的系统调用,如果某一个功能运行时IO相关的系统调用明显变多,那么IO得背锅了!

使用strace

一个简单的例子

下面动手写代码,很简单的代码:

#include
#include
#include

char *s[10]={"cat", "pi1.c", NULL};
int main(){
	execvp("cat", s);
	exit(0);
}

然后编译:gcc -o test test.c
然后:strace ./test 2> log.txt 将strace给出的系统调用信息重定向到log.txt中,看内容,有点多:

execve("./test", ["./test"], [/* 72 vars */]) = 0
brk(NULL)                               = 0x9c8e000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f21000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=86603, ...}) = 0
mmap2(NULL, 86603, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f0b000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\207\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1786484, ...}) = 0
mmap2(NULL, 1792540, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7d55000
mmap2(0xb7f05000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1af000) = 0xb7f05000
mmap2(0xb7f08000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f08000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d54000
set_thread_area({entry_number:-1, base_addr:0xb7d54700, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb7f05000, 8192, PROT_READ)   = 0
mprotect(0x8049000, 4096, PROT_READ)    = 0
mprotect(0xb7f4a000, 4096, PROT_READ)   = 0
munmap(0xb7f0b000, 86603)               = 0
execve("/home/zjp/bin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/home/zjp/.local/bin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/sbin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/bin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/bin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/sbin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/cat", ["cat", "pi1.c"], [/* 72 vars */]) = 0
brk(NULL)                               = 0x9507000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f44000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=86603, ...}) = 0
mmap2(NULL, 86603, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f2e000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\207\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1786484, ...}) = 0
mmap2(NULL, 1792540, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7d78000
mmap2(0xb7f28000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1af000) = 0xb7f28000
mmap2(0xb7f2b000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f2b000
close(3)                                = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7d77000
set_thread_area({entry_number:-1, base_addr:0xb7d77700, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 (entry_number:6)
mprotect(0xb7f28000, 8192, PROT_READ)   = 0
mprotect(0x8054000, 4096, PROT_READ)    = 0
mprotect(0xb7f6d000, 4096, PROT_READ)   = 0
munmap(0xb7f2e000, 86603)               = 0
brk(NULL)                               = 0x9507000
brk(0x9528000)                          = 0x9528000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=10219008, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7b77000
close(3)                                = 0
open("/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=10219008, ...}) = 0
mmap2(NULL, 4096, PROT_READ, MAP_PRIVATE, 3, 0x741000) = 0xb7f43000
close(3)                                = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
open("pi1.c", O_RDONLY|O_LARGEFILE)     = 3
fstat64(3, {st_mode=S_IFREG|0664, st_size=713, ...}) = 0
fadvise64_64(3, 0, 0, POSIX_FADV_SEQUENTIAL) = 0
mmap2(NULL, 139264, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7b55000
read(3, "#include\n#include\n#include

好吧,我承认看不明白,太多了叭~!也就最后几行看的直观点!

再来一个简单的例子

直接在命令行敲入:sudo strace dd if=/dev/zero of=/dev/null 2> log.txt
仍然能得到一大堆的信息,不过大量出现read(…),这属于IO密集型应用

如果绑定到进程

我们刚才的运行方式是strace ./test,那么如果某一个进程已经在运行了,我想用strace跟踪其系统调用怎么办?

首先top指令找到该进程的id号,然后sudo strace -p 4375,就OK了!!!

strace的统计功能

操作系统-课堂笔记-strace的使用(南航)_第1张图片
很简单-c选项,count=xxx,表示统计多少次,如上图所示能给出比较详细的统计信息,至于上述信息什么意思暂且不深究,这里大概理解strace命令能统计应用程序的系统调用即可,未来可能用得到!!!

研究fread/fwrite的内部实现

我们主要研究fread 和 read 的区别
fwrite和write的区别。

之前讲过缓存思想在计算机发展史上是十分伟大的,同理fread和fwrite也是利用缓存的思想。

我们在用c语言的fread和fwrite的时候,需要使用FILE结构体,该结构体内部维护了一个缓冲区。

在读取操作时:先调用read系统调用将内部缓冲区填满,然后从内部缓冲区读数据,同理再次调用fread的时候,可以先判断相关数据是否在缓冲区内,如果在那么就不用调用read系统调用了,下面我们实验验证,这个实验做的可能有点小问题:
代码:

#include


int main(){
	FILE *fp;
	char buf[1024];
	int i;

	fp=fopen("pi1.c", "r");
	for(i=0; i<1024; i++){
		fread(buf, 1, 1, fp);
	}
}

编译运行strace ./test_fread,结果如下
操作系统-课堂笔记-strace的使用(南航)_第2张图片
按照我的理解,read应该只调用一次鸭?为什么调用了那么多,不过有点靠谱的一点是:只有第一个read真正读取到了数据,其他的read都没有读取到数据,因为返回值表示读取到的字节数!

课外讨论

使用strace还可以做什么呢?

  • 我们可以验证lua和python的多线程库是系统级线程还是用户级线程
  • 写一段程序,然后运行,使用strace跟踪,看是否调用了创建线程的系统调用即可,这里仅仅提供一个思路,具体实验就不做了!
    //代码大概时这个样子
    strace lua thread.lua 2>&1 | grep clone
    strace python thread.py 2>&1 | grep clone
    
  • 老师给的结果是:thread.lua是用户态线程,thread.py是内核级线程

Linux的多线程基于clone系统调用,Linux中的线程只是在被创建时clone了父进程的资源。

本章和考试几乎无关,fread和read以及fwrite和write的区别可能会考!

本系列博客目录
下一篇:IO

你可能感兴趣的:(Linux,南航-操作系统-课堂笔记)