OS,  Study

[Linux] 표준 입출력 (stdin, stdout, stderr)

NameDescription
stdin키보드 입력을 뜻한다
stdout화면 출력을 말한다 (line buffer 사용)
stderr화면 출력을 말한다 (non buffer)

stdoutstderr의 차이는 line buffer의 사용 유무다. 우리가 C programming에서 사용하는 printffprintf(stdout, "")과 동일한 함수로, 텍스트를 line buffer에 저장한 뒤에 출력해준다.

반면 stderr 표준에러는 긴급성이 더 중요하기 때문에 line buffer를 거치지 않고 즉시 출력한다.

Example

#include <stdio.h>

int main(int argc, char **argv) {
    fprintf(stdout, "Shumin");
    sleep(10);
    return 0;
}

stdout을 통한 fprintf 출력은 개행문자 '\n'를 만나야 buffer flush를 하는데 만약 만나지 않는다면 process가 종료될 때 buffer flush를 한다. sleep 함수가 있기 때문에 10초 뒤에 종료된 뒤에 “Shumin” 문자가 찍히는 것을 볼 수 있다.

#include <stdio.h>

int main(int argc, char **argv) {
    fprintf(stderr, "Shumin");
    sleep(10);
    return 0;
}

반면 stderr를 사용한 경우 10초가 지나 process가 종료되기 전에 즉시 출력되는 것을 볼 수 있다.

실제로 system call trace 프로그램을 통해서 확인해보면 다음과 같이 stdin은 0, stdout은 1, stderr는 2로 나타난다.

$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7fff1d9b5380 /* 17 vars */) = 0
brk(NULL)                               = 0x55b225104000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=28088, ...}) = 0
mmap(NULL, 28088, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcefb302000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcefb300000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcefacef000
mprotect(0x7fcefaed6000, 2097152, PROT_NONE) = 0
mmap(0x7fcefb0d6000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fcefb0d6000
mmap(0x7fcefb0dc000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcefb0dc000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fcefb3014c0) = 0
mprotect(0x7fcefb0d6000, 16384, PROT_READ) = 0
mprotect(0x55b223c6a000, 4096, PROT_READ) = 0
mprotect(0x7fcefb309000, 4096, PROT_READ) = 0
munmap(0x7fcefb302000, 28088)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 16), ...}) = 0
brk(NULL)                               = 0x55b225104000
brk(0x55b225125000)                     = 0x55b225125000
nanosleep({tv_sec=5, tv_nsec=0}, 0x7ffcfa7ad940) = 0
write(1, "Shumin", 6Hello)           = 6
exit_group(0)                           = ?
+++ exited with 0 +++

Line buffer 응용

Line buffer는 개행문자 ‘\n'를 만나기전엔 flush가 되지 않는다. 만약 0.1초마다 문자를 출력해주는 프로그램을 만든다고 하면 아래처럼 하게되면 원하는대로 동작하지 않게 된다.

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

int main(int argc, char **argv) {
    FILE *fp;
    int ch; 
    fp = fopen(argv[1], "r");
    while((ch = fgetc(fp)) != EOF) {
        usleep(100000);
        fputc(ch, stdout);
//      fflush(stdout);
    }   
    fclose(fp);
    return 0;
} 

위 코드에 주석된 fflush(stdout) 부분을 넣으면 원하는 대로 동작한다.

Leave a Reply

Your email address will not be published. Required fields are marked *