CPU存在内核模式和用户模式两种模式,只有处于内核模式时才允许访问设备,通过不同模式隔绝进程调动设备的权限。
Linux系统调用是操作系统提供给用户程序或者其他程序与操作系统内核进行交互的接口。这些调用允许程序请求操作系统执行特定的操作,如文件操作、进程管理、网络通信等。
计算机系统中各种进程与OS的关系如下图
一般来说,由在用户模式在运行的进程通过系统调用向内核发送相应的请求。其中包括了进程独有的代码直接向内核发起请求的情况,也包括了进程所依赖的库向内核发起请求的情况。
进程在执行创建进程、操作硬件等依赖于内核的处理时,必须通过系统调用向内核发起请求,系统调用的种类如下:
- 进程控制(创建和删除)
- 内存管理(分配和释放)
- 进程间通信
- 网络管理
- 文件系统操作
- 文件操作(访问设备)
CPU的模式切换
系统调用需要通过执行特殊的CPU命令来发起。通常进程运行在用户模式下,当通过系统调用向内核发送请求时,CPU会发生名为“中断”的事件。这时,CPU将用户从用户模式下切换到内核模式,然后根据请求内容进行相应的处理。当内核处理完成系统调用后,将重新回到用户模式,继续运行进程。
发起系统调用的情形
若要了解进程在发生了哪些系统调用,可以strace
命令可以对进程进行追踪。
例如对一个C语言hello world程序进行追踪:
先编写代码hello.c
#inclue<stdio.h>
int main(void)
{
puts("hello world");
return 0;
}
输入以下命令
cc -o hello hello.c
strace -c ./hello
其中第一行命令是编译,第二行是跟踪执行 hello 可执行文件时的系统调用次数
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
34.08 0.000106 13 8 mmap
16.40 0.000051 12 4 mprotect
11.25 0.000035 11 3 3 access
8.68 0.000027 13 2 open
6.75 0.000021 7 3 fstat
5.47 0.000017 17 1 write
5.47 0.000017 17 1 munmap
3.22 0.000010 5 2 close
2.57 0.000008 8 1 read
2.57 0.000008 8 1 execve
1.93 0.000006 6 1 arch_prctl
1.61 0.000005 5 1 brk
------ ----------- ----------- --------- --------- ----------------
100.00 0.000311 28 3 total
% time
:每种系统调用所占用的时间百分比。
seconds
:总时间(秒)。
usecs/call
:平均每次调用耗时(微秒)。
calls
:调用次数。
执行系统调用的一些实验
sar
命令由于获取进程分别在用户模式与内核模式下运行时间的比例。
sar -P ALL 1
10:33:22 AM CPU %user %nice %system %iowait %steal %idle
10:33:23 AM all 0.25 0.00 0.25 0.00 0.00 99.50
10:33:23 AM 0 0.00 0.00 0.00 0.00 0.00 100.00
10:33:23 AM 1 1.00 0.00 0.00 0.00 0.00 99.00
10:33:23 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
10:33:23 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
10:33:23 AM CPU %user %nice %system %iowait %steal %idle
10:33:24 AM all 0.25 0.00 0.25 0.00 0.00 99.50
10:33:24 AM 0 0.99 0.00 0.00 0.00 0.00 99.01
10:33:24 AM 1 0.00 0.00 0.00 0.00 0.00 100.00
10:33:24 AM 2 1.00 0.00 1.00 0.00 0.00 98.00
10:33:24 AM 3 0.00 0.00 0.00 0.00 0.00 100.00
在每一行中,从%user
到%idle
表示在CPU核心上运行的处理的类型。其中%user
表示用户态进程(非内核态)消耗 CPU 时间的百分比,而"%system"表示在内核模式下消耗CPU时间的百分比。“%idle”表示CPU空闲时间的百分比。目前CPU几乎是空闲的。
现在我们运行一个循环程序loop.c
int main(void)
{
for(;;);
}
编译并运行程序
cc -o loop loop.c
./loopc &
sar -P ALL 1 1
运行结果如下:
10:42:42 AM CPU %user %nice %system %iowait %steal %idle
10:42:43 AM all 25.13 0.00 0.25 0.00 0.00 74.62
10:42:43 AM 0 0.00 0.00 1.00 0.00 0.00 99.00
10:42:43 AM 1 0.00 0.00 1.00 0.00 0.00 99.00
10:42:43 AM 2 0.00 0.00 0.00 0.00 0.00 100.00
10:42:43 AM 3 100.00 0.00 0.00 0.00 0.00 0.00
Average: CPU %user %nice %system %iowait %steal %idle
Average: all 25.13 0.00 0.25 0.00 0.00 74.62
Average: 0 0.00 0.00 1.00 0.00 0.00 99.00
Average: 1 0.00 0.00 1.00 0.00 0.00 99.00
Average: 2 0.00 0.00 0.00 0.00 0.00 100.00
Average: 3 100.00 0.00 0.00 0.00 0.00 0.00
我们可以发现运行loop程序的进程始终处于用户模式
我们关闭之前的程序,修改循环,执行getppid()这个由于获取父进程的进程id的系统调用的程序进行相同的操作。
代码ppidloop.c:
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
for(;;)
getppid();
}
执行命令
cc -o ppid ppidloop.c
./ppidloop &
sar -P ALL 1 1
执行结果如下:
10:49:59 AM CPU %user %nice %system %iowait %steal %idle
10:50:00 AM all 6.00 0.00 19.50 0.75 0.00 73.75
10:50:00 AM 0 0.00 0.00 0.00 2.02 0.00 97.98
10:50:00 AM 1 0.00 0.00 1.00 0.00 0.00 99.00
10:50:00 AM 2 28.00 0.00 72.00 0.00 0.00 0.00
10:50:00 AM 3 0.00 0.00 1.01 0.00 0.00 98.99
Average: CPU %user %nice %system %iowait %steal %idle
Average: all 6.00 0.00 19.50 0.75 0.00 73.75
Average: 0 0.00 0.00 0.00 2.02 0.00 97.98
Average: 1 0.00 0.00 1.00 0.00 0.00 99.00
Average: 2 28.00 0.00 72.00 0.00 0.00 0.00
Average: 3 0.00 0.00 1.01 0.00 0.00 98.99
我们可以发现CPU核心3运行ppidloop程序占用了28%的运行时间,ppidloop程序发起请求来获取父进程id这一内核处理占用了72%的运行时间。