利用管道通信(pipe)测量进程间的上下文切换(context switch)开销
《https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-mechanisms.pdf》
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. You can try to re-create something similar here, using pipes, or perhaps some other communication mechanism such as UNIX sockets.
然后,第一个进程(子进程)向第一个管道发出写操作,并等待第二个管道的读操作;在看到第一个进程等待从第二个管道读取内容时,操作系统会将第一个进程置于阻塞状态,并切换到另一个进程(父进程),后者从第一个管道读取内容,然后向第二个管道写操作。当第二个进程再次尝试从第一个管道中读取数据时,它就会阻塞,这样来回循环的通信就继续进行。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <assert.h>
const int N = 1e2 + 10; // Number of iterations for context switches
int main(int argc, char *argv[]) {
int pipefd[2][2]; // Array to hold two pipes: one for each direction of communication
char buf; // Buffer to hold the character read from the pipe
// Check if the correct number of arguments are provided
if (argc != 3) {
fprintf(stderr, "Usage: %s <string> <string>\n", argv[0]);
exit(EXIT_FAILURE);
}
// Create the two pipes
if (pipe(pipefd[0]) == -1 || pipe(pipefd[1]) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
// Fork the process to create a child process
int rc1 = fork();
if (rc1 < 0) {
// Fork failed
fprintf(stderr, "fork failed\n");
exit(EXIT_FAILURE);
} else if (rc1 == 0) {
// Child process
close(pipefd[0][0]); // Close unused read end of the first pipe
close(pipefd[1][1]); // Close unused write end of the second pipe
for (int i = 0; i < N; ++i) {
// Write to the first pipe
write(pipefd[0][1], argv[1], strlen(argv[1]) + 1);
// Read from the second pipe
while (read(pipefd[1][0], &buf, 1) > 0) {
// ************
//write(STDOUT_FILENO, &buf, 1);
if (buf == '\0') {
//write(STDOUT_FILENO, "\n", 1);
break;
}
}
}
// Close the used pipe ends before exiting
close(pipefd[0][1]);
close(pipefd[1][0]);
exit(EXIT_SUCCESS);
} else {
// Parent process
close(pipefd[0][1]); // Close unused write end of the first pipe
close(pipefd[1][0]); // Close unused read end of the second pipe
struct timeval start, end;
// Get the start time
int rc1 = gettimeofday(&start, NULL);
for (int i = 0; i < N; ++i) {
// Read from the first pipe
while (read(pipefd[0][0], &buf, 1) > 0) {
// ************
//write(STDOUT_FILENO, &buf, 1);
if (buf == '\0') {
//write(STDOUT_FILENO, "\n", 1);
break;
}
}
// Write to the second pipe
write(pipefd[1][1], argv[2], strlen(argv[2]) + 1);
}
// Get the end time
int rc2 = gettimeofday(&end, NULL);
assert(rc1 == 0 && rc2 == 0);
// Calculate elapsed time
double elapsed = (double) end.tv_sec + (double) end.tv_usec / 1e6 - ((double) start.tv_sec + (double) start.tv_usec / 1e6);
// Calculate average context switch time
double context_switch = elapsed / (2 * N);
printf("Total time: %f seconds\n", elapsed);
// Print the average context switch time
printf("Average context switch time: %lf seconds\n", context_switch);
// Close the used pipe ends
close(pipefd[1][1]);
close(pipefd[0][0]);
}
return 0;
}
运行结果:
[chap6] :) cc -o contextSwitch contextSwitch.c
[chap6] :) ./contextSwitch 1111 222222
Total time: 0.010745 seconds
Average context switch time: 0.000049 seconds
[chap6] :) ./contextSwitch 1111 222222
Total time: 0.014709 seconds
Average context switch time: 0.000067 seconds
[chap6] :) ./contextSwitch 1111 222222
Total time: 0.011982 seconds
Average context switch time: 0.000054 seconds
[chap6] :)