APUE習題10.6(父子程序同步)
阿新 • • 發佈:2019-02-03
編寫一段程式測試圖10_24中父子程序的同步函式,要求程序建立一個檔案並向檔案寫一個整數0,然後程序呼叫fork,接著父子程序交替增加檔案中的計數器值,每次計數器值增加1,列印是哪一個程序進行來該增加1操作
沒有完全按照題目要求做,直接fork之後01234這樣列印的
先上一個標準輸出的實現
1、寫到標準輸出的實現
main.c:以下實現是有問題的
問題在於:子程序的資料空間、堆疊空間都會從父程序得到一個拷貝,而不是共享。在子程序對count進行++操作,並沒有影響到父程序著哦您能夠的count值,即使是全域性變數也不會改變,《APUE》8.3節說到,子程序是父程序的副本(子程序獲得父程序資料空間、堆和棧的副本)。父程序和子程序並不共享這些儲存空間部分,父程序和子程序共享正文段。
#include "apue.h" static int count = 0; int main(void) { pid_t pid; TELL_WAIT(); if((pid = fork()) < 0 ){ err_sys("fork error"); } else if (pid == 0) { while(1){ TELL_WAIT(); WAIT_PARENT(); printf("output from child:"); printf("count=%d \n",count++); TELL_PARENT(getppid()); } exit(0); } else{ printf("Output from parent:"); printf("count=%d\n",count++); TELL_CHILD(pid); while(1){ sleep(2); TELL_WAIT(); WAIT_CHILD(); printf("output from parent:"); printf("count=%d\n",count++); TELL_CHILD(pid); } } exit(0); }
所以會出現以下結果:
正確的實現為:
結果如下:#include "apue.h" static int count = 0; int main(void) { pid_t pid; TELL_WAIT(); if((pid = fork()) < 0 ){ err_sys("fork error"); } else if (pid == 0) { count++; while(1){ TELL_WAIT(); WAIT_PARENT(); printf("output from child:"); printf("count=%d \n",count); count=count+2; TELL_PARENT(getppid()); } exit(0); } else{ printf("Output from parent:"); printf("count=%d\n",count); TELL_CHILD(pid);//父程序一定要現執行一次,然後通知子程序,否則父子程序會永遠互相等待 while(1){ sleep(2); TELL_WAIT(); WAIT_CHILD(); printf("output from parent:"); count=count+2; printf("count=%d\n",count); TELL_CHILD(pid); } } exit(0); }
2、寫檔案實現
fork的一個特性是父程序所有的開啟檔案描述符(file_struct)都被複制到子程序中,父子程序的每個相同的開啟描述符共享一個檔案表項如圖。
這種共享的方式使父、子程序對同一個檔案使用了同一個檔案偏移量。如果父、子程序寫到同一個檔案描述符,但有沒有任何形式的同步,那麼它們的輸出就會相互混合。在fork之後處理檔案描述符有兩種常見的情況:
(1)父程序等待子程序完成。在這種情況下,父程序無須對其描述符做任何處理。當子程序終止之後,它曾進行過讀、寫的人一個共享描述符的檔案偏移量已經執行了相應的更新。
(2)父、子程序各自執行不同的程式段。這種情況下,在fork之後,父、子程序各自關閉它們不需使用的檔案描述符,這樣就不會干擾對方使用的檔案描述符。這種方式是網路服務程序中常用的方式。
#include "apue.h"
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
const char *str_parent="output from parent";
const char *str_child="output from child";
static int count = 0;
void print(int fd,const char*str)
{
char str_count[5];
write(fd,str,strlen(str));
sprintf(str_count,":%d\n",count);
write(fd,str_count,strlen(str_count));
}
int main(void)
{
pid_t pid;
int fd;
fd=open("./test.txt",O_RDWR|O_CREAT|O_TRUNC|O_SYNC,S_IRUSR|S_IWUSR);
TELL_WAIT();
print(fd,str_parent);
TELL_CHILD(pid);
if((pid = fork()) < 0 ){
err_sys("fork error");
} else if (pid == 0) {
count++;
print(fd,str_child);
while(1){
TELL_WAIT();
WAIT_PARENT();
count=count+2;
print(fd,str_child);
TELL_PARENT(getppid());
}
exit(0);
}
else{
while(1){
sleep(2);
TELL_WAIT();
WAIT_CHILD();
count=count+2;
print(fd,str_parent);
TELL_CHILD(pid);
}
}
close(fd);
exit(0);
}
附上tellwait.c
#include "apue.h"
static volatile sig_atomic_t sigflag; /* set nonzero by sig handler */
static sigset_t newmask, oldmask, zeromask;
static void
sig_usr(int signo) /* one signal handler for SIGUSR1 and SIGUSR2 */
{
sigflag = 1;
}
void
TELL_WAIT(void)
{
if (signal(SIGUSR1, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR1) error");
if (signal(SIGUSR2, sig_usr) == SIG_ERR)
err_sys("signal(SIGUSR2) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGUSR1);
sigaddset(&newmask, SIGUSR2);
/* Block SIGUSR1 and SIGUSR2, and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
err_sys("SIG_BLOCK error");
}
void
TELL_PARENT(pid_t pid)
{
kill(pid, SIGUSR2); /* tell parent we're done */
}
void
WAIT_PARENT(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for parent */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}
void
TELL_CHILD(pid_t pid)
{
kill(pid, SIGUSR1); /* tell child we're done */
}
void
WAIT_CHILD(void)
{
while (sigflag == 0)
sigsuspend(&zeromask); /* and wait for child */
sigflag = 0;
/* Reset signal mask to original value */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
err_sys("SIG_SETMASK error");
}