最近在用dup/dup2做IO重定向的时候,由于缓冲区的问题出现了一次异步IO,导致无法正常输出正确的结果。
先贴出代码:
/*
* 从文件里面读出1000个随机数,进行排序,再写到另一个文件中(使用dup/dup2)
*
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char STR[32];
void quick(int *a, int i, int j);
int main(void)
{
int fd, fd2;
int save_fd, save_fd2;
int i;
if ((fd = open("data.txt", O_RDONLY)) < 0) {
perror("open data.txt");
exit(1);
}
if ((fd2 = open("sort_w.txt", O_RDWR|O_CREAT, 0644)) < 0) {
perror("open sort_w.txt");
exit(1);
}
save_fd = dup(STDIN_FILENO);
save_fd2 = dup(STDOUT_FILENO);
dup2(fd, STDIN_FILENO);
dup2(fd2, STDOUT_FILENO);
close(fd);
close(fd2);
STR *st = malloc(sizeof(STR) * 1000);
memset(st, 0, sizeof(STR)*1000);
int buf[1000] = {0};
for (i = 0; i < 1000; i++)
scanf("%s", st[i]);
for (i = 0; i < 1000; i++)
buf[i] = atoi(st[i]);
quick(buf, 0, 999);
char tmp[128] ={0};
for (i = 0; i < 1000; i++) {
memset(tmp, 0, 128);
sprintf(tmp, "%d\n", buf[i]);
/* error */
// printf("%s", tmp); // apue 标准输出关联到一个文件的时候,变成全缓冲的。
write(STDOUT_FILENO, tmp, strlen(tmp));
}
dup2(save_fd, STDIN_FILENO);
dup2(save_fd2, STDOUT_FILENO);
close(save_fd);
close(save_fd2);
free(st);
return 0;
}
void quick(int *a, int i, int j)
{
int m, n, temp;
int k;
m = i;
n = j;
k = a[(i + j) / 2]; /*选取的参照*/
do {
while (a[m]>k&&m<j) m++; /* 从左到右找比k大的元素*/
while (a[n]<k&&n>i) n--; /* 从右到左找比k小的元素*/
if (m <= n) { /*若找到且满足条件,则交换*/
temp = a[m];
a[m] = a[n];
a[n] = temp;
m++;
n--;
}
} while (m <= n);
if (m<j) quick(a, m, j); /*运用递归*/
if (n>i) quick(a, i, n);
}
问题出在注释的printf那一行,用write一切正常运行,用printf的时候出现如下结果。
forker@L:homework$ ./a.out
5
30094
29970
29882
29858
29769
29733
29690
29679
29673
29573
29434
29353
29309
29308
29269
29257
28751
28420
...
173
再来看看排序后生成的文件中:
673 30641
674 30637
675 30629
676 30628
677 30451
678 30401
679 30373
680 30364
681 30320
682 30199
683 3013
对比可以看出来,用printf在文件中写入了683行,并且没有写完,应该是30135,之后的数据打印到终端上。
后来,查阅资料Apue中说:
标准IO在关联到终端的时候是行缓冲的,在重将标准IO重定向到一个文件之后,这个时候就变成了全缓冲的。可以看到在第一次缓冲区满了的时候,IO例程进行刷新缓冲区,于是写入了文件。之后再将剩下的数据写到缓冲区,这个时候缓冲区并没有满,也就没有立刻刷新。接下来,标准IO又关联到终端,在进程终止之前,开始刷新缓冲区,于是剩下的数据被打印到终端上。