![为什么这里的“w”比简单的 python 脚本需要更多的系统调用?](https://linux22.com/image/103920/%E4%B8%BA%E4%BB%80%E4%B9%88%E8%BF%99%E9%87%8C%E7%9A%84%E2%80%9Cw%E2%80%9D%E6%AF%94%E7%AE%80%E5%8D%95%E7%9A%84%20python%20%E8%84%9A%E6%9C%AC%E9%9C%80%E8%A6%81%E6%9B%B4%E5%A4%9A%E7%9A%84%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%EF%BC%9F.png)
我想潜入深处Linux更多并且一直在使用一个不错的工具strace
(版本:4.11) 在Ubuntu 16.04 LTS我很好奇为什么像下面这样调用
# strace -c w
需要比简单的系统调用更多的数量Python可以打印字符串 10 次的脚本?
为了更具体,这里是输出strace -c w
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
shan tty7 :0 13:20 3:37m 8:14 0.72s /sbin/upstart --user
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
61.67 0.000037 0 487 4 open
23.33 0.000014 0 478 read
15.00 0.000009 0 485 close
0.00 0.000000 0 3 write
0.00 0.000000 0 226 stat
0.00 0.000000 0 38 fstat
0.00 0.000000 0 27 lseek
0.00 0.000000 0 63 mmap
0.00 0.000000 0 31 mprotect
0.00 0.000000 0 14 munmap
0.00 0.000000 0 5 brk
0.00 0.000000 0 22 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 1 ioctl
0.00 0.000000 0 19 19 access
0.00 0.000000 0 30 alarm
0.00 0.000000 0 2 socket
0.00 0.000000 0 2 2 connect
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 uname
0.00 0.000000 0 20 fcntl
0.00 0.000000 0 2 getdents
0.00 0.000000 0 1 getrlimit
0.00 0.000000 0 2 2 statfs
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 futex
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00 0.000060 1965 27 total
为了strace -c /tmp/loop.py
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000160 9 18 getdents
0.00 0.000000 0 54 read
0.00 0.000000 0 10 write
0.00 0.000000 0 44 2 open
0.00 0.000000 0 45 close
0.00 0.000000 0 108 16 stat
0.00 0.000000 0 69 fstat
0.00 0.000000 0 10 lstat
0.00 0.000000 0 30 6 lseek
0.00 0.000000 0 32 mmap
0.00 0.000000 0 16 mprotect
0.00 0.000000 0 1 munmap
0.00 0.000000 0 14 brk
0.00 0.000000 0 68 rt_sigaction
0.00 0.000000 0 1 rt_sigprocmask
0.00 0.000000 0 13 1 ioctl
0.00 0.000000 0 9 9 access
0.00 0.000000 0 10 select
0.00 0.000000 0 3 dup
0.00 0.000000 0 1 execve
0.00 0.000000 0 1 fcntl
0.00 0.000000 0 4 2 readlink
0.00 0.000000 0 1 getrlimit
0.00 0.000000 0 1 sysinfo
0.00 0.000000 0 1 getuid
0.00 0.000000 0 1 getgid
0.00 0.000000 0 1 geteuid
0.00 0.000000 0 1 getegid
0.00 0.000000 0 1 sigaltstack
0.00 0.000000 0 1 arch_prctl
0.00 0.000000 0 1 futex
0.00 0.000000 0 1 set_tid_address
0.00 0.000000 0 1 set_robust_list
0.00 0.000000 0 1 getrandom
------ ----------- ----------- --------- --------- ----------------
100.00 0.000160 573 36 total
推理
w
576
与Python 脚本中的调用相比,总共需要 1965 次调用。
问题
这个实现具体吗?即如果我用 C/C++ 而不是 python 编写代码,
read
调用会更多吗?调用是否会随着所使用的程序或工具的复杂性的增加而增加?有什么相关性吗?
与执行脚本相比,为什么执行脚本时出现更多错误
w
?
笔记
- 这里的Python脚本是一个简单的循环,它打印查看作为字符串 10 次,并且没有任何复杂性。 (不包括代码啤酒)
答案1
系统调用与语言无关;人们可以轻松地用汇编语言编写一个工具慢点如果系统调用的使用效率低下,则优于高级语言中的等效实现;一个精心设计但简单的例子来说明这一点是:
$ strace -c perl -e 'print "A"x9999' >/dev/null
...
100.00 0.000026 224 23 total
$ strace -c ./max >/dev/null
...
100.00 0.000430 10000 total
./max
下面的 Linux 的 IA-64 汇编程序在哪里:
SECTION .text
Message: db "A"
global _start
_start:
mov r9,9999
_again:
mov rax,1 ; sys_write
mov rdi,1 ; stdout
mov rsi,Message
mov rdx,1
syscall
dec r9
jnz _again
_finish:
mov rax,60 ; sys_exit
mov rdi,0 ; exit code
syscall
通过编译
$ nasm -f elf64 -o max.o max.asm ; ld -o max max.o
节目必须执行相同的任务以使直接比较有效。
调用通常可能会随着所使用工具的复杂性而增加,尽管我们可以设计一种极其复杂的神经网络的矛盾情况,该神经网络只利用sys_read
(获取输入)sys_write
(发出输出),然后可能sys_exit
。我想您可以分析系统调用的数量与“复杂性”(也许是代码行?)并绘制出来看看是否有任何趋势? (阅读“unikernel”可能会提供有关复杂性争论的信息。)
错误列是有多少错误,如关闭标准输出然后调用写入该内容的内容所示,然后显然失败:
$ strace -c ./max >&-
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
100.00 0.000427 0 9999 9999 write
0.00 0.000000 0 1 execve
------ ----------- ----------- --------- --------- ----------------
100.00 0.000427 10000 9999 total