簡體   English   中英

帶有管道的多進程程序中的意外 scanf 行為

[英]unexpected scanf behavior in multi-process program with pipes

對於 UNIX OS class 中的練習,我應該開發一個程序,其中兩個兄弟進程通過 pipe 進行通信。 一個進程(生產者)應該從標准輸出讀取字符串並將它們發送到另一個進程(消費者),然后必須將字符串轉換為大寫並再次將其打印到標准輸出。 字符串“end”終止了兄弟進程和父進程。

該程序運行良好,但是當嘗試從用戶那里讀取整個短語而不是單個字符串時它會崩潰。

這是我的主要內容的一部分:

int main(void) {
int child_status, fd[2];

if (pipe(fd)) {
  fprintf(stderr, "Error: Could not create pipe.\n");
  return EXIT_FAILURE;
}

if (!fork()) {
  // child1
  producer(fd);
  exit(0);
} else if (!fork()) {
  // child2
  consumer(fd); 
  exit(0);
} else {
  // father
  pid_t pid;
...

生產者流程:

void producer(int *fd) {
   char buff[BUFFSIZE];
   char *line;
 
   close(fd[0]);
   while (1) {
     fflush(stdout);
     fprintf(stdout, "Insert string:\t");
     scanf("%[^\n]s%*c", buff);
     fflush(stdout);
     line = strdup(buff);
     printToPipe(fd[1], line);
 
     if (!strcmp(line, "end")) // Special string terminates process
       return;
 
     sleep(1);
   }
 
   return;
 }

以及消費者流程:

 void consumer(int *fd) {
   char *buff;
 
   close(fd[1]);
   while (1) {
     if ( (buff = readFromPipe(fd[0])) == NULL ) {
       close(fd[0]);
       return;
     }
     fprintf(stdout, "%s\n", strToUpper(buff));
     fflush(stdout);
   }
 
   return;
 }

printToPipe() 和 readfromPipe() 直接實現 read() 和 write() 系統調用。

意外行為:

一旦輸入第一個短語並轉換為大寫,程序就會無限循環打印出提示和第一個大寫短語(即: Insert string: TEST TEST )。 尤其是:

  • 盡管我考慮了 \n 字符( scanf("%[^\n]s%*c", buff); ),但后續的 scanfs 將被忽略。 如果我檢查 scanf 返回值,則第一次之后的所有迭代都為 0,
  • 似乎消費者用它的 output “自我維持”自己:它不等待通過 pipe 的新輸入並繼續打印轉換后的短語。

盡管我考慮了 \n 字符,但后續的 scanfs 被忽略了

代碼從不讀取'\n'

scanf("%[^\n]s%*c", buff); @John Bollinger的問題。

  1. "%[^\n]"將無限數量的非'\n字符讀入buff ,可能會溢出buff 它比gets()更糟糕。 如果至少讀取了 1 個非'\n'字符,則將 null 字符附加到buff ,否則掃描停止。

  2. "s"匹配一個's' ,這在上述之后是不期望的 - 只有一個'\n' ,因此掃描停止而不消耗任何'\n'

  3. 格式的 Rest 永遠不會執行。

代碼從不檢查輸入函數的返回值

通過檢查scanf()的返回值, buff的內容可能不變或不確定。

// scanf("%[^\n]s%*c", buff);
if (scanf("%[^\n]s%*c", buff) == 1) {
  ; // OK to use `buff`
}

OP 的scanf()可能會消耗第一行的大部分內容,將'\n'留在stdin中,但第二次scanf()調用肯定會立即停止,因為它試圖讀取第一行的'\n' 這可能會使buff保持不變。

使用fgets()stdin輸入讀取一行輸入並保存為字符串 @xing

// scanf("%[^\n]s%*c", buff);
if (fgets(buff, sizeof buff, stdin) == NULL) {
  ; // Handle end-of-file or rare input error
  break;
}

要從輸入行中刪除'\n' ,請參閱此答案和其他答案。
打印時,代碼可能會在字符串中保留'\n'而不是 append 之一。

//fprintf(stdout, "%s\n", strToUpper(buff));
fprintf(stdout, "%s", strToUpper(buff));

這將更好地處理超過BUFFSIZE的長輸入行,因為多余的字符只留給下一次迭代。

OP 可能需要調整strcmp(line, "end")測試以考慮可能的尾隨'\n'


一個進程(生產者)應該從標准輸出中讀取字符串......

詳細信息:生產者不是從stdout讀取字符串,而是從stdin讀取一行string

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM