티스토리 뷰

리눅스

시프밍 수업 정리

삼전동해커 2021. 12. 19. 20:42

시스템 콜이란?

응용프로그램에서 운영체제에게 기능을 수행해달라고 하는 수단

사용자 프로세서가 소프트웨어 인터럽트를 통해 커널의 기능을 이용하기 위한 서비스를 요청하는 방법.

시스템 콜 = 소프트웨어 인터럽트

 

main함수에서 fork()를 호출하고 fork()함수 내에 선언되어 있는 system_call()함수가 호출되면 sys_call_table에서 호출하려는 기능을 찾아 실행함.

 

시스템 콜과 라이브러리 함수의 차이점

시스템콜의 수행공간은 커널 모드이고 메모리 할당은 별도로 사용자 모드에서 메모리 할당이 필요하다. fd 사용.

라이브러리 함수의 수행공간은 사용자 모드이고 메모리 할당 여부는 라이브러리 함수에서 할당된 메모리를 이용한다. 파일 포인터 사용.

 

파일 디스크립터

파일을 열면 파일 디스크립터가 반환됨. 시스템 콜을 이용해 접근할 때 fd값을 이용해 접근.

반환된 파일 디스크립터를 관련 시스템 콜의 첫 번째 인자로 전달하여 읽기,쓰기 등의 다양한 연산 수행

0 : 표준 입력

1 : 표준 출력

2 : 표준 에러

 

FD테이블의 각 항목은 FD 플래그와 파일 테이블로의 포인터를 가지고 있다.

 

open() 시스템콜

- 파일을 열 때 사용. 리턴 값은 fd값, 실패 시 -1 반환

- mode 설정으로 파일의 읽기/쓰기/실행 권한을 설정

O_TRUNC : 이미 있는 파일에 내용이 있으면 파일의 내용을 지우고 씀.

 

Read() 시스템콜

- 읽기 성공하면 버퍼에 쓴 바이트 수 반환

- 파일의 끝을 만나면 0 반환

- 실패 시 -1 반환

 

write() 시스템콜

- 참조하는 fd 파일의 현재 위치에 buf의 내용을 기록

- 성공하면 쓰기에 성공한 바이트 수 반환

- 실패 시 -1 반환

 

I/O 연산은 커널 안의 버퍼나 페이지를 사용한다. 따라서 파일에 데이터를 기록하면 커널은 그 데이터를 커널의 버퍼들 중 하나에 복사해서 내부적인 대기열에 등록하고 적당한 시기에 디스크에 기록한다.

하지만 디스크에 적힐 순서가 아직 안되었거나, 시스템이 비정상적인 종료가 발생할 경우 디스크에 기록되지 않을 수 있다.

이런 문제를 해결하기 위해 다음과 같은 함수를 사용한다.

 

sync()는 모든 버퍼 내용(데이터 + 메타데이터)을 디스크와 동기화 한다.

 

fd와 관련된 파일은 다음을 사용한다.

fsync()는 fd에 매핑된 파일의 모든 변경점을 디스크에 기록

fdatasync()는 fsync()과 기능은 동일하나 데이터만 디스크에 기록

 

lseek()

- 현재 스트림의 위치 조작

- 에러가 발생하면 -1 반환

SEEK_CUR : 현재 커서 위치

SEEK_SET : 파일의 맨앞

SEEK_END : 파일의 맨 끝

- 파일의 끝을 넘어선 위치도 지정 가능

 

리다이렉션

명령어 > 파일 : 명령어의 출력결과를 파일로 보냄

명령어 >> 파일 : 파일 끝에 출력결과를 씀

명령어 < 파일 : 명령어의 입력(표준입력 : 0)으로 파일을 사용

명령어 < 파일1 > 파일 2 : 명령어는 파일1의 내용을 입력값으로 받고(표준 입력 : 0) 파일2에 출력을 쓴다(표준 출력 : 1)

 

pread()

- pread(int fd, void *buf, size_t count, off_t offset);

- offset 위치에서 count 수만큼 파일을 읽어서 buf에 저장. read와 달리 파일을 읽어도 파일의 offset이 바뀌지 않는다.

- 리턴값은 읽은 데이터의 바이트 수

- 0을 리턴하면 end of file에 도달하여 읽을 데이터가 없음.

- 오류 발생 시 -1

fd의 2번째 위치에서 5개만큼 출력.

 

fd파일에 buffer의 내용을 0위치부터 7바이트를 적음.

 

pwrite()

- pwrite(int fd, void *buf,size_t count, off_t offset);

- pread와 비슷.

 

 

파일 크기 변경

truncate() : 파일 이름으로 파일 크기 변경

ftruncate() : 파일 디스크립터로 파일 크기 변경

- 파일의 크기를 지정한 크기만큼으로 잘라낸다.

- 파일이 지정된 크기보다 작으면 나머지 채워지는 부분은 0이 된다.

 

다중 입출력

프로세스

-

프로세스 제어 블록

- 문맥교환이 일어날 때, 프로세스의 정보를 저장하는 자료구조

1) 프로세스 식별자(프로세스 id)

2)프로세스 상태 : 생성, 준비, 실행, 대기, 완료

3)프로그램 계수기 : 다음에 실행할 명령어의 주소

4)cpu 레지스터 및 일반 레지스터

5)cpu 스케줄링 정보 : 우선순위,최종 실행시각,cpu 점유시간

6)메모리 관리 정보 : 해당 프로세스의 주소 공간

7)프로세스 계정 정보 : 페이지 테이블, 스케줄링 큐 포인터, 소유자, 부모

8)입출력 상태 정보 : 프로세스에 할당된 입출력장치 목록, 열린 파일 목록

9)포인터 : 부모프로세스에 대한 포인터, 자식 프로세스에 대한 포인터, 프로세스가 위치한 주소에 대한 포인터, 할당된 자원에 대한 포인터 정보

 

ipc 

- 프로세스는 각각이 독립적으로 실행되고, 다른 프로세스에 접근할 수 없다.

- 다른 프로세스에 접근하기 위해서는 IPC라는 통신 절차를 따라야 한다.

- 메일슬롯, 파이프, 소켓, 시그널, 공유메모리가 있음.

 

스레드

- 운영체제의 일부인 스케줄러에 의해 독립적으로 관리 될 수 있는 프로그래밍된 명령어의 가장 작은 시퀀스

- 프로세스 내에서 실행되는 여러 흐름의 단위

- 스레드는 프로세스 내에서 각각 stack만 따로 할당 받고, code,data,heap은 공유된다

 

멀티 스레드

- 하나의 프로세스가 여러 개의 스레드를 구성하고 각 스레드가 하나의 작업을 처리하도록 함.

 

동기/비동기

동기

- 호출된 함수의 수행 결과 및 종료를 호출한 함수가 신경쓰는 것. 커널의 작업이 끝날 때까지 대기하는 방식.

비동기 

- 호출된 함수의 수행 결과 및 종료를 호출한 함수가 신경쓰지 않는 것. 작업을 대기 시켰다가 어떤 신호가 오면 대기중인 작업을 진행시킴.

 

blocking

- 호출된 함수가 자신이 할 일을 모두 마칠 때까지 cpu 제어권을 계속 가지고서 호출한 함수에게 다시 돌려주지 않는것.

 

non-blocking

- 호출된 함수가 자신이 할 일을 마치지 않았더라도 cpu 제어권을 호출한 함수에게 넘겨주어 다른 일을 진행할 수 있도록 함.

 

다중 입출력

- 단일 스레드에서 여러 개의 파일을 작업하고자 할 때 사용.

- A,B,C,D 파일을 작업하고 싶을 때.

A작업을 진행하고 마침 -> B 작업에서 read()함수가 read할 내용이 없어 블록 상태가 됨. 작업이 멈춤

-> 이를 해결하기 위해 작업할 준비가 된 C,D를 먼저 실행. 이런 함수가 select()

 

select()

- select(int n, fdset *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

- 한 곳에 모아놓은 여러 개의 파일 디스크립터를 동시에 관찰.

- n : 감시할 fd 수

 - readfds : 읽을 데이터가 있는지 감시하는 파일의 집합

- writefds : 쓰기를 할 데이터가 있는지 감시하는 파일의 집합

- exceptfds : 예외사항이 있는지 검사하는 파일 집합

-timeout : 지정된 시간만큼만 파일이 블록상태 유지. 읽을 데이터가 없다면 0을 리턴, NULL로 설정 시 무기한 대기

 

fd_set자료형

- FD_ZERO : fd_set 초기화 함수

- FD_SET : 해당 fd를 1로 설정

- FD_CLR : 해당 fd를 0으로 설정

- FD_ISSET : 해당 fd가 1인지 확인

 

poll() 시스템 콜

- poll(struct pollfd* fds,nfds_t nfds, int timeout);

fds : 모니터링할 fd와 event 종류를 설정하고, poll이 반환되었을 때 결과값을 저장.

nfds : 설정된 fds 수

timeout : 0으로 설정하면 바로 리턴, null값(아무것도 안넣으면) 무한대로 설정. event가 발생할 때까지 무한 대기

- select와 비슷한 기능을 하지만, readfds,writefds는 재활용할 수 없지만, pollfd의 값을 한번 설정하면 fd가 추가되거나 삭제되지 않으면 재활용이 가능.

- 리턴값은 이벤트가 발생한 fd의 개수

 

pollfd 구조체

struct pollfd{

    int fd; //fd

   short events; //감시할 이벤트

   short revents; //발생한 이벤트

}

event 상수값

- POLLIN : 읽을 데이터가 있다

- POLLPRI : 먼저 읽어야할 데이터가 있다.

- POLLDNORM : 일반 데이터를 읽을 수 있다.

- POLLRDBAND : 우선권이 있는 데이터를 읽을 수 있다.

- POLLWDBAND : 우선권이 있는 데이터 쓸수 있다.

- POLLOUT : 쓰기가 블록되지 않는다. = 바로 쓸 수 있는 상태

- POLLIN | POLLPRI = select의 읽기이벤트 설정과 동일

- POLLOUT | POLLWRBAND : 쓰기 이벤트와 동일

- POLLIN = POLLRDNORM | POLLRDBAND

- POLLOUT = POLLWRNORM

 

revent 상수값

- POLLERR : 주어진 fd에 에러가 있다.

-POLLHUP : 주어진 fd에 이벤트가 지체되고 있다.

-POLLNVAL : 주어진 fd가 유효하지 않다.

 

페이지/버퍼 캐시

- 입출력의 성능 향상을 위해서 만든 메모리 영역

- 한 번 읽은 내용을 저장해 두는 영역

- 디스크 접근을 줄여서 파일에 대한 빠른 접근이 목적

 

버퍼 입출력 = 표준 입출력

 - fd를 직접 다루지 않고 파일 포인터라는 개념을 사용

 - 열린 파일은 스트림이라고 표현

 

가상 파일 시스템(VFS)

파일시스템 관련 인터페이스를 사용자 공간 어플리케이션에 제공하는 커널 서브시스템. 다양한 파일시스템들이 공존할 수 있도록 공통된 인터페이스를 제공. 즉, open,write,read를 다른 파일시스템에서 호출해도 동작할 수 있게 해줌.

 

벡터 입출력

 

struct iovec{
	ptr_t iov_base;	//전송할 데이터의 시작 주소를 가리킴
   	size_t iov_len;	//iov_base가 가리키는 위치를 시작으로 전송할 바이트 수
}
readv(int fd, const struct iovec *iov,int count); : 데이터를 여러 버퍼에 나누어서 수신.fd에서 데이터를 읽어서 count개수만큼 iov 버퍼에 저장.
writev(int fd, const struct iovec *iov, int count); : 여러 버퍼에 저장되어 있는 데이터를 한번에 전송. count 개수만큼 iov버퍼에 있는 데이터를 fd에 기록.
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<sys/uio.h>

int main(){
    char love[11] = {0}, hello[12] = {0}, test[12] = {0};
    struct iovec iov[3];
    ssize_t nr;
    int fd,i;

    fd = open("vectest.txt",O_RDONLY);

    iov[0].iov_base = love;	//수신할 데이터의 시작 주소(love 배열)
    iov[0].iov_len = 11;	//love 배열의 11바이트 수를 대입
    iov[1].iov_base = hello;	//hello 배열
    iov[1].iov_len = 12;	//hello 배열 12바이트 대입
    iov[2].iov_base = test;	//test 배열
    iov[2].iov_len = sizeof(test);   	//test의 크기 12바이트 만큼 대입

    nr = readv(fd,iov,3);

    for(i = 0;i<3;i++){
        printf("%d : %s\n",i,(char*)iov[i].iov_base);
    }
    
}

 

mmap()함수

mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

- 파일이나 디바이스를 프로그램의 주소 공간 메모리에 대응.

- mmap 함수는 fd로 지정된 파일에서 offset만큼의 물리주소에서 시작해 len 바이트 만큼을 addr로 대응.

 

prot : 맵핑을 원하는 메모리 보호 정책

- PROT_READ : 읽기 가능한 페이지

- PROT_WRITE : 쓰기 가능한 페이지

- PROT_EXEC : 실행 가능한 페이지

- PROT_NONE : 접근할 수 없는 페이지

 

flags : 맵핑 유형과 동작 구성 요소

 - MAP_FIXED : addr과 len 매개변수가 기존 맵핑과 겹칠 경우 중첩 페이지를 버리고 새 페이지 대체

 - MAP_SHARED : 동일 파일을 맵핑한 모든 프로세스들이 공유

- MAP_PRIVATED : 맵핑을 공유하지 않아 파일은 쓰기 후 복사로 맵핑. 원본은 훼손되지 않고 수정된 복사본이 생성됨.(copy-on-write)

 

Getpid는 함수를 호출한 프로세스의 id를 리턴, getppid는 부모의 id를 리턴

프로세스를 실행하는 방법은

exec() : 메모리를 별도로 할당하지 않고 인자로 전달받은 다른 프로그램을 자신을 호출한 프로세스의 메모리에 덮어쓴다.

execl,execv는 파일명을 전체 경로로 포함

execlp,execvp는 파일명만 넘겨주면 프로세스 환경변수에서 파일명을 탐색

execle,execve는 환경변수를 별도로 정의

fork() : 새로운 프로세스를 위한 메모리를 할당하여 자식 프로세스를 생성

부모와 자식은 코드 세그먼트는 복사하지 않고 공유, 스택은 복사본을 가짐

 

copy on write

fork를 통해 자식 프로세스를 생성하면 부모 프로세스와 자식프로세스는 코드 세그먼트를 공유한다. 그런데 부모 프로세스가 새로운 데이터를 쓰게 되면 같은 메모리 공간을 사용할 수 없다. 이 때 부모 프로세스가 새로운 데이터를 쓰려는 페이지를 복사한 다음 수정한다.

 

vfork는 부모 프로세스와 자원을 공유하지만 복사는 하지 않기 때문에 생성속도가 빠르다.

하지만 자원을 공유하기 때문에 자원에 대한 race condition이 발생하지 않도록 자식 프로세스가 종료되거나 exec()가 호출되기 전까지 부모 프로세스는 block상태이다.

exec()가 호출되면 현재 프로세스를 함수가 가리키는 실행 파일로 바꾼다.

 

 

'리눅스' 카테고리의 다른 글

리눅스 프로그래밍 파일 다루기  (0) 2021.09.22
scp를 이용한 파일 전송  (0) 2020.11.21
mpreferred-stack-boundary=2  (0) 2020.11.14
iptables 설정  (0) 2020.10.25
Linux의 부팅  (0) 2020.10.24
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함