
UNIX 운영체제는 계층적인 구조를 가지며 핵심적인 구성 요소는 다음과 같다.
커널(Kernel): 운영 체제의 핵심 부분으로, 하드웨어를 제어하고 시스템 리소스 관리
시스템 호출(System Call): 응용 프로그램이 커널 기능을 사용할 수 있도록 하는 인터페이스
라이브러리 루틴(Library Routines): 시스템 호출을 쉽게 사용할 수 있도록 지원하는 함수 모음
셸(Shell): 사용자가 명령을 입력하고 시스템과 상호작용할 수 있도록 하는 인터페이스
응용 프로그램(Applications): 사용자들이 활용하는 다양한 프로그램과 도구들
UNIX은 다중 사용자 시스템으로, 사용자는 로그인 정보를 입력하여 시스템에 접근할 수 있다.
login name:password:UID:GID:comment:home directory:shelllogin as: jeonjeon@winter.cau.ac.kr's password:셸은 명령어 해석기로, 사용자가 명령을 입력하면 이를 해석하고 실행한다.
$ lsUNIX의 파일 시스템은 계층적인 구조를 가진다.

파일 속성에는 파일 유형(Type), 파일 크기(Size), 소유자(Owner), 권한(Permission), 최종 접근 시간(Access Time) 등 정보가 포함된다.
파일 속성을 확인하는 명령어:
$ ls -al
출력 예제:
drwxr-xr-x 13 obama users 4096 Apr 5 12:39 .
drwxr-xr-x 32 obama users 4096 Nov 15 23:38 ..
-rw-r--r-- 1 obama users 20 Oct 13 2004 Readme
.(현재 directory), ..(상위 directory)/home/obama/testcd obamaUNIX은 file descriptor을 사용하여 파일을 관리한다. 여기서 file descriptor란 커널이 프로세스에 의해 액세스되는 파일을 식별하는데 사용되는 음수가 아닌 정수이다.

프로그램(Program) 이란 디스크에 저장된 실행 파일을 의미한다. 또한, 프로세스(Process) 란 실행 중인 프로그램의 인스턴스이다. 이는 task라고도 불린다. 프로세스를 식별하기 위해 고유하게 할당된 Process ID(PID)가 사용된다.

오류가 발생되면 음수 값이 반환되는 것이 일반적이다. 시스템은 오류 발생 시 errno 변수를 설정하여 추가적인 정보를 제공한다.
여기서 errno은 오류가 발생했을 때 해당 오류의 코드를 저장하는 변수이다. 예를 들어, 파일을 열 때 오류가 발생하면 open() 함수는 -1을 반환하고, errno에는 해당 오류 코드가 저장된다. errno 값은 여러 개의 오류 유형을 가질 수 있으며, 파일이 존재하지 않거나 접근할 수 없는 경우, 각각 다른 errno 값이 설정된다.
errno.h 헤더 파일은 다양한 오류 코드를 정의하고 있다. 모든 errno상수는 E 문자로 시작합니다.
ENOENT, EACCESS
- 예시 코드:
#include <string.h>
char *strerror(int errnum);
/* maps errnum(errno value) into an error message string & returns a pointer to the string */
#include <stdio.h>
void perror(const char *msg); /* outputs the string pointed to by msg */
/* output format “string pointed by msg: error message” */
#include "apue.h"
#include <errno.h>
// strerror와 perror의 사용 예
int main(int argc, char *argv[]) {
fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
errno = ENOENT;
perror(argv[0]);
exit(0);
}
- 결과:
$ ./a.out
EACCES: Permission denied
./a.out: No such file or directory
사용자 ID는 사용자를 식별하는 숫자이다. 시스템 관리자가 사용자 계정을 생성할 때 할당한다. 0번 UID은 슈퍼 유저(Super-user) 또는 root 계정을 의미한다.
그룹 ID는 여러 사용자를 하나의 그룹으로 묶는데 사용된다. 특정 프로젝트나 팀 내에서 동일한 파일 접근 권한을 부여하는데 유용하다.
사용자와 그룹을 ASCII 문자열(예: "john", "admin")로 저장하면, 추가적인 디스크 공간이 필요하거나 문자열 비교 연산의 성능 저하 야기 등 여러 가지 문제가 발생할 수 있다.
#include "apue.h"
int main(void)
{
printf("uid = %d, gid = %d\n", getuid(), getgid());
exit(0);
}
$ ./a.out
uid = 205, gid = 105
시그널은 특정 이벤트가 발생했다고 프로세스에 알리는 메커니즘이다. 예를 들어, '0으로 나누기'의 경우, SIGFPE(부동소수점 예외)가 발생한다. 프로세스의 signal 처리 방식은 다음과 같다.
- 예제: kill
$ ps
PID TTY STAT TIME COMMAND
28478 pp1 S 0:00 -bash
28616 pp1 R 0:02 yes
28617 pp1 R 0:00 ps
$ kill 28616
$ ps
PID TTY STAT TIME COMMAND
28478 pp1 S 0:00 -bash
28624 pp1 R 0:00 ps
[1] + Terminated yes > /dev/null
$
① 캘린더 시간(Calendar Time):
- 1970년 1월 1일 00:00:00 UTC 이후 경과한 초
- time_t 형식으로 저장됨
② 프로세스 시간 (Process Time):
- 특정 프로세스가 사용한 CPU 시간
- clock_t 단위로 측정됨
<참고 자료>
- 광운대학교 컴퓨터정보공학부 시스템프로그래밍 강의, 김태석(2020)