유닉스의 구조
1. 커널 (kernel) : 메모리에 상주하는 부분
작은 의미의 운영체제라고도 함.
2. 시스템 호출(system call) : 응용프로그램에서 운영체제에게 어떠한 기능을 수행해 달라는 하나의 수단
응용 프로그램이 커널의 기능을 사용할 수 있도록 한다.
3. 쉘 (shell) : 커널과 사용자 간의 다리역할
명령을 받아 해석하고, 프로그램을 실행시킨다.
쉘의 종류
1.Bourne shell : 최초의 셸, 가장 기본적인 쉘이다.
2. Bourne-again shell : 리눅스에서 가장 많이 사용하는 쉘로 C쉘과 콘 쉘의 장점을 결합하여 작성되었다.
3. C shell : C언어 기반으로 만들어졌다.
4. Korn shell : 본 쉘을 확장한 쉘로 본 쉘의 명령어를 모두 인식하며 유닉스에서 가장 많이 사용되는 쉘이다.
5. TENEX C shell : C shell의 기능을 강화한 쉘이다.
파일 및 디렉토리
1. 파일 시스템
데이터를 실제로 저장하는 파일과 이를 계층적으로 연결하는 디렉터리로 구성된다.
2. 파일 이름
디렉토리에 있는 이름
‘/’는 경로이름을 형성하는 파일이름을 구분한다.
이름은 문자(a-z, A-Z), 숫자(0-9), 마침표(.), 대시(-), 밑줄(_)로 구성된다.
3. Path name
‘/’로 경로명에서 이름을 구분한다
‘/’로 시작하는 경로를 절대 경로 이름, 아니면 상대 경로이름 이라 한다.
디렉토리 내의 모든 파일 리스트
ls 명령어 : 현재 디렉토리의 파일에 대한 리스트를 보여준다.
①#include “apue.h”
- 다운받아서 사용한다.
- 책에 등장하는 거의 모든 프로그램에 포함된다.
②#include <dirent.h>
- 파일 시스템의 디렉터리를 나타내기 위해 쓴다.
③int main(int argc, char *argv[])
- 메인함수 내 기본적으로 구성되어 있는 매개변수들이다.
int argc : arguments count로 함수에 전달된 정보의 개수이다.
char * argv[] : arguments vector이며, 메인함수에 전달되는 실질적인 정보로, 문자열의 배열을 의미한다.
argc는 4개
④DIR *dp;
DIR : 디렉토리 경로를 나타내는 자료형
⑤struct dirent *dirp;
dirent라는 구조체명을 가진 구조체 변수 선언
⑥if(argc !=2) : 2인 이유
cmd에 C:\test 파라미터를 입력 합니다 라고 입력시 int argc는 4이다.
따라서 2라는 숫자의 의미는 입력 파라미터가 1개라는 의미이다
⑦err_quit(“usage : ls directory_name”) : 에러에 대한 설명을 출력할때 사용하는 함수
⑧if((dp=opendir(argv[1]))==NULL)
opendir() : 해당 디렉토리를 열고 첫번째 요소의 위치를 리턴하는 함수
⑨err_sys(“can’t open %s”,argc[1]);
err_sys : 표준 에러 메시지를 출력하는 함수
⑩while((dirp = readdir(dp) != NULL)
readdir() : 디렉터리 내의 정보를 구하는 함수
⑪closedir(dp) : 디렉토리 닫기
exit(0) : 정상종료한다.
< 실행결과 >
*위 코드를 실행한 결과
*ls –a 명령어를 실행한 결과
디렉터리
디렉터리 (Directory) :
파일 분류를 위해 붙이는 이름공간
파일의 구조를 의미하며 폴더와 유사하다.
작업 디렉터리 (Working directory) :
모든 프로세스에 존재한다.
현재 위치의 디렉터리를 의미한다.
홈 디렉터리 (Home directory) :
사용자에게 할당된 사용자 개인에 대한 기본 디렉터리 영역
계정을 가진 사용자가 최초 접속 시 들어가게 되는 사용자 디렉터리
표준 입력을 표준 출력에 복사
①#define BUFFSIZE 4096 :
BUFFSIZE를 4096으로 설정하는 전처리기
②while((n=read(STDIN_FILENO, buf, BUFFSIZE))>0) :
read() : 파일 읽기 함수
리턴 값 : 성공 : 수신한 바이트 수
실패 : -1
③if (write (STDOUT_FILENO, buf, n) != n)
write() : 파일 쓰기 함수
리턴 값 : 성공 : 전달한 바이트 수
실패 : -1
<실행결과>
Standard I/O
Standard I/O를 사용하면 최적의 버퍼 크기를 선택할 필요가 없다.
가장 일반적인 표준 I/O의 기능은 printf이며, 이를 호출하는 프로그램에는 항상 <stdio.h>가 포함된다.
표준 I/O를 사용하여 표준 출력에 표준 입력을 복사
①while (( c = getc(stdin)) != EOF) :
getc(stdin) : = ( getchar() )
stdin에서 한문자를 가져온다
EOF : End Of File
getchar()이 파일의 끝에 도달했을때 반환
②if(putc (c,stdout)) == EOF) :
putc () : 스트림에 문자를 쓴다.
③err_sys (“output error”); : 에러메시지 출력
④if( ferror(stdin)) : 오류가 생기지 않았으면 0 리턴
⑤err_sys (“input error”); : 에러메시지 출력
⑥exit(); : 정상종료
<실행 결과>
버퍼를 사용하기 때문에 데이터 손실의 위험이 적다.
프로그램과 프로세스
프로그램 (Program)
-디렉터리의 디스크에 상주하는 실행 파일이다.
-메모리에 읽혀지고 커널에 의해 실행된다.
프로세스 및 프로세스 ID (Processes and Process ID)
-프로그램의 실행 인스턴스를 프로세스라고 한다.
-UNIX 시스템은 모든 프로세스에 프로세스 ID라는 고유한 숫자 식별자가 있다.
-프로세스 ID는 항상 음수가 아닌 정수이다.
프로세스 ID 출력
getpid() : -자신의 프로세스 ID를 반환함
-pid_t 데이터 유형을 반환한다.
pid_t : -프로세스 번호를 저장하는 타입
<실행결과>
표준 입력에서 명령을 읽고 실행
①#include <sys/wait.h> :
아래의 waitpid()를 쓰기 위해 써준다
②pid_t pid; :
pid_t : 프로세스 번호를 저장하는 타입
③while( fgets (buf, MAXLINE, stdin) != NULL) :
fgets : 스트림에서 문자열을 받는다
성공 : buf값을 리턴
실패 : null 리턴
④ if( buf [strlen(buf) - 1] == ‘\n’ :
strlen() : 문자열의 길이를 반환한다.
⑤if( ( pid = fork() ) < 0) :
fork() : 프로세스 생성 함수
성공 : 프로세스가 2개가 됨(부모,자식)
실패 : -1 리턴
⑥execlp(buf, buf, (char *) 0 );
execlp() : 시스템 호출 함수 / 읽은 명령 실행
⑦exit(127); : command not found
⑧if ( ( pid = waitpid(pid, &status, 0) ) <0
waitpid() : 자식프로세스가 종료될때까지 기다리는 함수
성공 : 프로세스 ID 반환
실패 : -1
<실행결과>
스레드와 오류처리
스레드 :
- 프로세스 내에서 실행되는 흐름의 단위
-한 프로그램은 하나의 스레드를 가지고 있다.
-멀티 스레드로 병렬처리를 이용할 수 있다.
-스레드는 ID로 식별이 된다. (스레드를 조작할때 식별하기 위해 사용)
오류처리 :
- 오류 발생시 보통 음수값이 반환된다.
-일부 함수는 null 포인터를 반환하여 오류를 나타낸다.
-<errno.h>는 error number로 정적 메모리 위치에 저장된 오류코드를 통해 오류 상태를 보고 및 검색하기 위한 매크로를 정의한다.
-함수의 반환 값에 오류가 발생했음을 나타내는 경우에만 값을 검사한다.
-errno의 값은 어떤 함수에도 0으로 설정되지 않는다.
-치명적 에러 : 복구작업이 없다. 실행이 중지된다.
-치명적이지 않은 에러 : 코드로 수정이 가능하다. 일시적이다.
오류 메시지 문자열인 errnum을 오류 메시지 문자열로 매핑하고 문자열에 포인터를 반환한다.
오류 함수는 현재 오류 값을 기준으로 표준 오류에 대한 오류 메시지를 생성하고 반환한다.
msg가 가리키는 문자열을 출력하고 콜론과 공백 뒤에 오류메시지를 출력한 뒤 새로운 줄을 출력한다.
strerror 및 perror 증명
①fprintf( stderr, “ EACCES : %s \n” , strerror(EACCES));
fprintf() : 데이터를 형식에 맞추어 스트림에 쓴다.
strerror() : 오류메시지 문자열을 가리키는 포인터를 얻어온다.
리턴값 : 오류번호에 해당하는 오류 문자열을 가리키는 포인터
②perror ( argv [0]); : perror() : 오류메시지를 출력한다.
<실행결과>
사용자 식별
①User ID : - 식별하는 숫자 값이다.
- 변경이 불가능하다.
- ID가 0이면 루트, 슈퍼유저이다.
②Group ID : - 암호파일 내에 동일한 ID를 지정하는 여러 항목이 포함되어 있다
- 모든 파일은 사용자 ID와 그룹ID를 모두 저장한다.
①printf(“uid = %d, gid = %d\n”, getuid(), getgid());
getuid() : 사용자 아이디를 얻어온다.
getgid() : 그룹 아이디를 얻어온다.
<실행결과>
보조그룹 ID
①암호파일에 지정된 그룹ID 외에도 대부분의 UNIX 시스템 버전은 사용자가
다른 그룹에 속하도록 허용한다
②로그인시 /etc/group 파일을 읽고 사용자를 구성원으로 나열하는 처음 16개의 항목을 찾아 가져온다.
signal
Signal : 프로세스가 일부 상태가 발생했음을 알리는 데 사용되는 기법이다.
프로세스를 처리하기 위해
시그널을 무시한다.
기본 작업을 수행한다.
시그널이 발생할 때 호출되는 기능을 제공한다. 이 기능으로 원하는 대로 처리 가능하다.
인터럽트 키와 종료 키는 현재 실행 중인 프로세스를 중단하는데 사용된다.
kill 함수를 호출하여 다른 프로세스에 시그널을 보낼 수 있다.
표준 입력에서 명령 읽기 및 실행
①if(signal(SIGINT , sig_int) == SIG_ERR)
②signal() : 인터럽트 신호를 처리한다.
③SIG_ERR :
이 리턴 값은 signal()에 대한 호출에서의 오류를 표시한다.
성공 시 sig_int의 값을 리턴 한다.
<실행결과>
시간 값(Time Values)
1.Calendar time : 1970년 1월 1일 이후 시간을 카운트 한다.
2. Process time : CPU 시간이라고도 하며 프로세스에서 사용하는 중앙 프로세서 리소스를 측정한다.
3. wall clock time : 프로세스가 실행되는데 걸리는 시간의 양.
4. user CPU time : 사용자 명령으로 인한 CPU 시간이다.
5. system CPU time : 프로세스를 대신하여 실행될 때 커널에 귀속되는 CPU 시간이다.
malloc 기능과 sbrk 시스템 호출의 분리
malloc(): 동적으로 메모리를 확보할 때
유용하게 사용 가능하다.
sbrk(): 필요에 따라 메모리를 확보하는 역할을 한다.
함수 내에 인자 값에 해당하는 만큼 메모리를 확장한다.
시스템 호출과 라이브러리 함수의 차이
라이브러리 함수를 사용 :
함수 내에 사용된 시스템 콜을 사용한다.
응용 프로그램 내에서 바로 시스템 콜을 사용 :
라이브러리 함수를 거치지 않고 커널의 기능을 사용할 수 있다.
피드백 정리
커널이 왜 메모리에 항상 상주해야 하는가?
먼저 커널이 아닌 다른 프로그램들은 메모리에 있어도 되고 없어도 된다. 프로그램이 동작하기 위해서는 메모리에 로드 되어야 하는데 메모리의 용량은 제한되어 있기 때문에 어러 프로그램을 동시에 할당하기에는 공간이 부족할 때가 많다. 커널이 아닌 프로그램은 사용자가 요청할 때에만 메모리에 로딩된다. 커널은 이런 프로세스를 구동하기 위해 메모리를 할당하고 관리해야 한다. 또한 응용프로그램이 하드웨어에 접근하는 것을 가능하게 한다. 따라서 커널이 메모리에 없으면 운영체제가 동작하지 않기 때문에 메모리에 상주해야 한다.
2. 절대/상대 경로에 관한 명령어
사진과 같이 A 디렉토리 내에 B, C, D가 차례로 있는 상태이다.
절대경로 ( b디렉토리에서 d 디렉토리로 이동 후 다시 b디렉토리로 이동)
→ 현재 위치에 관계없이 이동하고 싶은 경로를 적어주면 된다.
2. 상대경로 ( b디렉토리에서 d 디렉토리로 이동 후 다시 b디렉토리로 이동)
→ 현재 위치를 고려하여 전체 경로가 아닌 일부경로만 적어준다.
cd ./c/d 는 b에서 d로 이동하는 것이고 cd ../../은 d에서 b로 이동하는 것이다.
3. readdir()는 디렉터리 내의 정보를 구하는 함수인데 그 정보는 어떤 정보인가?
먼저, struct dirent이다.
inode의 번호, 디렉토리 내에서의 오프셋, 엔트리의 길이, 파일이나 디렉토리 이름을 담고 있다.
readdir(dp)는 dp라는 이름의 디렉터리 구조체의 포인터를 반환한다.
더 이상 읽을 정보가 없거나 오류가 발생하면 NULL을 반환한다.
처음 readdir()을 호출하면 첫 번째 파일, 또 호출하면 두 번째 파일 또 호출하면 세 번째 파일 순서로 정보를 반환한다.
따라서 더 이상 읽을 정보가 없을 때까지 디렉토리의 이름을 출력하는 코드이다.
4. getpid()를 long으로 형변환 해준 이유.
getpid()는 반환값이 pid_t 이다.
long으로 형변환 해주는 이유는 ID는 숫자이므로 숫자를 출력해야하기 때문이다.
5. fork()
fork() : 프로세스를 생성하는 함수
성공 시 리턴값이 2개인데 fork를 호출하는 프로세스는 부모프로세스, 새롭게 생성되는 프로세스는 자식 프로세스가 된다.
부모프로세스는 새로운 pid가 반환되고, 자식 프로세스는 0이 반환된다.
프로세스 생성 실패 시 –1을 리턴한다.
이 부분에서 if문 내에서 pid < 0 은 fork()가 실패했을 경우이므로 –1이 리턴되었다.
else if 문에서 pid == 0 은 새롭게 생성되어 pid가 0인 자식 프로세스를 의미한다.
6. EACCES
errno.EACCES
Permission denied – 사용 권한이 거부되었습니다.
errno.ENOENT
No such file or directory – 그런 파일이나 디렉터리가 없습니다
그 외의 errno.h에 정의되어 있는 다른 오류 메시지 메크로들은
https://docs.python.org/ko/3/library/errno.html
에서 확인 가능하다.
7. signal은 어디에서 신호를 주고 받는가?
신호는 운영체제가 프로세스에게 신호를 준다.
참고자료 : https://blog.naver.com/shw20319/20147713837
SIG_ERR
signal()에 대한 호출에서의 오류를 표시합니다.
8. STDIN_FILENO
파일 디스크립터 : 시스템으로부터 할당 받은 파일을 대표하는 0이 아닌 정수 값, 프로세스에서 열린 파일의 목록을 관리하는 테이블의 인덱스
<unistd.h>에서 찾아볼 수 있다.
9. getc(stdin)
표준 입력 버퍼에서 문자를 하나 가져오는 함수이다.
stdin은 stdin 스트림에서 입력값을 가져온다는 의미이다.
getc(stdin)과 getchar()는 같은 함수로 볼 수 있다.
getc()는 스트림을 인자로 받지만 getchar()는 스트림 인자를 받지않고 getchar() 이렇게만 사용한다.
getchar()는 getc()를 표준입력 스트림을 사용한 것과 동일하다.
10. 시스템 호출과 라이브러리 함수
시스템 호출 : 커널의 자원을 사용자가 사용할 수 있도록 만들어 놓은 함수들
시스템 호출은 커널이 건네주는 정보를 사용자 모드의 프로그램에 받아 오려면 반드시 사용자 영역에 메모리를 할당해야 한다.
라이브러리 함수 : 사용자들이 많이 사용할 것 같은 기능들을 미리 함수로 만들어 놓은 것.
라이브러리 함수는 메모리를 별도로 할당하지 않아도 된다.
printf()나 scanf()같은 함수가 여기에 속한다.
커널모드와 사용자모드로 수행공간을 나누는 이유는 여러 프로그램을 동시에 실행할 때 프로그램이 다른 프로그램을 방해하거나 충돌이 일어나는 것을 방지하기 위해 구분시켜준다.
커널모드 : 운영체제가 cpu의 제어권을 가지고 운영체제 코드를 실행하는 모드로 이 모드에서는 모든 종류의 명령을 다 실행가능하다.
사용자모드 : 일반 사용자 프로그램이 실행되며 제한적인 명령만이 가능하다.
프로세스는 두 모드들을 실행되는 동안 수없이 옮겨가는 과정을 반복하며 실행된다.
책에서는 시스템 호출과 라이브러리 함수의 예를 malloc()과 sbrk로 설명하였다.
malloc() (라이브러리 함수)는 메모리 할당 함수이다. sbrk(시스템 콜)은 함수를 처리하는 역할을 한다.
커널의 시스템 호출인 sbrk는 프로세스 대신 추가 공간을 할당한다. 이 사이에서 malloc는 사용자 수준에서 공간을 관리한다.
'Computer Science > Operating System' 카테고리의 다른 글
[OS] CPU 스케줄링 (0) | 2023.12.13 |
---|---|
[OS] 컴퓨터의 구조와 성능 향상 (0) | 2023.11.01 |
[OS] Code, Data, Heap, Stack (코드, 데이터, 힙, 스택) (0) | 2023.02.15 |