我应该测量两个进程之间的延迟和带宽。为此,我使用管道和叉子用 C 语言编写了一个简单的程序。为了确保一切正常,我希望父进程和子进程交替工作。所以我希望父进程在写入后停止,并在子进程读取并打印带有“a”的字符串后继续。
我使用了kill()和pause()以及sleep。我的问题是,程序有时会自行终止,而在其他情况下它工作正常,因为我想它可能不会达到竞争条件。
到目前为止我有以下代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <signal.h>
#define DATASIZE 5
int count = 0;
double diff = 0;
void wakeup_handler(int sig)
{
// Do nothing
}
int main(int argc, char *argv[])
{
int pipefd[2];
pid_t pid;
int i, n;
double total_time = 0;
struct timeval t1, t2;
if (pipe(pipefd) == -1)
{
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
// Child Process
if (pid == 0)
{
close(pipefd[0]);
char tmp1[DATASIZE];
// Set tmp to 'a'
for (i = 0; i < DATASIZE; i++)
{
tmp1[i] = 'a';
}
gettimeofday(&t1, NULL);
signal(SIGUSR1, wakeup_handler);
for (int i = 0; i < 1000; i++) {
write(pipefd[1], tmp1, sizeof(tmp1));
pause();
count++;
printf("CountC: %d\n", count);
}
gettimeofday(&t2, NULL);
diff = (t2.tv_sec - t1.tv_sec); // sec to ms
diff += (t2.tv_usec - t1.tv_usec) / 1e6; // us to sec
//Correction of 10 seconds because of the 10 seconds sleep in the parent process
// diff+= diff - 10;
double band = (DATASIZE * 1000) / diff;
printf("Average latency: %lf seconds\n", diff / 1000);
printf("Average bandwidth: %lf Mbps\n", band / 1e6);
close(pipefd[1]);
}
// Parent Process
else
{
close(pipefd[1]);
char tmp2[DATASIZE];
for (int i = 0; i < 1000; i++) {
// printf("Parent: Iteration %d...\n", i+1);
read(pipefd[0], tmp2, sizeof(tmp2));
printf("%s\n", tmp2);
sleep(0.01);
kill(pid, SIGUSR1);
}
close(pipefd[0]);
}
return 0;
}
为了避免在pause
执行之前发送信号的竞争情况,可以使用
sleep
来阻塞,而不是通过在父进程中调用
sigprocmask
来“购买更多时间”在子进程中传递信号,延迟它们的到达,直到它们稍后“解锁”。延迟信号被称为“待定”。
sigsuspend
可用于在等待信号到达时暂时解锁信号(通过更改信号掩码)。
为了避免父进程在子进程完成建立其信号处理之前发送数据请求信号的竞争情况,可以使用管道来传输一些初始数据。通过在父进程中执行阻塞
read
,子进程可以通过初始 write
来解除对父进程的阻塞,以指示它已准备好。
工作完成后,父进程应该wait
让子进程终止。
sigaction
而不是 signal
。
这是对您的示例的粗略重构。但是,为了简洁起见,它没有错误处理。请注意
write
的阻塞性质,并且 read
有助于在此处创建 lockstep,确保进程之间迭代的奇偶性。否则,调用
kill
N次并不能保证
N写入管道,因为如果 to-be-delivered 信号已经处于
pending状态,则会被丢弃。使这种同步起作用的一个重要部分是父进程立即等待它请求的数据。
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
struct packet {
char buffer[32];
};
void wakeup_handler(int sig)
{
/* Do nothing */
(void) sig;
}
int main(void)
{
/* each process receives a copy of the number of tests to run */
unsigned cycles = 1000;
int pipefd[2];
pipe(pipefd);
pid_t pid = fork();
if (pid == 0) {
/* Child process */
close(pipefd[0]);
struct sigaction sa = { .sa_handler = wakeup_handler };
sigaction(SIGUSR1, &sa, NULL);
sigset_t mask, original;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
/* block (delay) delivery of the SIGUSR1 signal */
sigprocmask(SIG_BLOCK, &mask, &original);
/* write some initial data
* letting the parent know the child has finished
* setting up its signal handling
*/
int ready = 1;
write(pipefd[1], &ready, sizeof ready);
struct timeval t1, t2;
int count = 0;
double diff = 0;
gettimeofday(&t1, NULL);
while (cycles--) {
struct packet output = { "aaaaa" };
/* temporary restore the original signal mask
* and wait for a signal to arrive
* (including previously delayed signals)
*/
sigsuspend(&original);
write(pipefd[1], &output, sizeof output);
printf("CountC: %d\n", ++count);
}
gettimeofday(&t2, NULL);
diff = (t2.tv_sec - t1.tv_sec); // sec to ms
diff += (t2.tv_usec - t1.tv_usec) / 1e6; // us to sec
double band = (sizeof (struct packet) * 1000) / diff;
printf("Average latency: %lf seconds\n", diff / 1000);
printf("Average bandwidth: %lf Mbps\n", band / 1e6);
close(pipefd[1]);
/* ensure child does not run off */
exit(0);
}
/* Parent process */
close(pipefd[1]);
/* block execution in the parent process until
* some initial data arrives indicating the child process
* is ready to receive signals
*/
int initialized;
read(pipefd[0], &initialized, sizeof initialized);
while (cycles--) {
struct packet input;
kill(pid, SIGUSR1);
read(pipefd[0], &input, sizeof input);
printf("%.*s\n", (int) sizeof input.buffer, input.buffer);
}
close(pipefd[0]);
/* wait for the child process to terminate */
wait(NULL);
}