[英]Eliminating a potential racing condition
我應該測量兩個進程之間的延遲和帶寬。 為此,我使用 pipe 和 fork 在 C 中編寫了一個簡單的程序。 為確保一切正常,我希望父進程和子進程交替工作。 所以我希望父進程在寫入后停止並在子進程讀取並打印帶有'a'的字符串后繼續。
我使用 kill() 和 pause() 以及睡眠。 我的問題是程序有時會自行終止,而在其他情況下它運行良好,因為我想它可能沒有達到競爭條件。
如果有另一種方法來處理這個問題,我將不勝感激。 如果有任何信息遺漏,請告訴我,不要只是 dwonvote。
到目前為止,我有以下代碼:
#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
可用於在等待信號到達時臨時解鎖信號(通過更改信號掩碼)。
為了避免父進程在子進程完成建立其信號處理之前發送數據請求信號的競爭條件,pipe 可用於傳輸一些初始數據。 通過在父進程中執行阻塞read
,子進程可以指示它已准備好進行解除阻塞父進程的初始write
。
當工作完成后,父進程應該wait
子進程終止。
為了顯着提高可移植性,更喜歡sigaction
而不是signal
。
這是您示例的粗略重構。 但是,為了簡潔起見,它沒有錯誤處理。
請注意write
和read
的阻塞性質有助於在此處創建一個鎖步,確保進程之間的迭代對等。 否則,調用kill
N次並不能保證對 pipe 進行N次寫入,因為如果待交付信號已經處於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);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.