Computer Science/시스템 프로그래밍

시스템 프로그래밍 chapter 04.

만능 엔터테이너 2024. 10. 9. 17:42
728x90
반응형
SMALL

UNIX I/O(Input & Output)

목표

  • 장치에 독립적인 입출력의 기본 개념을 학습한다.
  • 읽기(read)와 쓰기(write)를 실험한다.
  • 여러 파일 디스크립터를 모니터링하는 방법을 탐구한다.
  • 올바른 오류 처리를 사용한다.
  • 파일 디스크립터의 상속을 이해한다.

{장치 용어}

- 주변 장치 : 컴퓨터 시스템에서 접근하는 하드웨어 장치(입출력 장치) ex) 디스크, 테이프, CD-ROM,화면,키보드,프린터,마우스 장치 및 네트워크 인터페이스
장치 드라이버 : 사용자 프로그램이 시스템 호출을 통해 운영체제 모듈에 접근하여 장치들에 대한 제어 및 입출력을 수행
장치 드라이버는 장치 작동의 세부 사항을 숨기고, 장치를 비인가 사용으로부터 보호함
- UNIX는 대부분의 장치에 5가지 함수를 통해 일관된 접근을 제공하여 장치 인터페이스를 단순화
(open, close, read, write and ioctl)
- 특수 파일 : 모든 장치는 특수 파일이라고 불리는 파일로 표현되고 이는 /dev(device의 약자) 디렉토리에 위치, 디스크 파일 및 다른 장치는 동일한 방식으로 이름이 지정 및 접근됨

UNIX 파일

- UNIX 파일m 바이트로 이루어진 연속된 시퀀스 ex) B0,B1...Bk...,Bm-1

- 모든 입출력 장치 및 커널파일로 표현

/dev/sda2 (하드 디스크 파티션), /dev/tty2 (터미널)   /   /dev/knem (커널 메모리 이미지), /proc (커널 데이터 구조)

UNIX I/O

- 파일을 우아하게 장치에 매핑하여 단순하고 저수준의 인터페이스인 UNIX I/O를 제공하고, 모든 입출력일관되고 통일된 방식으로 처리
- 기본적인 UNIX I/O 작업 (시스템 호출) - 파일 열기 및 닫기

  • open() : 애플리케이션이 I/O 장치에 접근할 의도를 알림
  • close() : 애플리케이션이 파일 접근을 마침

- 현재 파일 위치 변경 (seek)

  • 커널은 파일 위치 k를 유지하고 열린 파일에 대해 처음에는 0으로 설정됨, 파일 위치는 파일 시작점으로부터의 바이트 오프셋이고 애플리케이션은 seek 함수를 호출하여 파일 위치 k를 명시적으로 설정 가능함

- 파일 읽기 및 쓰기 - 연속적으로 작동, 중간에 임의의 지점에 설정하려면 처음에 위치를 중간으로 지정해야됨

  • read() : 파일에서 메모리로 n 바이트 읽음
  • write() : 메모리에서 파일로 n 바이트를 씀

Reading (읽기)

UNIX는 readwrite 함수를 통해 파일과 다른 장치에 순차적으로 접근 (3가지 파라미터 제공, 

#include <unistd.h>
ssize_t read(int filedes, void *buf, size_t nbyte);

- read 함수는 fildes로 나타내는 파일 또는 장치에 nbyte 데이터를 사용자 변수 buf로 읽어오려고 시도를 함
- 데이터를 담을 수 있을 만큼 buf 버퍼를 제공, 초기화되지 않은 포인터 buf를 제공하면 안됨
- ssize_t 데이터 타입 : 읽은 바이트 수에 사용되는 서명된 정수형 데이터 타입 / size_t : 읽을 바이트 수에 사용되는 부호 없는 정수형 데이터 타입

Read 함수

- 반환 값 : 읽기 성공 시 (1바이트라도 읽으면) 실제로 읽은 데이터의 크기(바이트 수)를 알려주고, 읽기가 실패하면 -1을 반환하고 왜 실패하였는지 오류 코드(errno)를 설정합니다.
- 특징 : 파일을 읽을 때 요청한 데이터 양보다 적게 읽을 수 있습니다. ex) 파일의 끝에 도달했을 때 요청한 양을 다 채우지 못할 수 있음, 파일을 계속 읽다가 파일의 끝(end-of-file)에 도달하면 더 이상 읽을 게 없다는 뜻으로 0을 반환

