LoginSignup
3
3

More than 5 years have passed since last update.

【C言語】バッファリングするAPIとバッファリングしないシステムコールの組み合わせに注意

Last updated at Posted at 2019-01-25

###【概要】
文字列をファイルに書き込むプログラムでバッファリングするAPIを先に呼び出し、その後にバッファリングしないシステムコールを呼び出した出力結果を確認します。
APIはバッファリングするため、システムコールで出力した文字列が先に出力されます。

###【環境】

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

###【コード】
パラメータでファイルを渡して、そのファイルに文字列を書き込む処理です。

####①バッファリング有りの場合

buffer.c
#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

####②バッファリング無しの場合

no_buffer1.c
#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は自由に行き来できますが、このようにバッファによって入出力の順序がおかしくなる場合があるため、安易に2つを混ぜてはいけないようです。

###【おまけ】
printfはバッファリングされるはずなのに、システムコールよりAPIの方が先に出力される。
[追記] angel_p_57さんのコメントより

端末デバイスを参照する出力ストリームは、 デフォルトでは常に行単位でバッファーリングされている

no_buffer2.c
#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プログラミングの王道

3
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3