###【概要】
文字列をファイルに書き込むプログラムでバッファリングする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