파일 디스크립터

- 열린 파일 및 장치를 나타내며 쉘에서 프로그램 실행 시, 3개의 열린 스트림을 시작
(STDIN_FILENO, STDOUT,FILENO, STDERR_FILENO)
- STDIN_FILENO (표준 입력)

  • 키보드 입력을 말하며 오래된 코드는 0으로 표현됨

-STDOUT_FILENO (표준 출력)

  • 화면 출력을 말하며 오래된 코드는 1로 표현됨

- STDERR_FILENO (표준 오류 장치)

  • 오류 출력을 처리하는 장치를 말하며 오래된 코드는 2로 표현되고 프로그램에서 이 스트림을 닫으면 안됨

Example (예시)

아래 코드 세그먼트는 표준 입력으로부터 최대 100바이트를 buf에 읽어옵니다.

char buf[100];  
ssize_t bytesread;  

bytesread = read(STDIN_FILENO, buf, 100);

 
다음 코드가 실행되면 어떻게 될까요?

char *buf;  
ssize_t bytesread;  

bytesread = read(STDIN_FILENO, buf, 100);

- buf에 대한 메모리 공간을 할당하지 않기에 read 함수의 결과가 예측 불가능 (메모리 접근 오류)

read 함수 내부에서 발생가능하고 read 함수가 적어도 1바이틀를 읽어 들었는데 newline을 만나지 않고 중간에 end-of-file을 읽은 경우

 
다음 코드 세그먼트는 readline 함수를 호출하여 표준 입력으로부터 최대 99바이트의 한 줄을 읽습니다.

int bytesread;  
char mybuf[100];  

bytesread = readline(STDIN_FILENO, mybuf, sizeof(mybuf));

read 함수는 3가지 파라미터를 필요로 함

데이터 복사 

  • 출발지: 현재 파일 오프셋 위치
  • 도착지: 메모리 영역 (프로그램 변수)
  • 크기: 지정된 길이
  • filedes: 파일을 열 때 사용한 파일 디스크립터
  • buffer: 데이터를 저장할 버퍼의 주소
  • n: 읽을 데이터의 크기 (바이트 수)

 


Writing (쓰기)

write 함수

#include <unistd.h>

ssize_t write(int filedes, const void *buf, size_t nbyte);

- write 함수파일에 데이터를 쓰는 역할을 하고 사용자가 제공하는 버퍼(buf) 내부에 있는 데이터를 fildes로 지정된 파일에 nbyte만큼 작성, 반환 값실제로 바이트가 얼만큼 쓰였는지 나타냄

  • filedes: 파일을 가리키는 번호 (파일 디스크립터)
  • buf: 파일에 쓸 데이터가 저장된 버퍼
  • nbyte: 쓰려고 하는 데이터의 크기 (바이트 단위)

=> 실제로 쓰인 바이트 수가 요청한 바이트 수보다 적을 수 있지만, 이것은 오류가 아닙니다.

  • r_read(), r_write()
    • 신호를 처리하면서 읽기와 쓰기가 필요한 프로그램을 크게 단순화시킵니다.
  • readwrite()
    • 하나의 파일 디스크립터에서 바이트를 읽고, 읽은 모든 바이트를 다른 파일 디스크립터로 씁니다 (r_read()와 r_write() 사용).
    • 버퍼 크기는 PIPE_BUF로 설정되며, 이는 PIPE_BUF 바이트 이하로 파이프에 쓰는 작업이 원자적이기 때문에 유용합니다.
  • copyfile()
    • readwrite() 함수를 사용하는 버전입니다.
