我有一个使用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
次出现(频率似乎是完全随机的)。
我已经被困在这两天了。请给我一些帮助。
答案 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;
}