执行创建线程和操作硬件的时候依赖于系统调用,想要操作需要提前调用内核发送请求。
系统调用可以分为以下几类
进程控制(创建和删除)
内存管理(分配和释放)
进程间通信
网络管理
文件访问(访问设备)
CPU 模式切换
系统会发送特殊的 CPU 命令发起。在进程中的用户模式下,当通过系统调用向内核发送请求,CPU 会发生一个中断事件,这个时候切换为内核状态,根据请求的内容进行处理,当内核请求完之后就会重新回到用户模式。并且这个 CPU 信号不是所有的系统都一样的
注意:用户模式不可能直接绕过内核进行系统调用

测试 Hello World 程序发生的系统调用
#include <stdio.h>
int main(void)
{
puts("hello world");
return 0;
}
使用 strace 命令的输出结果
execve("./hello", ["./hello"], 0x7ffc9e25b5f0 /* 23 vars */) = 0
brk(NULL) = 0x24f6000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9de0d1d000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=30274, ...}) = 0
mmap(NULL, 30274, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9de0d15000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`&\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2156592, ...}) = 0
mmap(NULL, 3985920, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f9de072f000
mprotect(0x7f9de08f3000, 2093056, PROT_NONE) = 0
mmap(0x7f9de0af2000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c3000) = 0x7f9de0af2000
mmap(0x7f9de0af8000, 16896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f9de0af8000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9de0d14000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9de0d12000
arch_prctl(ARCH_SET_FS, 0x7f9de0d12740) = 0
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/sysconfig/strcasecmp-nonascii", F_OK) = -1 ENOENT (No such file or directory)
mprotect(0x7f9de0af2000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f9de0d1e000, 4096, PROT_READ) = 0
munmap(0x7f9de0d15000, 30274) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9de0d1c000
write(1, "hello world\n", 12) = 12
exit_group(0) = ?
+++ exited with 0 +++
sar 命令,可以查看用户模式和内核模式下运行的时间比例
完整用法:man sar

只有用户模式程序
我们测试一个只有用户模式的程序
loop.c
int main(void)
{
for(;;)
;
}
进行编译
cc -o loop loop.c
运行 loop
./loop &

查看调用情况
sar -P ALL 1
可以看到几乎没有系统调用而是一直是用户模式

调用示意图

既有用户模式也有系统模式
下面测试一个 ppidloop.c
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
for(;;)
getppid();
}
注释:getppid() 是获取父线程的进程 ID
使用下面的命令可以获取到 1s 时间内运行的运行时间比例
sar -P ALL 1

在 CPU1 核心上面运行运行 getppid 占用了 59% 的时间
根据 ppidloop 程序发出的请求来获取父进程的进程 ID 这一内核处理占用了 41% 的运行时间
为什么 %system 不是 100% ? 因为用于循环执行 main() 函数内的 getppid() 的循环处理,是属于进程自身的处理
流程示意图
下面是直接用的书上面的示意图,根据我的实际情况需要改一下 用户模式:内核模式 = 59:41

一般 %system 的值高达好几十的时候一般是因为陷入系统调用,或者是服务器负载过高
如何确认系统调用的确时间?
可以使用 strace 命令后面加上一个 -T 的选项实现
比如我们测试一下 helloworld.c
strace -T -o hello.log ./hello
这个后面的 <0.000040> 就是耗时,单位是微秒

Comments
Join the discussionNo comments yet. Be the first to comment.