[Minishell] 과제 Exteranl functs 함수 정리

bolee·2022년 9월 15일
0

42seoul

목록 보기
26/27

42Seoul의 Minishell 과제에서 허용되는 External functions를 정리한 포스트이다.

GNU readline

GNU readline 은 CLI에서 줄 편집 및 입력 기록 저장 등의 역할을 하는 라이브러리이다.

GNU 프로젝트에 속해 있으며 입력 자동 완성, 커서 이동, 잘라내기, 복사, 붙여넣기 등의 기능을 지원하며, Bash 등의 CLI 기분 인터랙티브 소프트웨어에서 사용된다.

-lreadline 컴파일러 플래그를 사용해 컴파일해야한다.

readline

#include <stdio.h>
#include <readline/readline.h>

char	*readline (const char *prompt);

prompt를 출력하고 프롬프트를 열어 표준 입력으로 문자열을 입력받으며, 개행(newline, /n)을 받으면 지금까지 입력된 문자열을 리턴한다.
문자열을 입력받기 전까지는 다음 코드로 진행되지 않는다.
rl_replace_line(), rl_redisplay()등으로 프롬프트가 비워져도 계속 문자열을 받는 상태가 된다.

  • prompt: 출력할 문자열

반환값

  • 읽은 줄의 텍스트를 반환하며, 빈 줄은 빈 문자열을 반환한다.
  • 행을 읽는 동안 EOF가 발생하고 행이 비어 있으면 NULL이 리턴된다.
  • EOF가 비어 있지 않은 행으로 읽혀지면 개행으로 처리된다.

rl_on_new_line

#include <stdio.h>
#include <readline/readline.h>

int	rl_on_new_line(void);

일반적으로 개행을 출력한 후 새(빈) 줄로 이동했음을 업데이트 함수에 알리는 함수이다.
rl_redisplay()를 실행하기 위해 필요한 함수이다.

반환값

  • 성공: 0
  • 실패: -1

rl_replace_line

#include <stdio.h>
#include <readline/readline.h>

void rl_replace_line (const char *text, int clear_undo);

rl_line_buffer의 내용을 text로 바꾼다.
즉, 현재까지 입력된 프롬프트의 문자열을 text로 바꾸어주는 함수이다.
가능한 경우 포인트와 마크가 유지된다.

clear_undo는 내부적으로 유지중인 undo_list를 초기화할 지의 여부를 결정한다.
clear_undo가 0이 아니면 현재 행과 관련된 실행 취소 목록이 지워진다.

ctrl + C처럼 프롬프트를 입력하지는 않고 새로운 프롬프트를 출력해야 할 때 rl_replace_line(””, 1);처리를 해준다면 새로운 프롬프트를 비워줄 수 있게 된다.

char *rl_line_buffer
지금까지 모은 라인이다. rl_extend_line_buffer 함수는 rl_line_buffer에 할당된 메모리를 늘리는 데 사용할 수 있다.

rl_redisplay

#include <stdio.h>
#include <readline/readline.h>

void rl_redisplay (void)

rl_line_buffer의 현재 내용을 반영하도록 화면에 표시되는 내용을 변경한다.
rl_replace_line()을 하지 않으면 작동하지 않으며, readline() 함수의 인자로 넣은 문자열을 다시 출력한다.

char *rl_line_buffer
지금까지 모은 라인이다. rl_extend_line_buffer 함수는 rl_line_buffer에 할당된 메모리를 늘리는 데 사용할 수 있다.

add_history

#include <stdio.h>
#include <readline/history.h>

void add_history (const char *string)

history 목록의 끝에 문자열을 배치한다. 연결된 데이터 필드(있는 경우)는 NULL로 설정된다.

즉, 인자에 넣은 문자열을 history에 저장하며, 프롬프트가 열린 상태에서 키보드 방향키 위, 아래를 통해 이제껏 프롬프트에 입력한 문자열을 불러올 수 있다.
스택(stack)처럼 가장 마지막에 넣은 문자열부터 불러온다.

Example

#include <readline/readline.h>
#include <readline/history.h>
#include <stdio.h>
#include <stdlib.h>

int	main(void)
{
	char	*str;

	while (1)
	{
		// 무한루프로 readline 함수 호출, 라인을 계속해서 입력받는다.
		str = readline("prompt : ");
		if (!str)
			break ;
		printf("str = %s\n", str);
		// history에 문자열을 저장
		add_history(str);
		// 할당된 메모리 해제
		free(str);
	}
	return (0);
}

Simple example readline library

#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <stdlib.h>

int	main(void)
{
	char	*str;

	while (1)
	{
		str = readline("prompt : ");
		if (!str)
			break ;
		rl_on_new_line();
		rl_replace_line("replace line\n", 1);
		rl_redisplay();
		printf("\n");
		add_history(str);
		free(str);
	}
	system("leaks a.out; rm -rf a.out");
	return (0);
}

printf

#include <stdio.h>

int printf(const char *format, ...);

여러 종류의 데이터를 다양한 서식에 맞춰 출력하는 함수

  • format: 출력할 데이터이다. 서식 문자를 통해 여러 데이터를 출력할 수 있다.
  • ...: 가변인자로, format 내 서식을 대체하여 출력될 데이터들이 들어온다.

반환값

  • 성공: 출력한 byte 수
  • 실패: -1 반환 및 적당한 errno 값 설정

malloc, free

malloc

#include <stdlib.h>

void * malloc(size_t size);

원하는 만큼 동적으로 메모리를 할당하여 주소값을 반환하는 함수이며, 동적으로 메모리를 할당할때 Heap(힙)영역에 할당 한다.

  • size: 할당을 원하는 메모리 크기(byte 단위)

반환값

  • 성공: 할당한 메모리의 첫번째 주소
  • 실패: NULL

free

#include <stdlib.h>

void free(void *ptr);

동적으로 할당된 메모리를 해제한다.

반환값

  • 없음

open, read, write, close

open

#include <fcntl.h>

int		open(const char *path, int oflag, ...);

파일을 사용하기 위해 여는(open) 함수

  • path: 대상 파일 이름(경로 포함)
  • oflag: 파일에 대한 열기 옵션

반환값

  • 성공: 파일 열기에 성공하면 파일 디스크립터의 양의 정수 값 반환
  • 실패: -1 반환 및 적당한 errno 값 설정

파일을 open()할 때 용도에 따라 oflag에 옵션을 지정하여 연다.
아래의 옵션은 모두 <fcntl.h>에 정의되어 있다.

열기 옵션설명
O_RDONLY읽기 전용으로 열기
O_WRONLY쓰기 전용으로 열기
O_RDWR읽기와 쓰기 모두 가능

추가적인 옵션도 존재한다.

기타 옵션설명
O_CREAT해당 파일이 없으면 생성한다.
(주의) O_CREATE 가 아니라 끝에 E가 없는 O_CREAT 다.
O_CREAT로 파일을 생성하게 된다면 파일의 접근권한을 지정하기 위해 접근 권한 값을 추가해야 한다.
ex: open( "badayak.txt", O_WRONLY \| OCREAT, 0644);
O_EXCLO_CREAT를 사용했을 때, 파일이 이미 있어도 열기가 가능하여 쓰기를 하면 이전 내용이 사라진다.
O_CREAT를 사용할 때, O_EXCL를 함께 사용하면, 이미 파일이 있을 때에는 open() 되지 않아 이전 파일을 보존할 수 있다.
ex: fd = open( "./test.txt", O_WRONLY \| O_CREAT \| O_EXCL, 0644)
즉, 이렇게 O_EXCL을 함께 사용하면, 파일이 없을 경우에는 O_CREAT 영향을 받아 쓰기 전용의 파일이 생성하고, 파일이 이미 존재하면 open()이 실패된다.
O_TRUNC기존의 파일 내용을 모두 삭제한다.
O_APPEND파일을 추가하여 쓰기가 되도록 open 후에 쓰기 포인터가 파일의 끝에 위치하게 된다.
O_NOCITTY열기 대상이 터미널일 경우, 이 터미널이 프로그램의 제어 터미널로 할당하지 않는다.
O_NONBLOCK읽을 내용이 없을 때에는 읽을 내용이 있을 때까지 기다리지 않고 바로 복귀한다.
O_SYNC쓰기를 할 때, 실제 쓰기가 완료될 때 까지 기다린다.
즉, 물리적으로 쓰기가 완료되어야 복귀하게 된다.

이 외에도 더 다양한 옵션들이 존재한다.

read

#include <unistd.h>

ssize_t	read(int fildes, const void *buf, size_t, nbyte);

파일에 데이터를 읽는 함수

  • fildes: 데이터 전송 영역을 나타내는 파일 디스크립터
  • buf: 읽어드릴 데이터를 받아들일 버퍼의 포인터
  • nbyte: 읽을 데이터의 바이트 수

반환값

  • 성공: 읽어들인 바이트 수
  • 실패: -1 반환 및 적당한 errno 값 설정

write

#include <unistd.h>

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

파일에 데이터를 출력하는 함수

  • fildes: 데이터 전송 영역을 나타내는 파일 디스크립터
  • buf: 전송할 데이터를 가지고 있는 버퍼의 포인터
  • nbyte: 전송할 데이터의 바이트 수

반환값

  • 성공: 전달한 바이트 수
  • 실패: -1 반환 및 적당한 errno 값 설정

close

#include <unistd.h>

int		close(int fildes);

열린 파일을 닫는 함수
close()를 호출하면 fd 를 해제하며, 파일에서 프로세스를 분리한다.
프로세스가 종료되면 커널이 프로세스의 열린 파일들을 모두 자동으로 닫기 때문에 명시적으로 close() 함수를 호출하지 않는 프로그램들도 많다.

  • fildes: 데이터 전송 영역을 나타내는 파일 디스크립터

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 값 설정

fork

#include <unistd.h>

pid_t fork(void);

현재 실행되는 프로세스에 대한 복사본을 만들어 자식 프로세스를 생성하는 함수이다.

반환값

  • 성공:
    • 부모 프로세스: 자식 프로세스의 pid 리턴(0보다 큰 값)
    • 자식 프로세스: 0 리턴
  • 실패:
    • 부모 프로세스: -1 리턴
    • 자식 프로세스는 생성되지 않는다.

wait

#include <sys/wait.h>

pid_t wait(int *stat_loc);

현재 프로세스가 fork()한 자식 프로세스(child proccess)의 상태 변화를 monitoring한다. 주로 자식 프로세스가 종료되었을 때에 많이 사용하고, child process의 종료 원인이 무엇인지를 확인한다.

만약 부모 프로세스에서 SIGCHLD 시그널에 대해서 signal() 또는 sigaction()으로 SIG_IGN으로 설정하지 않았다면, wait() / waitpid() 등의 함수로 자식 프로세스 종료를 기다려야한다. 그렇지 않으면, 종료된 자식 프로세스는 메모리에서 사라지지않고 좀비("zombie" 또는 "defunct")상태가 된다.

좀비 상태에서는 kill 등으로 절대로 종료시킬 수 없으며, 오로지 부모 프로세스가 종료해야만 사라지게 된다. 따라서 SIGCHLD signal에 대해서 SIG_IGN으로 설정하거나 signal handler 함수에 wait() / waitpid()함수로 자식 프로세스 상태를 읽어주어야 종료된다. 또는 주기적으로 waitpid()를 호출하여 wait상태인 자식 프로세스의 memory를 free 시켜주어야 한다.

좀비 프로세스(zombie process)
자식 프로세스가 정상 또는 비정상적으로 종료했는 데, 부모 프로세스에게 종료상태(정상종료면 main함수의 return값 또는 exit함수의 파라미터값, 비정상종료이면 signal 번호)를 전달하기 위해서 최소한의 resource로 유지하고 있는 프로세스를 말한다.
즉, 자식 프로세스가 완전히 사라지면 부모 프로세스에게 실행 결과를 전달을 할 수 없으니, 아무런 기능도 하지 않고 오로지 부모 프로세스에게 종료 메시지를 전달하기 위해서 떠 있 는 프로세스이다.
만약, 부모 프로세스가 종료가 되면 좀비 프로세스의 부모 프로세스가 init 프로세스인 1번으로 바뀌고 init 프로세스가 wait 또는 SIGCHLD에 대해 SIG_IGN으로 처리해버리기 때문에 좀비 프로세스는 사라진다.

  • stat_loc
    • return된 자식 프로세스의 상태 변경값을 저장하는 변수 (ouput only)
    • 이 상태값의 의미를 해석하는 여러가지 macro함수를 제공한다.
    • staus의 상위 바이트는 자식 프로세스의 상태 및 종료 원인 그리고 하위 바이트는 상세 값을 저장되어 있다.
    • WIF로 시작하는 매크로(macro)는 상위 바이트의 상태를 판단하는 함수이며 나머지 매크로 함수는 상세 값을 읽을 수 있다.

반환값

  • 성공: 자식 프로세스의 pid
  • 실패: -1

stat_loc

stat_loc 관련 매크로(macro)들

macrosdescription
WIFEXITED(statloc)child process가 정상 종료 시 true 반환
WEXITSTATUS(statloc)WIFEXITEDtrue를 반환한 경우에만 사용 가능, 하위 8비트를 참조하여 child process가 exit, _exit, _Exit에 넘겨준 인자 값을 받음
WIFSIGNALED(statloc)signal에 의해 종료된 경우 true를 반환
WTERMSIG(statloc)child process를 종료시킨 signal의 숫자를 반환하며 WIFSIGNALEDtrue를 반환했을 경우 사용
ECHILD호출자의 child process가 없는 경우를 나타냄
EINTR시스템 콜이 인터럽트 되었을 때 사용

wait3

#include <sys/wait.h>

pid_t wait3(int *statloc, int options, struct rusage *rusage);

자식 프로세스가 종료되는 것을 기다리며, 종료된 프로세스의 상태와 자원 사용량을 알려주는 함수.

wait4

pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage);

waitpid

#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *stat_loc, int options);

매개변수로 주어진 pid 번호의 자식 프로세스가 종료되거나, 시그널(signal) 함수를 호출하는 신호가 전달될 때까지 waitpid() 함수를 호출한 영역에서 일시 중지된다.

  • pid: 감시 대상인 자식 프로세스 ID
  • stat_loc: 자식 프로세스의 종료 상태 정보
    • 정상 종료 시: stat_loc의 하위 8비트에는 0이 저장되며, 상위 8비트에는 프로세스가 종료되게 한 exit함수의 인자가 기록된다.
    • 비정상 종료 시: stat_loc의 하위 8비트에는 프로세스를 종료시킨 시그널 번호가 저장되며, 상위 8비트에는 0이 저장된다.
  • options: waitpid() 옵션 정보
    • WCONTINUED: 중단 되었다가 재개된 자식 프로세스의 상태를 받는다.
    • WNOHANG: 자식 프로세스가 종료되었는지 실행 중인지 확인하고 바로 복귀한다. 즉, 부모 프로세스는 block되지 않은다.
    • WUNTRACED: 중단된 자식 프로세스의 상태를 받는다.
    • 0: 자식 프로세스가 종료될 때까지 block

pid 값에 따라 다음과 같이 처리된다.

|pid|의미|
|양수|프로세스 ID가 pid인 자식 프로세스를 기다림|
|0|프로세스 그룹 ID가 호출한 프로세스의 ID와 같은 자식 프로세스를 기다린다.|
|-1|임의의 자식 프로세스를 기다림|
|-1 이외의 음수|프로세스 그룹 ID가 pid의 절대값과 같은 자식 프로세스를 기다림|

반환값

  • 성공: 자식 프로세스 pid
  • 실패: -1 반환 및 적당한 errno 설정
  • 0: WNOHANG을 사용하였고, 자식 프로세스가 종료되지 않았다면 0을 리턴

stat_loc

stat_loc 관련 매크로(macro)들

macrosdescription
WIFEXITED(statloc)child process가 정상 종료 시 true 반환
WEXITSTATUS(statloc)WIFEXITEDtrue를 반환한 경우에만 사용 가능, 하위 8비트를 참조하여 child process가 exit, _exit, _Exit에 넘겨준 인자 값을 받음
WIFSIGNALED(statloc)signal에 의해 종료된 경우 true를 반환
WTERMSIG(statloc)child process를 종료시킨 signal의 숫자를 반환하며 WIFSIGNALEDtrue를 반환했을 경우 사용
ECHILD호출자의 child process가 없는 경우를 나타냄
EINTR시스템 콜이 인터럽트 되었을 때 사용

options

options 관련 매크로들(marcos)

macrosdescription
WCONTINUED중단되었다가 재개된 child process의 상태를 받는다
WNOHANG기다리는 pid가 종료되지 않아 즉시 종료 상태를 회수할 수 없는 경우 호출자는 반환값으로 0을 받으며, 종료되지 않음
WUNTRACED중단된 자식 프로세스의 상태를 받는다

signal

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

시그널(signal)을 받아 해당 함수를 실행시키는 함수

  • sig: 시그널 번호
  • func: 시그널을 처리할 핸들러 함수
    • 핸들러 함수 프로토타입: void handler(int signo);

반환값

  • 성공: 이전의 시그널 핸들러의 포인터
  • 실패: SIG_ERR 리턴

Singal이란?

  • Software interrupt로, process에 무엇인가 발생했음을 알리는 간단한 메시지를 비동기적으로 보내는 것
  • Signal을 받은 프로세스는 Signal에 따른 미리 지정된 기본 동작(default action)을 수행할 수도 있고, 사용자가 미리 정의해 놓은 함수에 의해서 무시하거나, 특별한 처리를 할 수 있다.

시그널은 <signal.h> 헤더 파일에 정의되어있고, 시그널에 따른 기본 처리는 프로세스를 종료하거나, 코어 덤프를 생성한다.

NoNameDefault ActionDescription
1SIGHUPterminate processterminal line hangup
2SIGINTterminate processinterrupt program
3SIGQUITcreate core imagequit program
4SIGILLcreate core imageillegal instruction
5SIGTRAPcreate core imagetrace trap
6SIGABRTcreate core imageabort program (formerly SIGIOT)
7SIGEMTcreate core imageemulate instruction executed
8SIGFPEcreate core imagefloating-point exception
9SIGKILLterminate processkill program
10SIGBUScreate core imagebus error
11SIGSEGVcreate core imagesegmentation violation
12SIGSYScreate core imagenon-existent system call invoked
13SIGPIPEterminate processwrite on a pipe with no reader
14SIGALRMterminate processreal-time timer expired
15SIGTERMterminate processsoftware termination signal
16SIGURGdiscard signalurgent condition present on socket
17SIGSTOPstop processstop (cannot be caught or ignored)
18SIGTSTPstop processstop signal generated from keyboard
19SIGCONTdiscard signalcontinue after stop
20SIGCHLDdiscard signalchild status has changed
21SIGTTINstop processbackground read attempted from control terminal
22SIGTTOUstop processbackground write attempted to control terminal
23SIGIOdiscard signalI/O is possible on a descriptor (see fcntl(2))
24SIGXCPUterminate processcpu time limit exceeded (see setrlimit(2))
25SIGXFSZterminate processfile size limit exceeded (see setrlimit(2))
26SIGVTALRMterminate processvirtual time alarm (see setitimer(2))
27SIGPROFterminate processprofiling timer alarm (see setitimer(2))
28SIGWINCHdiscard signalWindow size change
29SIGINFOdiscard signalstatus request from keyboard
30SIGUSR1terminate processUser defined signal 1
31SIGUSR2terminate processUser defined signal 2

kill

#include <signal.h>

int kill(pid_t pid, int sig);

kill() 함수는 쉘에서 프로세스를 죽이는 kill 명령과는 달리 프로세스에 시그널을 전송한다. 물론, 프로세스에 SIGKILL을 보내면 쉘 명령의 kill과 같은 역할을 할 수도 있다.

특정 프로세스 뿐만 아니라 같은 그룹 ID가 같은 모든 프로세스에게 동시에 시그널을 전송할 수 있으며, 권한 안에 있는 모든 프로세스에게도 시그널을 전송할 수 있다.

  • pid: kill 대상 프로세스 ID
  • sig: 시그널 번호

pid 값에 따라 다음과 같이 처리된다.

pid의미
양수지정한 프로세스 ID에만 시그널을 전송
0함수를 호출하는 프로세스와 같은 그룹에 있는 모든 프로세스에 시그널을 전송
-1함수를 호출하는 프로세스가 전송할 수 있는 권한을 가진 모든 프로세스에 시그널을 전송
-1 이외의 음수첫 번째 인수 pid 의 절대값 프로세스 그룹에 속하는 모든 프로세스에 시그널을 전송

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 값 설정

exit

#include <stdlib.h>

void exit(int status);
  • 프로세스를 종료시키는 함수
  • 종료하기 전에 모든 열려진 파일들을 자동으로 닫는다.
  • 출력 버퍼 속에 데이터가 있으면 그것을 쓰기 완료시킨다.
  • 주로 에러가 났을 때 강제 종료시키기 위해 if문 속에서 사용된다.
  • 입력 인자로 전달하는 status는 운영체제에 전달하며 main함수의 return값과 같은 역할이다.
  • status값은 에러 코드와 같으므로 정상 종료시 0을, 에러로 인한 종료시 0이 아닌 숫자, 대체로 1을 반환한다.

getcwd

#include <unistd.h>

char *getcwd(char *buf, size_t size);

현재 작업 디렉토리의 full path 명을 문자열로 얻는다. 즉, 프로세스에서 디렉토리의 위치를 알아내는 함수이다.

  • buf:
    • 현재 작업 디렉토리 경로를 저장할 buffer
    • 만약 buf가 NULL일 경우 size는 무시되며, malloc()을 통해 메모리를 할당한 후 작업 디렉토리 경로를 문자열로 반환한다.
  • size: buf에 할당된 메모리의 크기

반환값

  • 성공: 디렉토리 경로 문자열
  • 실패: NULL 및 적당한 errno 설정

chdir

#include <unistd.h>

int chdir(const char *path);

현재 작업 디렉토리를 변경하는 함수이다.

  • path: 변경할 상대 또는 절대 directory path

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

stat, lstat, fstat

stat()

#include <sys/stat.h>

int stat(const char *restrict path, struct stat *restrict buf);

파일의 크기, 권한, 생성일시, 최종 변경일 등 파일의 상태나 파일의 정보를 얻는 함수이다.
symbolic link인 파일을 path로 넘기면 그 원본 파일의 정보를 얻는다.

symbolic link
링크를 연결하여 원본 파일을 직접 사용하는 것과 같은 효과를 내는 링크이다.
윈도우의 바로가기와 비슷한 개념

  • path: 파일 명 또는 파일에 대한 상대 경로 또는 절대 경로
  • buf: 파일 상태 및 정보를 저장할 stat 구조체

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

Example

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>

int main(int argc, char *argv[])
{
    struct stat sb;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
        return 1;
    }

    if (stat(argv[1], &sb) == -1) {
        perror("stat");
        return 1;
    }

    printf("File type:                ");

    switch (sb.st_mode & S_IFMT) {
        case S_IFBLK:  printf("block device\n");            break;
        case S_IFCHR:  printf("character device\n");        break;
        case S_IFDIR:  printf("directory\n");               break;
        case S_IFIFO:  printf("FIFO/pipe\n");               break;
        case S_IFLNK:  printf("symlink\n");                 break;
        case S_IFREG:  printf("regular file\n");            break;
        case S_IFSOCK: printf("socket\n");                  break;
        default:       printf("unknown?\n");                break;
    }

    printf("I-node number:            %ld\n", (long) sb.st_ino);
    printf("Mode:                     %lo (octal)\n", (unsigned long) sb.st_mode);
    printf("Link count:               %ld\n", (long) sb.st_nlink);
    printf("Ownership:                UID=%ld   GID=%ld\n", (long) sb.st_uid, (long) sb.st_gid);
    printf("Preferred I/O block size: %ld bytes\n",         (long) sb.st_blksize);
    printf("File size:                %lld bytes\n",        (long long) sb.st_size);
    printf("Blocks allocated:         %lld\n",              (long long) sb.st_blocks);
    printf("Last status change:       %s", ctime(&sb.st_ctime));
    printf("Last file access:         %s", ctime(&sb.st_atime));
    printf("Last file modification:   %s", ctime(&sb.st_mtime));

    return 0;
}

>> File type:                regular file
>> I-node number:            4579628
>> Mode:                     100644 (octal)
>> Link count:               1
>> Ownership:                UID=103967   GID=4222
>> Preferred I/O block size: 4096 bytes
>> File size:                2660 bytes
>> Blocks allocated:         8
>> Last status change:       Tue Oct  4 16:52:32 2022
>> Last file access:         Tue Oct  4 16:52:38 2022
>> Last file modification:   Tue Oct  4 16:52:32 2022

lstat()

#include <sys/stat.h>

int lstat(const char *restrict path, struct stat *restrict buf);

파일의 크기, 권한, 생성일시, 최종 변경일 등 파일의 상태나 파일의 정보를 얻는 함수이다.
symbolic link인 파일을 path로 넘기면 symbolic link 파일 자신의 정보를 얻는다.

symbolic link
링크를 연결하여 원본 파일을 직접 사용하는 것과 같은 효과를 내는 링크이다.
윈도우의 바로가기와 비슷한 개념

  • path: 파일 명 또는 파일에 대한 상대 경로 또는 절대 경로
  • buf: 파일 상태 및 정보를 저장할 stat 구조체

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

fstat()

#include <sys/stat.h>

int fstat(int fildes, struct stat *buf);

열려진 파일의 크기, 권한, 생성일시, 최종 변경일 등 열려진 파일의 상태나 파일 정보를 얻는 함수이다.

  • fd: open() 등으로 생성한 file descriptor
  • buf: 파일의 상태 및 정보를 저장할 stat 구조체

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

stat 구조체

struct stat {
	dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* 파일의 종류 및 접근권한 */
    nlink_t   st_nlink;   /* hardlink 된 횟수 */
    uid_t     st_uid;     /* 파일의 owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* 파일의 크기(bytes) */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};

이 중 주요 내용을 보면
st_mode는 파일의 종류와 파일에 대한 access 권한 정보를 담고 있다.
아래 POSIX macro를 통해 파일의 종류를 확인할 수 있다.

S_ISREG(buf.st_mode) : 일반 파일 여부
S_ISDIR(buf.st_mode) : 디렉토리 여부
S_ISCHR(buf.st_mode) : character device 여부
S_ISBLK(buf.st_mode) : block device 여부
S_ISFIFO(buf.st_mode) : FIFO 여부
S_ISLNK(buf.st_mode) : symbolic link 여부
S_ISSOCK(buf.st_mode) : socket 여부 (주로 AF_UNIX로 socket 생성된 경우)

또한 st_mode는 파일 유형값으로 직접 비트연산 &로 확인할 수 있다.

S_IFMT     0170000   type of file
  
/* 파일 유형 */
S_IFSOCK   0140000   socket
S_IFLNK    0120000   symbolic link
S_IFREG    0100000   regular file
S_IFBLK    0060000   block device
S_IFDIR    0040000   directory
S_IFCHR    0020000   character device
S_IFIFO    0010000   named pipe (FIFO)

/* 특수 권한 설정 bit */
S_ISUID    0004000   set-user-ID bit
S_ISGID    0002000   set-group-ID bit
S_ISVTX    0001000   sticky bit

접근 권한 값(chmod의 값과 같음)
S_IRWXU    00700     mask for file owner permissions
S_IRUSR    00400     owner has read permission
S_IWUSR    00200     owner has write permission
S_IXUSR    00100     owner has execute permission
S_IRWXG    00070     mask for group permissions
S_IRGRP    00040     group has read permission
S_IWGRP    00020     group has write permission
S_IXGRP    00010     group has execute permission
S_IRWXO    00007     mask for permissions for others (not in group)
S_IROTH    00004     others have read permission
S_IWOTH    00002     others have write permission
S_IXOTH    00001     others have execute permission    

st_atime, st_mtime, st_ctime member 변수는 time관련 함수(<time.h>localtime(), ctime() 등)를 이용하여 conversion후에 사용하면 쉽게 사용할 수 있다.


unlink

#include <unistd.h>

int unlink(const char *path);

파일을 삭제하는 system call 함수이다. 정확하게는 unlink()hard link의 이름을 삭제하고 hard link가 참조하는 count를 1 감소시킨다.

hard link의 참조 count가 0이 되면, 실제 파일의 내용이 저장되어 있는 disk space를 free하여 OS가 다른 파일을 위해서 사용할 수 있도록 한다.
따라서 hard link를 생성하지 않은 파일은 바로 disk space를 해제하여 사실상 파일 삭제한다.

그러나 open()으로 파일이 열려진 상태에서 unlink()를 호출하여 hard link 참조 count가 0이 되어도 directory entry에서 파일 이름 등의 정보는 삭제되지만 disk space는 해제되지 않는다.

하드 링크(hard link)
파일 시스템의 파일의 이름과 연결되는 디렉터리 엔트리이다.
모든 디렉터리 기반의 파일 시스템들은 각 파일의 원래 이름을 제공하는 (적어도) 하나의 하드 링크가 있어야 한다.
"하드 링크"라는 용어는 동일한 파일에 대해 일반적으로 하나 이상의 하드 링크를 허용하는 파일 시스템에만 사용된다.

  • path: 파일을 삭제할 파일명 (절대 또는 상대 경로)

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

execve

#include <unistd.h>

int execve(const char *path, char *const argv[], char *const envp[]);

실행가능한 파일인 filename의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행하는 system call 함수이다.
즉, 현재 실행되는 프로그램의 기능은 없어지고 filename 프로그램을 메모리에 loading하여 처음부터 실행한다.

  • path
    • 교체할 실행파일 또는 명령어
    • 실행 가능한 binary이거나 shell이어야 하낟.
    • 정확한 절대 또는 상대 경로를 지정해야한다.
  • argv: c언어의 main(int argc, char *argv[])에서 argv와 비슷하며, main함수에는 argc가 있지만 execve에는 argc가 없으므로 main의 argv과 달리 array 끝에 NULL이 있다.
  • envp
    • key=value형식의 환경변수 문자열 배열로 마지막은 NULL이어야 한다.
    • 만약 기존에 설정된 환경변수를 사용하려면 environ 전역변수를 그냥 사용한다.

반환값

  • 성공: 없음 / return 값은 이미 지정된 프로그램의 로직으로 실행되기 때문에 받을 수 없다.
  • 실패: -1 반환 후 적당한 errno 설정

execve() 호출 후에 일어나는 프로세스의 변화

- signal 설정이 default로 변경
- mmap()으로 생성 memory mapping이 보존되지 않는다.
- shared memory 영역에 대한 access가 해제
- message queue descriptor가 close
- named semaphore가 close
- timer가 보존되지 않는다.
- opendir로 open된 directory stream이 close
- memory lock이 보존되지 않는다.
- atexit(), on_exit()로 등록된 exit handler가 해제
- 모든 thread가 사라진다.
- aio_read()/aio_write()과 같은 async I/O 동작이 취소
- 일반 file descriptor는 close되지 않는다.

dup

#include <unistd.h>

int	dup(int fd);

file descriptor를 받아 복제하여 반환한다.

  • fd: file descriptor

반환값

  • 성공: fd 값
  • 실패: -1 반환 및 적당한 errno 값 설정

dup2

#include <unistd.h>

int dup2(int fd, int fd2);

fd2fd가 가리키는 file descriptor를 복제하고 해당 file desctiptor를 반환한다.

  • fd: file descriptor
  • fd2: file descriptor

반환값

  • 성공: fd2가 fd가 가리키는 file descriptor를 가리키고 해당 file descriptor 반환
  • 실패: -1 반환 및 적당한 errno 값 설정

pipe

#include <unistd.h>

int	pipe(int fd[2]);

서로 독립된 프로세스들이 데이터를 주고받을 수 있도록 하는 pipe file descriptor를 설정한다.
fd[0]는 read, fd[1]은 write을 의미한다.
파이프 자체는 fork()를 하더라도 복사되지 않으며, 방향성이 없다.

  • fd: pipe file descriptor가 설정될 int 배열

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 값 설정

opendir

#include <dirent.h>

DIR *opendir(const char *filename);

특정 directory의 하위에 있는 파일명 및 디렉토리명 등의 목록을 얻기 위한 DIR *(디렉토리 스트림)를 생성하는 함수이다.

  • filename
    • directory 정보를 얻고 싶은 directory 명
    • directory 명은 절대 또는 상대 경로

반환값

  • 성공: NULL이 아닌값
  • 실패: NULL 반환 및 적당한 errno 반환

readdir

#include <dirent.h>

struct dirent *readdir(DIR *dirp);

디렉토리의 하위에 있는 파일 및 디렉토리 정보를 1개 읽는다. 즉, 1회 호출에 1건의 정보를 return 한다.
읽는 순서는 시간 순서, 파일명 순서, 파일 크기 등 어떤 기준이 없으므로 기본적으로 sorting이 되어 있지 않다.

  • dirp: opendir() 또는 fdopendir()을 통해 생성된 DIR *

반환값

  • 성공: NULL이 아닌값
    • LINUX의 struct dirent 구조체는 아래와 같으나, UNIX는 다르다.
    • 하지만 d_name는 유효하다.
    struct dirent {
        ino_t          d_ino;       /* inode number */
        off_t          d_off;       /* offset to the next dirent */
        unsigned short d_reclen;    /* length of this record */
        unsigned char  d_type;      /* type of file; not supported by all file system types */
        char           d_name[256]; /* filename */
    };
  • 실패: NULL 반환 및 적당한 errno 반환

closedir

#include <dirent.h>

int closedir(DIR *dirp);

opendir()으로 open한 DIR *에 대해, 더 이상 directory의 파일목록을 검색을 하지 않을 경우에 closedir()DIR *를 close하는 함수이다.

  • dirp: opendir() 또는 fdopendir()을 통해 생성된 DIR *

반환값

  • 성공: 0
  • 실패: -1 반환 및 적당한 errno 설정

strerror

#include <string.h>

char *strerror(int errnum);

오류 메세지 문자열을 가리키는 포인터를 얻어오는 함수이다. 즉, errnum 의 값을 통해 발생하였던 오류에 알맞은 오류 메세지를 가리키는 포인터를 리턴한다.
strerror() 에 의해 출력되는 오류 메세지는 현재 사용중인 컴파일러나 플랫폼에 따라 다를 수 있다.

  • errnum: 오류 번호

반환값

  • 오류 번호에 해당하는 오류 문자열을 가리키는 포인터

errno

#include <sys/errno.h>

extern int errno

system call 함수 또는 일부 함수들에서 오류 발생 시 해당 오류를 나타내는 오류번호가 담기는 전역 변수이다.
각 운영체제 마다 errno에 담기는 번호와 오류 메세지가 다양하다.


isatty

#include <unistd.h>

int isatty(int fd);

file descriptor가 터미널에 연결되어 있는지 확인하는 함수이다.

  • fd: file descriptor

반환값

  • 성공: 터미널에 연결되어 있으면 1, 그렇지 않으면 0
  • 실패: -1 반환 및 적당한 errno 설정

ttyname

#include <unistd.h>

char *ttyname(int fd);

file descriptor에 대한 터미널 이름을 얻어오는 함수이다.
일반적으로 ttyname은 자신의 터미널이름을 가져오는데 사용되므로 프로세스가 만들어질 때 기본적으로 생성되는 파일지정자(0, 1, 2)에 대해서 적용하는 경우가 많다.

  • fd: file descriptor

반환값

  • 성공: 터미널 이름이 담긴 포인터
  • 실패: NULL

ttyslot

#include <unistd.h>

int	ttyslot(void);

터미널에 대한 DB의 entry index를 반환한다.

반환값

  • 성공: 터미널에 대한 DB의 entry index
  • 실패: 이용하고 있는 시스템에 따라 0 또는 -1이 반환

ioctl

#include <sys/ioctl.h>

int ioctl(int fildes, unsigned long request, ...);

device driver와 통신하기 위한 함수이다.
즉, 장치에게 요청을 보낼 때 사용되는 시스템 콜(system call) 함수이다.

What is device driver?
하드웨어 주변 장치는 커널 안에 직접적으로 주소 지정을 할 수 있다.
응용 프로그램이 장치와 통신하길 원할 수 있다. (하드웨어 장치의 기능을 사용하고 싶을 때)
그런데 커널 개발자는 어떤 장치가 커널 안에 주소를 할당받을지, 그 장치에는 어떤 기능이 있는지 알 수가 없다.
따라서 확장가능한 모듈식으로 커널을 유연하게 설계할 필요가 있었고, 그렇게 해서 나온게 device driver이다.
즉, 어떠한 장치가 연결될 지 모르는 상황에서 device driver라는 하나의 중간 계층을 둠으로써 문제를 해결할 수 있다.

  • fildes: 장치를 참조하는 fildes로써, open()을 통해 얻는 fildes가 된다. (O_NONBLOCK flag 이용 권장)
  • request: fildes에 해당하는 장치에게 보낼 명령이다. 이 명령에 따라서 디바이스를 컨트롤 할 수 있다.

사용자 정의하에 이용되는 경우에는 ioctl 함수의 반환값을 이용할 수도 있다.
일반적으로 함수 호출 시 마지막 인자에는 char *타입을 이용한다.

반환값

  • 성공: 0
  • 실패: -1

getenv

#include <stdlib.h>

char *getenv(const char *name);

name에 해당하는 환경변수의 값에 대한 문자열을 반환한다.

  • name: 환경변수 key name

반환값

  • 성공: name에 해당하는 환경변수 값
  • 실패: NULL

tcsetattr

#include <termios.h>

int tcsetattr(int fildes, int optional_actions, const struct termios *termios_p);

터미널 파일에 대한 속성을 설정하는 함수

  • fildes : 터미널 file discriptor
  • optional_actions : 동작 선택
    • TCSNOW → 속성을 바로 변경
    • TCSADRAIN → 송신을 완료한 후 변경
    • TCSAFLUSH → 송수신 완료 후 변경
  • termios_p : 터미널 속성을 저장할 포인터

반환값

  • 성공: 0
  • 실패: -1 및 적당한 errno 설정

tcgetattr

#include <termios.h>

int tcgetattr(int fildes, struct termios *termios_p);

터미널 파일에 대한 속성을 얻어서 termios_p에 저장하는 함수

  • fildes: 터미널 file discriptor
  • termios_p : 터미널 속성을 저장할 포인터

반환값

  • 성공: 0
  • 실패: -1 및 적당한 errno 설정

terminos 구조체

#include <termios.h>

struct termios
{
    tcflag_t c_iflag;    /* input flags */
    tcflag_t c_oflag;    /* output flags */
    tcflag_t c_cflag;    /* control flags */
    tcflag_t c_lflag;    /* local flags */
    cc_t     c_cc[NCCS]; /* control chars */
    speed_t  c_ispeed;   /* input speed */
    speed_t  c_ospeed;   /* output speed */
};

termios구조체는 터미널의 거의 모든 속성을 다룰 수 있는 변수들로 이루어져있다.

tgetent

#include <termios.h>

int tgetent(char *bp, const char *name);

TermCap의 쿼리를 수행할 수 있도록 만드는 루틴이다.
일반적으로 name에 할당하는 값은 TERM 환경변수로 할당된 터미널 타입을 이용한다.

What is TermCap ?
Termcap은 Unix 계열 컴퓨터에서 사용되는 소프트웨어 라이브러리 및 데이터베이스이다.

반환값

  • 성공: 1
  • DB가 발견되었지만 TERM을 위한 개체가 없는 경우: 0
  • 실패: -1

tgetflag

#include <termios.h>

int tgetflag(char *id);

쿼리로 사용할 이름을 인자로 받는다. 플래그를 얻을 수 있는 쿼리의 경우 1, 그렇지 않은 경우 0을 반환한다.

tgetnum

#include <termios.h>

int tgetnum(char *id);

쿼리로 사용할 이름을 인자로 받는다.

함수 수행에 성공하면 그 값을 반환하고 그렇지 않으면 -1을 반환한다.

tgetstr

#include <termios.h>

char *tgetstr(char *id, char **area);

쿼리로 사용할 이름을 인자로 받는다.

쿼리에 해당하는 Escape Sequence를 얻을 수 있다면 그 값을 반환하고 그렇지 않은 경우 NULL을 반환한다.

tgoto

#include <termios.h>

char *tgoto(const char *cap, int col, int row);

매개변수를 지정된 기능으로 인스턴스화한다. 출력은 tputs로 전달된다.

colrow를 고려한 Cursor MotionEscape Sequence를 반환한다. 함수 수행에 실패하면 NULL을 반환한다.

tputs

#include <termios.h>

int tputs(const char *str, int affcnt, int (*putc)(int));

Escape Sequence에 대한 터미널 출력 결과를 내는 루틴이다.

tgetstr 또는 tgoto를 통해 얻은 값을 str인자로 받는다.

affcnt의 경우 영향을 끼칠 줄의 수를 나타나는데, 1로 주는 것이 일반적.

putc는 ASCII 문자 값을 인자로 받아 표준 출력의 쓰기 작업으로 터미널에 ASCII 문자 값을 출력해주는 함수.

0개의 댓글