[英]measures context switch with two UNIX pipes running on two processes on a single CPU
引用自操作系统:三个简单的 cpu 机制作业(测量) session:
测量上下文切换的成本有点棘手。 lmbench 基准测试通过在单个 CPU 上运行两个进程,并在它们之间设置两个 UNIX 管道来实现; pipe 只是 UNIX 系统中进程可以相互通信的多种方式之一。 然后第一个进程向第一个 pipe 发出写入,并等待第二个读取; 在看到第一个进程等待从第二个 pipe 读取某些内容时,操作系统将第一个进程放入阻塞的 state,然后切换到另一个进程,该进程从第一个 pipe 读取,然后写入第二个。 当第二个进程再次尝试从第一个 pipe 读取时,它会阻塞,因此来回通信循环继续进行。 通过反复测量这样的通信成本,lmbench 可以很好地估计上下文切换的成本。
我的问题是,pipe 本身是否必须由 2 个不同的进程访问? 或者只是在这种情况下(也在lmbench 工具中)它被不同的进程用来测量上下文切换? 这是我在github上搜索的一些代码:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sched.h>
int main(int argc, char *argv[]) {
// measure context switch
cpu_set_t set;
CPU_ZERO(&set);
CPU_SET(0, &set);
int first_pipefd[2], second_pipefd[2];
if (pipe(first_pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
if (pipe(second_pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
} else if (cpid == 0) { // child
if (sched_setaffinity(getpid(), sizeof(cpu_set_t), &set) == -1) {
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < nloops; i++) {
read(first_pipefd[0], NULL, 0);
write(second_pipefd[1], NULL, 0);
}
} else { // parent
if (sched_setaffinity(getpid(), sizeof(cpu_set_t), &set) == -1) {
exit(EXIT_FAILURE);
}
gettimeofday(&start, NULL);
for (size_t i = 0; i < nloops; i++) {
write(first_pipefd[1], NULL, 0);
read(second_pipefd[0], NULL, 0);
}
gettimeofday(&end, NULL);
printf("context switch: %f microseconds\n", (float) (end.tv_sec * 1000000 + end.tv_usec - start.tv_sec * 1000000 - start.tv_usec) / nloops);
}
return 0;
}
pipe 本身是否必须由 2 个不同的进程访问?
是的,pipe 必须被两个进程访问。 因为,
上下文切换被描述为 kernel 在 CPU 上暂停执行一个进程并恢复执行之前暂停的其他进程。
这些上下文切换有一个“成本”,需要时间来完成。
在每个任务运行之前,CPU 需要知道从哪里加载和启动任务。 这意味着系统需要提前帮助设置 CPU 寄存器和程序计数器。
在上面的示例代码中有一个fork()
调用。 这个fork
创建了另一个进程,child,parent也恢复执行。 请参见man 2 fork子进程中fork()
调用的返回值将为 0,而在父进程中它将是子进程的 pid。
sched_setaffinity
用于保证父子都运行在同一个cpu上, CPU_SET(0, &set);
参见man 2 sched_setaffinity这样做是为了强制上下文切换。 否则在今天的多处理器机器中,父进程和子进程可能运行在不同的 CPU 上。
块else if (cpid == 0)
{} 将在子进程中执行。 块else {}
将在子进程中执行。
parent 向first_pipefd
写入数据并被阻塞和挂起,child 恢复执行并从first_pipefd
读取数据
然后子进程向second_pipefd
写入数据被阻塞挂起,父进程恢复执行并从second_pipefd
读取数据
pipe 在被其他进程读取之前阻塞是强制上下文切换的原因。
因此 cpu 在父进程和子进程之间切换上下文nloops
次。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.