데이터 복사
  • 출발지: 메모리 영역 (프로그램 변수)
  • 목적지: 현재 파일 오프셋 위치
  • 크기: 지정된 길이
  • filedes: 파일 디스크립터 (파일을 열 때 받은 값)
  • buffer: 데이터를 저장하고 있는 버퍼의 주소
  • n: 쓸 데이터의 크기 (바이트 수)

> 프로그램 메모리에 저장된 데이터를 파일에 쓰는 역할을 하며 fildes는 어디에 쓸지(파일)을 나타내고 buffer는 쓸 데이터가 있는 곳, n은 쓸 데이터의 크기를 지정함 / 데이터를 복사할 때, 프로그램 변수의 메모리 영역에서 파일의 현재 위치로 데이터를 옮김

파일 오프셋 (File Offset)

  • 현재 I/O 위치
    • VCR 헤드의 위치와 같음 (즉, 파일에서 읽거나 쓸 위치를 가리킴)
  • 읽기/쓰기 작업은 파일 오프셋이 가리키는 위치에서 수행됨
  • 읽기/쓰기 작업이 완료되면 파일 오프셋이 자동으로 이동됨 (다음 읽기/쓰기는 새로운 위치에서 시작됨)
파일을 오픈하면 파일 오프셋이 파일의 시작 지점을 가리킴, 마지막 read 함수의 오픈 값은 100이 되고 작업이 종료됨

발생할 수 있는 문제:

  1. write 함수의 가정: write 함수가 buf(버퍼)가 1024 바이트(BLKSIZE)로 가득 차 있다고 가정합니다.
  2. read 함수의 불완전성: 그러나 read 함수는 1024 바이트 전체를 읽지 못할 수도 있습니다. (예를 들어, 입력이 적을ㄸ때, read가 실패할 수도 있습니다.
  3. 결과: write는 제대로 채워지지 않은 buf를 그대로 출력하게 되며, 그 결과 쓰레기 데이터가 출력될 수 있습니다.

해결 방법: read 함수가 반환하는 실제 읽은 바이트 수를 확인한 후, 그만큼만 write출력하는 것이 안전합니다.

위의 코드를 개선된 예시

 

이 코드에서는 read가 성공적으로 데이터를 읽어들인 후, write로 출력하는 구조입니다. 하지만 다음과 같은 문제가 있을 수 있습니다:

  1. write가 요청한 바이트 모두를 쓰지 않을 수 있음: write 함수가 읽은 바이트 수만큼 출력하려 하지만, 모든 바이트를 출력하지 못할 가능성이 있습니다.
  2. 신호에 의해 중단될 수 있음: readwrite 함수가 중간에 신호를 받아 실행이 중단되면, -1을 반환하며 EINTR 오류를 설정합니다. 이 경우 코드를 다시 시도하는 처리가 필요할 수 있습니다.

따라서, write가 모든 바이트를 출력했는지 확인하고, 신호 중단 시 복구할 수 있도록 처리하는 것이 중요합니다.

 

  • 파일 복사: 하나의 파일(fromfd)에서 데이터를 읽어 다른 파일(tofd)로 복사합니다.
  • 중단 처리: readwrite 작업이 신호로 중단되면, 이를 다시 시도하여 안전하게 복사를 계속합니다.
  • 포인터 사용: write 함수에서 buf 대신 bp라는 포인터를 사용하여 데이터를 쓰는 위치를 지정합니다. bpbuf의 특정 위치를 가리키는 포인터입니다.

Restart read/write after a signal (신호 이후에 읽기 및 쓰기를 재시작)

  • r_read(), r_write() : 신호를 처리하면서 읽기와 쓰기가 필요한 프로그램을 크게 단순화시킵니다.
  • readwrite() : 하나의 파일 디스크립터에서 바이트를 읽고, 읽은 모든 바이트를 다른 파일 디스크립터로 씁니다. (r_read()와 r_write() 사용), 버퍼 크기는 PIPE_BUF로 설정되며, 이는 PIPE_BUF 바이트 이하로 파이프에 쓰는 작업이 원자적이기 때문에 유용합니다.
  • copyfile() : readwrite() 함수를 사용하는 버전입니다.

readblock()

readblock() 함수r_read()에서 발생할 수 있는 문제를 해결하기 위해 만들어졌습니다. r_read()는 신호에 의해 중단되면 다시 시도하지만, 경우에 따라 요청한 바이트보다 적은 양의 데이터를 읽을 수 있습니다.

readblock() 함수는 이런 문제를 해결하기 위해:

  1. 요청한 바이트 수를 모두 읽을 때까지 계속해서 데이터를 읽습니다.
  2. 오류가 발생하거나 파일 끝에 도달할 때까지 멈추지 않습니다.

반환 값:

  • 0: 파일 끝에 도달하여 더 이상 읽을 데이터가 없을 때
  • 요청한 바이트 수: 모든 데이터를 성공적으로 읽었을 때
  • -1: 오류가 발생했을 때


 Opening

 

open 함수

#include <fcntl.h>
#include <sys/stat.h>

int open(const char *path, int flag); // 기존 파일을 open 할 때 필요
int open(const char *path, int flag, mode_t mode); // 새로운 파일을 open 할 때 필요

- open 함수는 파일을 열거나 새로 생성할 때 사용하는 함수
- 반환값 : 성공하면 파일 디스크립터(양수 값)을 반환하고 실패하면 -1을 반환하여 오류가 발생하였음을 나타냅니다.

  • path: 파일 또는 장치의 경로명을 가리킵니다.
  • flag: 열려는 파일에 대한 상태 플래그와 접근 모드를 지정합니다.
  • 접근 권한 (Access permission): 파일을 생성하는 경우, 접근 권한을 지정하는 세 번째 매개변수를 포함해야 합니다.

Flag

flag 인자접근 모드(access mode)추가 플래그(additional flags)의 원하는 조합을 비트 OR 연산을 통해 생성합니다
 

  • 접근 모드 (access mode):
    • 반드시 하나의 접근 모드를 지정해야 합니다.
    • O_RDONLY: 읽기 전용
    • O_WRONLY: 쓰기 전용
    • O_RDWR: 읽기 및 쓰기
  • 추가 플래그 (Additional flags):
    • O_APPEND: 쓰기 전에 파일의 끝으로 파일 오프셋이 이동합니다.
    • O_TRUNC: 쓰기 위해 열린 일반 파일의 길이를 0으로 잘라냅니다.
    • O_CREAT: 파일이 존재하지 않으면 생성합니다. 세 번째 인자를 사용하여 권한을 지정해야 합니다.
    • O_EXCL: O_CREAT와 함께 사용되며, 파일이 이미 존재하면 오류를 반환합니다.
    • O_NOCTTY: 열린 장치가 제어 터미널이 되는 것을 방지합니다.

질문

  • 새로운 파일을 생성할 때, 기존 파일을 덮어쓰지 않으려면 어떤 플래그를 사용해야 할까요?
  • O_CREAT | O_EXCL 조합을 사용, 이 조합은 파일이 이미 존재할 경우 오류를 반환합니다.

파일 오프셋이 파일의 끝으로 먼저 이동하고 파일을 덮붙이는 경우(아래)

 

Permission Mask (권한 마스크)

- 각 파일에는 3가지 클래스 (사용자 or 소유자, 그룹, 다른 모든 사용자)가 연결되어 있음
- 가능한 권한 및 특권읽기(r), 쓰기(w), 실행(x) 이렇게 3가지가 존재, 이 권한은 사용자, 그룹, 기타 사용자에게 각각 따로 지정되야 합니다.
- 파일을 O_CREAT 플래그로 열 때, 세 번째 인자mode_t 유형의 마스크를 사용하여 파일 권한을 지정해야 합니다.
- 파일 권한 비트 마스크로 배열되었으며, 특정 비트 위치에 1을 설정하여 권한을 나타냅니다. 이 비트가 1이면 해당 클래스 해당 권한을 가지고 있음을 의미합니다.

  • 권한 마스크의 역사적 배열은 다음과 같습니다: user(사용자), group(그룹), others(기타 사용자) 순서로 권한이 나열되어 있으며, 각각 읽기(r), 쓰기(w), 실행(x) 권한을 가질 수 있습니다.

POSIX symbolic names (POSIX 심볼릭 이름)

  • POSIX는 파일 권한 비트에 대응하는 마스크에 대한 심볼릭 이름을 정의, 이러한 이름은 sys/stat.h에 정의되어 있습니다.

int fd; // 파일 디스크립터를 저장할 변수
mode_t fdmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 
// 권한 설정: 소유자는 읽기/쓰기 가능, 그룹 및 기타는 읽기만 가능

// info.dat 파일을 읽기/쓰기 모드로 열거나, 파일이 없으면 생성함.
if ((fd = open("info.dat", O_RDWR | O_CREAT, fdmode)) == -1)
    perror("failed to open info.dat"); // 파일 열기에 실패하면 오류 메시지 출력

 

  • info.dat라는 이름의 파일을 현재 디렉터리에 만듭니다.
  • 만약 info.dat 파일이 이미 존재한다면, 기존 파일을 덮어씁니다.
  • 새 파일에 대한 권한 설정:
    • 파일 소유자는 읽기쓰기가 가능.
    • 그룹과 다른 사람들은 읽기만 가능.


Closing

cp 명령어

- cp 명령어를사용하여 파일을 복사할 수 있음 위의 copyfilemain.c는 open은 있지만 close함수를 하지 않고  main 함수에서 반환되면 메모리를 불필요하게 사용하여 메모리를 낭비함으로 open하고 I/O 작업을 하게 되면 반드시 close 함수로 닫아주어야 함

 

close() 함수

#include <unistd.h>
int close(int filedes);

 

  • 커널에 파일이 더 이상 사용되지 않음을 알려 커널이 해당 파일에 할당된 리소스를 해제할 수 있음

r_close() 함수

  • close() 함수와 유사하지만, 신호에 의해 중단될 경우 다시 시작됩니다.
#include <errno.h>
#include <unistd.h>

int r_close(int fd) {
    int retval;

    while ((retval = close(fd)), retval == -1 && errno == EINTR);
    return retval;
}

 

 

  • 파일 닫기 작업 중 EINTR(Interrupted System Call) 오류가 발생하면,
    • close()가 중단되지 않고 재시도하도록 구현된 함수, 정상적으로 닫히면 반환값을 리턴합니다.

lseek

  • 파일 오프셋(File Offset)을 명시적으로 지정된 위치로 이동합니다.
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int filedes, off_t offset, int start_flag);

 

 

매개변수 설명:

  • filedes: 파일 디스크립터
  • offset: 이동할 거리 (정수형 숫자 타입) / 파일 위치의 이동방향 ex) 10이면 오른쪽으로 10 바이트 이동
    • 음수 값: 왼쪽으로 이동
    • 양수 값: 오른쪽으로 이동
  • start_flag: 기준점 설정
    • SEEK_SET: 파일 시작 위치 기준
    • SEEK_CUR: 현재 파일 오프셋 기준
    • SEEK_END: 파일 끝 위치 기준


File representation

File descriptors vs. pointers (파일 디스크립터와 포인터의 비교)

  • C 프로그램에서 파일은 파일 포인터 또는 파일 디스크립터를 통해 지정됩니다.

File pointers (파일 포인터)

사용 예: fopen, fscanf, fprintf, fread, fwrite, fclose 등

- 이들은 stdio.h 헤더에 정의되어 있습니다. stdin, stdout, stderr [표준 입출력 및 에러] 로 정의

 

File descriptors (파일 디스크립터)  (중점적으로 다룸!)

사용 예: open, read, write, close, ioctl 등

- 이들은 unistd.h 헤더에 정의되어 있습니다. STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO [표준 입출력 및 에러] 로 정의

왼쪽 - User 프로세스 영역, 오른쪽 - OS 커널 영역, STD_IN (0), STD_OUT(1), STD_ERR(2) 파일의 인덱스가 파일 디스크립터 값
  • 파일 디스크립터 테이블 (User Program Area)
    • 사용자 프로그램이 파일을 열면 파일 디스크립터 번호가 할당됨, 프로세스마다 가짐
    • 예: myfd = 3은 테이블의 3번 슬롯에 저장됩니다.
  • 시스템 파일 테이블 (Kernel Area) , (커널에 있는 테이블, 여러 프로세스가 공유하며 사용하는 테이블)
    • 파일 디스크립터가 시스템 파일 테이블의 항목을 가리킵니다.
    • 파일에 대한 상태 정보(열림 상태, 읽기/쓰기 모드 등)가 저장됩니다.
  • 메모리 상의 inode 테이블 (In-memory inode Table) (커널에 있는 테이블, 여러 프로세스가 공유하며 사용하는 테이블)
    • 시스템 파일 테이블의 항목은 해당 파일의 inode 정보와 연결됩니다.
    • inode에는 파일 경로, 크기, 권한 등 메타데이터와 실제 위치 정보가 포함됩니다.

=> 파일을 open 하면 3개의 테이블에 entry가 추가되면서 실행

File descriptors (파일 디스크립터)

  • 파일 디스크립터는 프로세스마다 고유한 파일 디스크립터 테이블의 인덱스

The system file table (시스템 파일 테이블)

  • 시스템의 모든 프로세스가 공유
  • 열린 파일마다 하나의 항목(entry)이 생성됩니다.
  • 각 항목에는 다음 정보가 포함됩니다:
    • 파일 오프셋(offset)
    • 접근 모드(access mode)
    • 파일 디스크립터 테이블 항목의 개수 (파일을 참조 중인 프로세스 수) / 여러 시스템 파일 테이블 항목같은 물리적 파일을 가리킬 수 있습니다.

In-memory inode table (메모리 상의 inode 테이블)

  • 시스템 파일 테이블 항목이 inode 테이블 항목을 참조합니다.
  • 시스템에 열린 모든 파일의 정보가 inode 테이블에 저장됩니다.

 Question.

1. 이전 예제에서 프로세스가 close(myfd) 함수를 실행하면?

  • OS가 파일 디스크립터 테이블의 네 번째 항목과 해당되는 시스템 파일 테이블의 항목을 삭제합니다.
  • OS는 inode 항목의 참조 수를 감소시킵니다.
  • 만약 inode의 참조 수가 0이 되면, OS는 메모리에서 inode 항목을 삭제합니다.

2. 두 개의 프로세스가 동일한 파일을 쓰기 모드로 열면?

- 각 프로세스는 별도의 파일 오프셋을 가지므로, 한 사용자가 작성한 내용을 다른 사용자가 덮어쓸 수 있습니다.

 

3. 파일 오프셋이 inode 테이블에 저장되면 어떻게 될까?

  • 모든 프로세스가 동일한 파일 오프셋을 공유하게 됩니다. 이 경우, 여러 프로세스의 쓰기 작업이 순차적으로 이루어집니다.

File pointers (파일 포인터)

  • 파일 포인터프로세스의 사용자 영역에 있는 FILE 구조체를 가리킵니다.

FILE 구조체

