繁体   English   中英

使用在单个 CPU 上的两个进程上运行的两个 UNIX 管道测量上下文切换

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM