시스템 콜 system call ?

리눅스의 세 가지 개념 중 '스트림(stream)'이 있었다. 이 스트림과 관련된 것은 시스템 콜과 라이브러리 함수이다. 라이브러리보다 시스템 콜이 커널 쪽에 좀 더 가깝다. 라이브러리는 유저모드에서 동작하며 시스템 콜은 커널(운영체제)에서 동작한다. 참고로 시스템 콜은, 그야말로 리눅스 프로그램에서 핵심이다.

 

>> 함께 보기 좋은 포스팅

https://jinn-o.tistory.com/119?category=971524 

 

[리눅스 프로그래밍] 리눅스 추상화의 층구조

추상화의 층구조 응용 프로그램 application program ex. hello.c ▽ 라이브러리 library ex. printf() ▽ 시스템 콜 system call ex. sys_write() ▽ 파일 시스템 file system ex. fs_write() ▽ 디바이스 드라이..

jinn-o.tistory.com

https://jinn-o.tistory.com/120?category=971524 

 

[리눅스 프로그래밍] 리눅스의 세 가지 중요 개념

리눅스에서 중요한 세 가지 개념은 바로 파일 시스템, 프로세스, 스트림 이다. 데이터에 이름을 붙여 보관하고 관리하는 파일 시스템이 있다. 그리고 어떤 활동을 하는 주체로서 프로세스가 있

jinn-o.tistory.com

https://jinn-o.tistory.com/124

 

[리눅스 프로그래밍] 스트림 stream

스트림 Stream ?  리눅스 프로그래밍에서의 스트림은 바이트의 흐름, 즉 바이트 스트림(byte stream)을 말한다. 스트림을 바이트들이 흘러다니는 길이라고 생각해도 좋다. 스트림은 파일 디스크립터

jinn-o.tistory.com

 

리눅스(유닉스)의 입출력은 대부분 다음 네 개의 시스템 콜로 처리된다.

read 스트림에서 바이트 열을 읽는다.
write 스트림에 바이트 열을 쓴다.
open 새로운 스트림을 생성한다.
close 사용 완료한 스트림을 닫는다.

 

 

 

 

 

read(2)

스트림에서 바이트 열을 읽기 위해 사용하는 시스템 콜이다.

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t butsize);
내용 설명
read(2) 에서 2 시스템 콜을 의미한다. (man 명령어에서 2는 시스템 콜을 의미한다.)
#include <unistd.h> API 를 사용하기 위해서 해당 헤더 파일을 포함해야 한다.
즉, read(2) 를 사용하기 위해서는 unistd.h를 include 해야 한다.
ssize_t 그리고 size_t sys/types.h 에 정의된 자료형으로 정수형의 별명(alias)이다.
말하자면 int 타입 또는 log 타입이다.
ssize_t는 부호가 있는 정수형이고, size_t는 부호가 없는 정수형이다.
fd 파일 디스크립터 번호
*buf 메모리 공간
bufsize buffer 의 최대 사이즈

 

 read()는 파일 디스크립터 번호인 fd에 해당하는 스트림에서 바이트 열을 읽는 시스템 콜이다. 읽기 작업이 순조롭게 완료되면 읽어 들인 바이트 수를 반환한다. 그리고 파일의 끝에 도달한 경우에는 0을 반환하고, 중간에 에러가 발생한 경우에는 -1을 반환한다. bufsize 바이트 수보다 적은 바이트 수를 읽는 경우도 많으므로 반환값을 체크하도록 코딩해야 한다.

 그런데 C 언어 문자열(char 배열)에는 임의의 바이트 열을 저장할 수 있지만, 일반적으로 문자 열의 끝에는 '\0'을 넣는 게 관례다. API 중에도 문자열의 끝에 '\0'이 있다고 전제하는 것과 그렇지 않은 것이 있어 사용에 주의해야 한다. read(2)의 경우는 읽어 들인 데이터의 끝에 '\0'가 있다고 전제하지 않는 API다. 따라서 read(2)를 통해 읽어 들인 문자열의 끝에 '\0'이 있다고 생각하고 코드를 작성해서는 안된다. 예를 들어, printf()의 경우는 문자열의 끝에 '\0'이 들어가 있다고 전제하는 API이므로 read(2)로 읽은 문자열을 그대로 printf()로 전달해서는 안된다. 이는 보안상 취약점이 될 수도 있다.

 

>> read() 시스템 콜 직접 실습해보기

https://jinn-o.tistory.com/125

 

[LPI 실습] 존재하는 파일으로부터 데이터를 읽기

vi file_test.c 먼저 vi 명령어로 file_test.c 파일을 생성해준다. vi 편집기 내부에서는 다음과 같이 작성했다. /* file_test1.c: read data from a file, by mjson. jinn_o@naver.com */ #include #include #in..

jinn-o.tistory.com

 

 

 

 

 

write(2)

스트림에 바이트 열을 쓸 때 사용하는 시스템 콜이다.

#include <unistd.h>

ssize_t write(int fd, const *bf, size_t bufsize);

 write()는 인자로 지정한 bufsize 바이트만큼 buf의 내용을 fd로 지정한 파일 디스크립터의 스트림에 쓴다. 정상적으로 쓴 바이트 수를 반환하고 에러가 발생한 경우는 -1을 반환한다.

 

>> write() 시스템 콜 직접 실습해보기

https://jinn-o.tistory.com/126

 

[LPI 실습] write() 시스템 콜 직접 사용해보기

write() 시스템 콜 직접 사용해보기 실습 실습(1)의 확장판, 터미널에 읽은 데이터를 출력하기 >> 실습(1) https://jinn-o.tistory.com/125 [LPI 실습] 존재하는 파일으로부터 데이터를 읽기 vi file_test.c 먼저..

jinn-o.tistory.com

 

 

 

 

 

open(2)

파일을 읽고 쓰는 스트림을 만들기 위한 시스템 콜이다.

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

// 대괄호[]는 선택 인자를 의미한다.
int open(const char *pathname, int flags, [mode_t mode]);
내용 설명
const char *pathname 절대 경로 or 상대 경로.
여기에 지정한 경로의 파일에 대한 스트림을 만들고, 그 스트림을 가리키는 파일 디스크립터를 반환한다. 이러한 과정을 흔히 파일을 연다(open)고 한다.
int flags 파일을 어떤 모드로 열 것인지 지정한다.
mode_t mode 두 번째 인자 flags에 O_CREAT를 설정했을 때만 유효한 인자다. 새로운 파일을 만들 때, 그 파일의 권한을 설정한다. (S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP ... S_IROTH, ... 등등)
반환 값 성공한다면 파일 디스크립터를 반환하고, 실패한다면 -1 을 반환한다.

 

int flags

이때 1은 man 명령어에서 실행 가능한 프로그램이나 셸 명령어를 의미한다.

flag(1)의 종류 의미
O_RDONLY 읽기 전용
O_WRONLY 쓰기 전용
O_RDWR 읽고 쓰기

 

 

만약 O_WRONLY 또는 O_RDWR 을 사용하여 쓰는 스트림을 만들 때는 아래와 같은 추가적인 옵션을 지정할 수 있다.

flag(2)의 종류 의미
O_CREAT 파일이 존재하지 않으면 새로 만든다.
O_EXCL Exclusive(배타적)라는 의미이며, O_CREAT와 함께 사용되어 이미 파일이 존재하면 에러가 된다.
O_TRUNC Truncate(잘라 없애다)라는 의미이며, O_CREAT와 함께 사용되어 이미 파일이 존재하면 파일의 크기를 0으로 만든다. 즉, 기존에 작성되어 있는 데이터를 날리고 새로 작성한다. 덮어쓰기라고 생각하면 쉽다.
O_APPEND 기존의 데이터 뒤에 작성한 내용을 추가한다.
O_NONBLOCK 만약 BLOCK의 상태라면 데이터가 들어올 때까지 프로그램이 블락킹 상태가 되는데, 이때 블락킹 상태란 프로그램이 멈춘다고 생각하면 된다. 그러나 NONBLOCK 상태라면 데이터가 있든 없든 기다리지 않고 나온다. (백그라운드 작업과 비슷하다)
O_SYNC Synchronous라는 의미이며, 작업 내용이 항상 디스크에 반영되도록 한다.

 

 

 

 

 

close(2)

사용이 끝난 스트림을 닫는 시스템 콜이다.

#include <unistd.h>

int close(int fd);

close()는 파일 디스크립터 fd에 연결된 스트림을 해제한다. 오류 없이 닫히면 0, 에러가 발생하면 -1을 반환한다. 일반적으로 close()함수를 호출하는 코드는 다음과 같다.

if (close(fd) < 0) {
	// ...
}

프로세스가 종료되면 사용하던 모든 스트림을 커널이 파기하기 때문에 close()를 하지 않아도 시스템에 이상이 생기지 않을 수도 있다. 그러나 사용이 완료된 스트림은 반드시 바로 종료해주는 것이 좋다. 프로세스가 동시에 사용할 수 있는 스트림의 개수에 제한이 있기도 하고, 스트림의 반대편에 프로세스가 close()할 때까지 기다리고 있을 수도 있다. 모든 리소스는 사용이 완료되었을 때 닫아주는 것이 바람직하다.

+ Recent posts