measures context switch with two UNIX pipes running on two processes on a single CPU

Viewed 54

Quoted from Operating Systems: Three Easy Pieces cpu-mechanisms Homework (Measurement) session :

Measuring the cost of a context switch is a little trickier. The lmbench benchmark does so by running two processes on a single CPU, and setting up two UNIX pipes between them; a pipe is just one of many ways processes in a UNIX system can communicate with one another. The first process then issues a write to the first pipe, and waits for a read on the second; upon seeing the first process waiting for something to read from the second pipe, the OS puts the first process in the blocked state, and switches to the other process, which reads from the first pipe and then writes to the second. When the second process tries to read from the first pipe again, it blocks, and thus the back-and-forth cycle of communication continues. By measuring the cost of communicating like this repeatedly, lmbench can make a good estimate of the cost of a context switch.

My question is , does the pipe itself must be accessed by 2 different process ? Or it is just in this case (and also in lmbench tool) it was used by different process in orer to measure context switch ? Here are some code I searched on 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;
}
1 Answers

does the pipe itself must be accessed by 2 different process?

Yes, the pipe must be accessed by two processes. Because,

a context switch is described as the kernel suspending execution of one process on the CPU and resuming execution of some other process that had previously been suspended.

These context switches have a "cost", it takes time to do them.

Before each tasks runs, CPU needs to know where to load and start the task. This means the system needs to help set up CPU registers and program counters in advance.

In the example code above there is a fork() call. This fork creates another process, the child, and the parent also resumes execution. See man 2 fork The return value of the fork() call in the child will be 0 and in the parent it will be the pid of the child.

sched_setaffinity is used to ensure that the parent and child both run on the same cpu, CPU_SET(0, &set); See man 2 sched_setaffinity This is done to force context switching. Otherwise in today's multi-processor machines, the parent and child may run on different CPUs.

The block else if (cpid == 0) {} will be executed in the child process. The block else {} will be executed in the child process.

parent writes data into first_pipefd and gets blocked and suspended, the child resumes execution and read data from first_pipefd

Then the child writes data into second_pipefd and gets blocked and suspended, the parent resumes execution and read data from second_pipefd

The pipe blocking until it is read by the other process is what forces the context switch.

Thus the cpu is switching context between the parent process and the child process nloops times.

Related