请注意使用在C语言中进行缓存的API和不进行缓存的系统调用的组合

【摘要】
在一个将文本字符串写入文件的程序中,首先调用一个进行缓冲处理的API,然后再调用一个不进行缓冲处理的系统调用,来确认输出结果。由于API进行了缓冲处理,所以通过系统调用输出的字符串将先被输出。

###【环境】
###【Environment】

[root@vagrant-centos65 buff]# cat /etc/centos-release 
CentOS release 6.5 (Final)

###【代码】
该代码接收一个参数作为文件,并在该文件中写入字符串的过程。

如果有缓冲区

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	FILE *f;
	char *apiStr = "Hello, API\n";
	char *systemCallStr = "Hello, system call\n";

	if((f = fopen(argv[1], "w")) == NULL) {
		puts("fopen error");
		exit(1);
	}

	// fputsはバッファリングされる
	fputs(apiStr, f);
	// システムコールであるwriteはバッファリングされないため、即出力される
	if (write(fileno(f), systemCallStr, strlen(systemCallStr)) < 0) exit(1);
	fclose(f);
	exit(0);
}

执行结果

[root@vagrant-centos65 buff]# gcc -o buffer buffer.c 
[root@vagrant-centos65 buff]# ./buffer buffer.txt
[root@vagrant-centos65 buff]# cat buffer.txt
Hello, system call
Hello, API

如果没有缓冲的话

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	FILE *f;
	char *apiStr = "Hello, API\n";
	char *systemCallStr = "Hello, system call\n";

	if((f = fopen(argv[1], "w")) == NULL) {
		puts("fopen error");
		exit(1);
	}

	// バッファリングしないよう設定
	// あるいは、fputsした後にfflushでもOK
	setbuf(f, NULL);

	// 即出力される
	fputs(apiStr, f);
	// 即出力される
	if (write(fileno(f), systemCallStr, strlen(systemCallStr)) < 0) exit(1);
	fclose(f);
	exit(0);
}

执行结果

[root@vagrant-centos65 buff]# gcc -o no_buffer1 no_buffer1.c 
[root@vagrant-centos65 buff]# ./no_buffer1 no_buffer1.txt
[root@vagrant-centos65 buff]# cat no_buffer1.txt 
Hello, API
Hello, system call

总结:文件描述符和FILE之间可以自由转换,但是由于缓冲区可能导致输入输出顺序混乱,所以不应该随意混合使用这两者。

###【额外内容】
尽管printf应该具有缓冲功能,但API的输出似乎比系统调用更快。
[补充说明] 根据angel_p_57的评论

默认情况下,引用终端设备的输出流始终以行为单位进行缓冲处理。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
	char *apiStr = "Hello, API\n";
	char *systemCallStr = "Hello, system call\n";

	printf("%s", apiStr);
	if (write(STDOUT_FILENO, systemCallStr, strlen(systemCallStr)) < 0) exit(1);
	exit(0);
}

执行结果

[root@vagrant-centos65 buff]# gcc -o no_buffer2 no_buffer2.c 
[root@vagrant-centos65 buff]# ./no_buffer2
Hello, API
Hello, system call

###【参考书】
《普通的Linux编程 第2版》 从Linux的机制中学习gcc编程的王道

广告
将在 10 秒后关闭
bannerAds