버퍼(buffer)와 파일 디스크립터 값을 포함합니다.

 

  • 왼쪽 코드: 두 프로세스는 파일 디스크립터와 파일 오프셋을 공유하며, 순차적으로 파일을 읽습니다.
  • 동작 설명:
    1. 파일 디스크립터(myfd)가 부모 프로세스에서 먼저 열립니다.
    2. fork()로 프로세스가 복제되면서 부모와 자식 프로세스는 같은 파일 디스크립터를 공유합니다.
    3. 공유된 파일 디스크립터를 통해 파일의 파일 오프셋(file offset)도 공유됩니다.
    4. 두 프로세스가 순차적으로 파일을 읽어 각각 다른 문자(a, b)를 가져옵니다.
    출력:
    • Process nnn got a
    • Process mmm got b
  • 오른쪽 코드: 각 프로세스가 별도의 파일 디스크립터와 오프셋을 사용하여, 동일한 데이터를 읽습니다.
  • 동작 설명:
    1. fork()로 부모와 자식 프로세스가 복제됩니다.
    2. 각 프로세스가 독립적으로 파일을 열고(open) 자신의 파일 디스크립터를 생성합니다.
    3. 따라서 두 프로세스는 서로 다른 파일 오프셋을 사용하여 같은 위치에서 읽습니다.
    출력:
    • Process nnn got a
    • Process mmm got a

 

  • 왼쪽 코드:
    • 버퍼링이 발생하여 fork() 이후 두 프로세스가 동일한 출력을 두 번 수행합니다.
    • 결과: "This is my output.This is my output."
  • 오른쪽 코드:
    • 줄바꿈(\n)으로 인해 버퍼가 플러시된 후 fork()가 호출되므로 한 번만 출력됩니다.
    • 결과: "This is my output."

이 예제는 버퍼링과 프로세스 복제가 출력에 어떤 영향을 미치는지 설명합니다. 줄바꿈(\n)의 유무가 버퍼링에 영향을 주며, fork() 전에 버퍼가 비워지지 않으면 중복 출력이 발생할 수 있습니다.


Filters and Redirection

필터(Filters)

  • 정의: 입력(표준 입력) 데이터를 읽어서 처리한 후, 결과를 출력(표준 출력)하는 프로그램.
  • 예시 명령어:
    • head: 파일의 처음 몇 줄을 출력
    • tail: 파일의 마지막 몇 줄을 출력
    • sort: 데이터를 정렬
    • grep: 특정 단어를 검색
    • awk: 데이터 처리 및 변환
  • cat 명령어: 입력 파일 없이 사용하면 필터처럼 동작해서 입력을 받아 바로 출력합니다.

예시:

cat | grep "hello"
  • 사용자가 입력한 내용 중 "hello"가 포함된 줄만 출력.

리다이렉션(Redirection)

  • 정의: 명령어의 입출력 방향을 변경하는 기능.
  • 종류:
    • >: 출력 리다이렉션 (출력을 파일로 저장)
      • 예: ls > output.txt
        → ls의 결과가 output.txt 파일에 저장됨.
    • <: 입력 리다이렉션 (파일에서 입력을 읽음)
      • 예: cat < input.txt
        → input.txt의 내용을 cat이 출력함.

 

  • 필터: 데이터를 가공해서 출력하는 프로그램 (예: grep, sort).
  • 리다이렉션: 명령어의 출력을 파일에 저장하거나 입력을 파일에서 읽음 (>, < 사용)

 

dup2() 함수란?

  • 기능: 기존 파일 디스크립터(oldfd)를 새 디스크립터(newfd)에 복사합니다.
  • 동작:
    1. newfd가 열려 있으면 닫음.
    2. oldfd의 파일을 newfd로 연결.

예시 흐름:

  1. 파일 오픈 후:
    • [1]: 표준 출력
    • [3]: my.file에 쓰기
  2. dup2(3, 1) 호출 후:
    • [1]: my.file로 리다이렉션 (표준 출력이 파일로 연결됨)
  3. 닫은 후:
    • [1]: 여전히 my.file로 쓰기 가능

결과:

  • 표준 출력이 **my.file**에 기록됩니다.
  • 프로그램에서 printf()로 출력하면 화면 대신 파일에 저장됩니다.

 

728x90
반응형
LIST