C语言中FIFO读/写的奇怪行为

时间:2013-11-10 13:22:29

标签: c fifo mkfifo

我有一个使用FIFO重现服务器的C程序。程序从输入FIFO读取两行 - 一个数字n和一个字符串str - 并写入输出FIFO n行,每行{{1} }}。我写了下面的代码。

str

该程序编译并运行时没有错误,但每次执行时都会输出不同数量的字符串。例如,如果输入FIFO中的两个输入行为#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <ctype.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #define MAX_SIZE 256 char *readline(int fd, char *buffer) { char c; int i = 0; while (read(fd, &c, 1) != 0) { if (c == '\n') break; buffer[i++] = c; } return buffer; } int main(int argc, char** argv) { mkfifo(argv[1], 0666); mkfifo(argv[2], 0666); int in = open(argv[1], O_RDONLY); int out = open(argv[2] ,O_WRONLY); char line[MAX_SIZE]; memset(line, 0, MAX_SIZE); int n, i; while (1) { strcpy(line, readline(in, line)); sscanf(line, "%d", &n); strcpy(line, readline(in, line)); for (i = 0; i < n; i++) { write(out, line, strlen(line)); write(out, "\n", 1); } } close(in); close(out); return 0; } ,则每次运行时会出现1到25次5\nhello次出现(频率似乎是完全随机的)。

我已经被困在这两天了。请给我一些帮助。

3 个答案:

答案 0 :(得分:2)

我没有声明或保证我甚至不知道你的程序做了什么,因为我已经20年了,因为我迫切需要在系统级别使用FIFO。但有一件事是清楚的。两天是一段很长的时间来处理某些事情而没有在调试器中运行它,这样做会暴露出许多问题。

首先,readline()永远不会终止传递的字符串。这与第二次和第二次一样重要,因为输入线中可能存在较短的数据。此外,read()可能会失败,并且这样做不会返回0,这是您循环中唯一会破坏的条件。该失败应该打破循环反映在返回结果中。因为您返回缓冲区指针,所以合理的失败结果可能是NULL:

考虑这样的事情:

char *readline(int fd, char *buffer)
{
    ssize_t res = 0;
    char c = 0;
    int i = 0;
    for(;;)
    {
        res = read(fd, &c, 1);
        if (res < 0)
            return NULL;
        else if (res == 0 || c == '\n')
            break;
        buffer[i++] = c;

    };
    buffer[i] = 0;
    return buffer;
}

有人可能会争辩说,如果缓冲区为空,它应该返回NULL,因为你不能在FIFO上放一个0长度的数据包。我留下给你决定,但确定你的算法是一个潜在的漏洞。


如果提交的缓冲区重叠,则strcpy()函数具有未定义的行为。由于readline()返回传入的缓冲区,并且由于所述相同的缓冲区也是strcpy()的目标相同的缓冲区,因此您的程序正在执行UB。从我看到的所有内容来看,strcpy()在这个程序中首先是无用的,甚至不应该

这显然是错误的:

strcpy(line, readline(in, line));
sscanf(line, "%d", &n);
strcpy(line, readline(in, line));

for (i = 0; i < n; i++) {
    write(out, line, strlen(line));
    write(out, "\n", 1);
}

以上应该是这样的:

if (readline(in, line))
{
    if (sscanf(line, "%d", &n) == 1)
    {
        if (readline(in, line))
        {
            for (i = 0; i < n; i++) 
            {
                write(out, line, strlen(line));
                write(out, "\n", 1);
            }
        }
    }
}

假设已按照规定对readline()进行了更改。这些可以组合成一个三表达式if子句,但如上所述,它至少是可调试的。换句话说,通过短路评估,这样做应该没有问题:

if (readline(in, line) &&
    sscanf(line, "%d", &n) == 1 &&
    readline(in, line))
{
    for (i = 0; i < n; i++) 
    {
        write(out, line, strlen(line));
        write(out, "\n", 1);
    }
}

但我建议你保留前者,直到你彻底调试过它。

最后,请注意readline()仍然是等待发生的缓冲区溢出。你真的应该将max-len传递给该函数并限制这种潜在的可能性,或者动态地管理缓冲区。

答案 1 :(得分:1)

代码不会为每次迭代初始化line,因此如果readline()没有读取任何内容,则会使line的内容保持不变。

并且您不测试sscanf()是否失败,代码无法识别als n保持不变,line的最后一个值打印出n次并且一切都重新开始......


同样readline()未检查read()是否失败。


要从此练习中学习,始终测试(系统)调用的结果是否失败。

答案 2 :(得分:-1)

int readline(int fd, char *buf, int nbytes) {
   int numread = 0;
   int value; /* read fonksiyonu sonunda okunan sayı degerini gore islem yapar */
   /* Controls  */
   while (numread < nbytes - 1) {
      value = read(fd, buf + numread, 1);
      if ((value == -1) && (errno == EINTR))
         continue;
      if ( (value == 0) && (numread == 0) )
         return 0;
      if (value == 0)
         break;
      if (value == -1)
         return -1;
      numread++;

      /* realocating for expand the buffer ...*/
      if( numread == allocSize-2 ){
         allocSize*=2;   /* allocSize yeterli olmadıgı zaman buf ı genisletmemizi saglayarak
         memory leak i onler  */
         buf=realloc(buf,allocSize);

         if( buf == NULL ){
           fprintf(stderr,"Failed to reallocate!\n");
             return -1; 
         }
      }
      /* Eger gelen karakter \n ise return okudugu karakter sayısı */
      if (buf[numread-1] == '\n') {
          buf[numread] = '\0';
          return numread; 
      }  
   }
   errno = EINVAL;
   return -1;   
}