<< 유닉스 개요 >>
앞으로의 스터디 방향은 Unix system call interface와 표준 C 라이브러리가 제공하는 여러 함수들에 대한 심층적인 탐구
시스템 호출 인터페이스(system call interface)
- 파일 열기, 파일 읽기, 새 프로그램 실행, 메모리 영역 할당, 현재 날짜 얻기 등등 Unix system이 Application에 제공하는 다양한 서비스의 총칭
# UNIX 표준들
1980년대 - Unix의 춘추전국시대, 서로 다른 Unix 버전들이 수 없이 등장함.
1980년대 후반 - 여러 Unix 버전들 사이의 차이를 극복하기 위한 국제 표준 제정 진행됨.
예) ANSI 표준, IEEE POSIX 패밀리, X/Open 이식성 지침
참고: ANSI (미국 규격협회 : American National Standards Institute)
IEEE( 전기 전자 기술자 협회 : Institute of Electrical and Electronics Engineers)
POSIX(표준 운영체제 인터페이스 : Portable Operating System Interface)
위 국제 표준들에 대한 주요 구현
- System V Release 4
- 4.4 BSD
# 유닉스 고급 프로그래밍 구성
1. 기본적인 Unix 프로그래밍 개념들과 용어들의 개요 및 소개(제 1 장)
여러 Unix 표준화 노력들과 서로 다른 Unix 구현들의 소개(제 2 장)
2. I/O
비버퍼링 방식 I/O(제 3 장)
파일과 디렉토리의 특성들(제 4 장)
표준 I/O 라이브러리(제 5 장)
표준 시스템 자료 파일들(제 6 장)
3. Process
Unix 프로세스의 환경(제 7 장)
프로세스 제어(제 8 장)
서로 다른 프로세스들 사이의 관계(제 9 장)
signal(제 10 장)
4. 추가적인 I/O
터미널 I/O (제 11 장)
advanced I/O( 제 12 장 )
데몬 프로세스(제 13 장)
5. IPC
프로세스 간 통신 (제 14 장과 제 15 장)
6. 사례 연구 예제들
Database library(제 16 장)
PostScript 프린터와 통신(제 17 장)
모뎀 다이얼 프로그램(제 18 장)
유사 터미널 사용(제 19 장)
# 예제들을 시험하는데 사용한 시스템 들
* System V와 4.x BSD의 여러 버전들의 최근 진화
4.x BSD - University of California(UC Berkeley)의 Computer systems Research group이 개발 및 배포하는 여러 시스템을 통칭
BSD Net 1과 BSD Net 2는 4.x BSD의 오픈 소스 코드 버전
SVRx - AT&T의 System V Release x를 지칭
XPG3 - X/Open Portability Guide Issue 3
ANSI C - C 프로그래밍 언어에 대한 ANSI 표준
POSIX.1 - Unix류 시스템들의 인터페이스에 대한 IEEE 및 ISO 표준
위 표준들에 대한 자세한 내용은 2장. 유닉스 표준과 구현들을 참조
1.2 Unix 아키텍처
* 운영체제(OS)
- 컴퓨터의 하드웨어 자원들을 제어하고 프로그램이 실행될 환경을 제공하는 소프트웨어, 흔히 커널이라고 지칭함
광의적인 의미로 커널 뿐만 아니라 컴퓨터를 유용하게 만들고 컴퓨에 개성을 부여하는 다른 모든 소프트웨어까지 포괄함
* 리눅스 사례
GNU 운영체제가 사용하는 커널, GNU/Linux라고도 부름
* UNIX 운영체제의 아키텍처
1.3 로그인
# 로그인 이름
/etc/passwd 파일에 저장
wonsik:x:10009:10009::/home/wonsik:/bin/bash
로그인 이름:암호화된 패스워드: USER ID : Group ID : 주석 필드 : 홈 디렉토리 : 셀 프로그램
암호화된 패스워드는 특정 파일에 저장
[wonsik@as48-x64 etc]$ ls -ailtr | grep shadow
2367309 -rw------- 1 root root 10243 Jun 30 13:17 shadow-
2367324 -rw------- 1 root root 2333 Jun 30 13:17 gshadow-
2367400 -r-------- 1 root root 2347 Jun 30 13:19 gshadow
2363928 -r-------- 1 root root 10307 Jul 2 15:27 shadow
[wonsik@as48-x64 ~]$ cat /etc/shadow
cat: /etc/shadow: Permission denied
# 셀
사용자의 입력을 읽고 명령들을 수행하는 명령줄 해석기(command-line interpreter)
/etc/passwd의 마지막 필드를 참조하여 사용자 로그인시 해당 쉘을 수행함.
Last login: Wed Jul 9 10:13:43 2014 from 192.168.6.26
################# NOTIFY #############################################
# #
# This server is managed by the technical service division. #
# To free disk space, delete regularly unnecessary files. #
# #
# For this reason, a account of the other division can be deleted. #
# #
######################################################################
[wonsik@as48-x64 ~]$ echo $SHELL
/bin/bash
[wonsik@as48-x64 ~]$
* UNIX 시스템에서 흔히 쓰이는 쉘
[wonsik@as48-x64 bin]$ ls -ailtr | grep sh
689571 -rwxr-xr-x 1 root root 566496 Oct 4 2006 zsh
689555 -rwxr-xr-x 1 root root 217552 Jun 22 2007 ksh
689522 -rwxr-xr-x 1 root root 349568 Feb 7 2008 tcsh
689538 -rwxr-xr-x 1 root root 619104 Nov 19 2008 ash.static
689566 -rwxr-xr-x 1 root root 118592 Nov 19 2008 ash
689558 -rwxr-xr-x 1 root root 773216 Dec 8 2008 bash
689476 lrwxrwxrwx 1 root root 4 Mar 8 2011 sh -> bash
689539 lrwxrwxrwx 1 root root 3 Mar 8 2011 bsh -> ash
689567 lrwxrwxrwx 1 root root 4 Mar 8 2011 csh -> tcsh
Bourne 쉘 - Bell Labs의 Bourne이 최초 개발하였고, 버전 7부터 사용됨.
현존하는 거의 모든 UNIX 시스템이 이 쉘을 제공함.
C 쉘 - Berkeley의 Bill Joy가 최초 개발, 모든 BSD 릴리즈에서 사용가능
AT&T의 System V/386 Release 3.2와 System V Release 4(SVR4)도 이 쉘을 제공
제어 구조가 C 언어와 비슷함.
Bourne 쉘에 없는 추가적인 기능(job control, history, command-line 편집)
Bourne-again 쉘 - 모든 Linux 시스템에서 제공하는 GNU 쉘
Bourne 쉘과 호환, POSIX 준수하여 설계
C 쉘과 Korn 쉘의 기능들도 지원
TENEX C 쉘 - C쉘의 개선판
명령 완성 같은 여러 기능들을 TENEX 운영체제(1972년 Bolt Beranek and Newman사가 개발)
C쉘의 대체용을 많이 쓰임.
Korn 쉘 - Bourne 쉘의 상위 버전으로 AT&T의 David Korn이 제작함.
C쉘보다 더 많은 기능이 포함.
모든 UNIX 쉘 중의 끝판왕
1.4 파일과 디렉토리
# 파일 시스템
디렉토리들과 파일들을 계층적으로 배치한 형태
Unix 파일시스템의 모든 항목 이름은 '/'(루트 디렉토리) 인 디렉토리에서 시작
디렉토리 - 디렉토리내 파일의 이름과, 그 파일의 특성들을 서술하는 정보를 담은 구조체 등을 담은 하나의 파일
[wonsik@as48-x64 ~]$ ls -ailtr | grep apue
3277648 drwxr-xr-x 32 wonsik wsalti 4096 Jul 7 17:27 apue
[wonsik@as48-x64 ~]$ vi apue
" Sorted by name (.bak,~,.o,.h,.info,.swp,.obj at end of list)
"= /home/wsalti/apue/
../
advio/
call/
calld/
daemons/
datafiles/
db/
environ/
exercises/
file/
include/
파일의 특성 - 파일의 종류(정규 파일 or 디렉토리), 파일의 크기, 파일의 소유자, 파일에 대한 권한, 파일이 마지막으로 수정된 일시
# 파일 이름
파일 이름으로 사용하지 못하는 문자는 단 2개, 보통 인쇄 가능한 문자를 파일 이름으로 사용
'/' - 경로이름을 구성하는 파일이름들을 구분하는 용도
널(null) 문자 - 경로 이름의 끝을 표시하는 용도
[wonsik@as48-x64 ~]$ mkdir new_dir
[wonsik@as48-x64 ~]$ ls -ailtr new_dir
total 8
3227649 drwx------ 37 wonsik wsalti 4096 Jul 9 10:43 .. <--- 부모 디렉토리 지칭
3277647 drwxr-xr-x 2 wonsik wsalti 4096 Jul 9 10:43 . <--- 현재 디렉토리 지칭
루트 디렉토리에서 .. 와 . 은 동일함.
[wonsik@as48-x64 ~]$ cd /
[wonsik@as48-x64 /]$ ls -ailtr
2 drwxr-xr-x 37 root root 4096 Jul 3 09:20 ..
2 drwxr-xr-x 37 root root 4096 Jul 3 09:20 .
[wonsik@as48-x64 /]$ cd ..
[wonsik@as48-x64 /]$ pwd
/
# 경로 이름
/home/wonsik/altibase_home/trc <-- 절대 경로
[wonsik@as48-x64 trc]$ cd ../../ <-- 상대 경로 지정을 통해 change directory 수행
예제)
[wonsik@as48-x64 file]$ cat ls1.c
#include "../include/apue.h"
#include <dirent.h>
int
main(int argc, char *argv[])
{
DIR *dp;
struct dirent *dirp;
if (argc != 2) <--- 프로그램 인수 갯수 2가 아니면 에러메세지 출력
err_quit("usage: ls directory_name");
if ((dp = opendir(argv[1])) == NULL) <--- 두번째 인수로 지정된 디렉토리를 open
err_sys("can't open %s", argv[1]);
while ((dirp = readdir(dp)) != NULL)
printf("%s\n", dirp->d_name); <--- dirent 구조체에 포함된 d_name 속성 출력
closedir(dp);
exit(0);
}
[wonsik@as48-x64 file]$ cc -o ls1 ls1.c ../lib/error.c
[wonsik@as48-x64 file]$ ./ls1 .
hole.c
.
mycd.c
hello.c
longpath.c
access.c
linux.mk
zap.c
ftw4.c
solaris.mk
macos.mk
fileflags.c
ls1
filetype.c
changemod.c
error.c
a.out
uidgid.c
..
ls1.c
seek.c
freebsd.mk
testerror.c
devrdev.c
unlink.c
cdpwd.c
umask.c
Linux의 경우 기본적으로 'gcc' 라는 GNU C 컴파일러를 사용한다.
cc는 gcc로 링크되어 있음.
[wonsik@as48-x64 file]$ cd /usr/bin
[wonsik@as48-x64 bin]$ ls -ailtr cc
182546 lrwxrwxrwx 1 root root 3 Mar 8 2011 cc -> gcc
opendir()은 DIR 구조체에 대한 포인터 반환하고, readdir()에서 인자값으로 사용
readdir()은 하나의 디렉토리 항목을 대표하는 dirent 구조체를 가리키는 포인터 반환
while 문으로 더이상 읽을 디렉토리 항목이 없을 경우 null을 반환
struct dirent {
long d_ino;
__kernel_off_t d_off;
unsigned short d_reclen;
char d_name[256]; /* We must not include limits.h! */
};
# 작업 디렉토리
현재 작업 디렉토리(CWD, Current working directory)라고도 부름.
프로세스내에서 chdir()를 이용해 작업디렉토리 변경 가능
/home/wonsik/altibase_home/trc
위 경로이름은 절대 경로를 나타내가, trc가 디렉토리인지, 파일인지 알 수 없다.
# 홈 디렉토리
사용자가 시스템에 로그인하면 사용자의 홈 디렉토리(Home directory)가 작업 디렉토리로 설정
Last login: Wed Jul 9 10:29:21 2014 from 192.168.6.26
################# NOTIFY #############################################
# #
# This server is managed by the technical service division. #
# To free disk space, delete regularly unnecessary files. #
# #
# For this reason, a account of the other division can be deleted. #
# #
######################################################################
[wonsik@as48-x64 ~]$ echo $HOME
/home/wonsik
[wonsik@as48-x64 ~]$
1.5 입력과 출력
# 파일 서술자(file descriptor)
특정 프로세스가 접근하는 파일들을 식별하기 위해 커널이 사용하는 값
보통 음이 아닌 작은 정수 값임.
프로세스가 기존 파일을 열거나, 새 파일을 생성하면 커널은 해당 파일에 대한 FD를 프로세스에게 반환함.
# 표준 입력, 표준 출력, 표준 오류
표준 출력에 대한 재지정
ls > file.list
# 비버퍼링 입출력(unbuffered I/O)
open()
read()
write()
lseek() <-- file에 대한 read/write를 수행할 offset 값 반환
close()
위 함수들은 File descriptor를 인자값으로 받아 작동
예제)
[wonsik@as48-x64 mycat]$ cat mycat.c
#include "../include/apue.h"
#define BUFFSIZE 4096
int
main(void)
{
int n;
char buf[BUFFSIZE];
while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
exit(0);
}
[wonsik@as48-x64 mycat]$ gcc -o mycat mycat.c ../lib/error.c
[wonsik@as48-x64 mycat]$ ./mycat
Hi~ My name is wonsik.
Hi~ My name is wonsik.
I'm a handsome guy.
I'm a handsome guy.
[wonsik@as48-x64 include]$ cat apue.h | grep "\#include"
#include <sys/types.h> /* some systems still require this */
#include <sys/stat.h>
#include <sys/termios.h> /* for winsize */
#include <sys/ioctl.h>
#include <stdio.h> /* for convenience */
#include <stdlib.h> /* for convenience */
#include <stddef.h> /* for offsetof */
#include <string.h> /* for convenience */
#include <unistd.h> /* for convenience */
#include <signal.h> /* for SIG_ERR */
unistd.h 헤더파일과 STDIN_FILENO, STDOUT_FILENO 상수는 POSIX 표준의 일부임.
/* Standard file descriptors. */
#define STDIN_FILENO 0 /* Standard input. */
#define STDOUT_FILENO 1 /* Standard output. */
#define STDERR_FILENO 2 /* Standard error output. */
extern ssize_t read (int __fd, void *__buf, size_t __nbytes);
read한 byte들의 개수를 반환함
파일 끝에 도달하면 0을 반환
오류 발생시 -1 반환
extern ssize_t write (int __fd, __const void *__buf, size_t __n);
byte단위로 지정된 FD에 write함.
[wonsik@as48-x64 mycat]$ ./mycat > data
Hi~ My name is wonsik.
Good morning~
<--- CTRL + C 로 종료
[wonsik@as48-x64 mycat]$
[wonsik@as48-x64 mycat]$ ./mycat > data
Hi~ My name is wonsik.
Good morning~ <--- CTRL + D 로 종료
[wonsik@as48-x64 mycat]$
# 표준 I/O 함수들
비버퍼링 방식 I/O 함수들에 대한 버퍼링 방식 인터페이스를 제공
BUFFSIZE 상수 같은 최적의 버퍼 크기 고민할 필요 없음
입력을 줄단위로 처리함.
fgets() - 하나의 줄 전체를 읽어 들임.
read() - 지정된 개수의 바이트를 읽어들임
가장 흔히 쓰이는 표준 I/O 함수는 printf(), stdio.h 헤더파일에 정의됨
extern int printf (__const char *__restrict __format, ...);
예제)
[wonsik@as48-x64 mycat]$ cat getcputc.c
#include "../include/apue.h"
int
main(void)
{
int c;
while ((c = getc(stdin)) != EOF)
if (putc(c, stdout) == EOF)
err_sys("output error");
if (ferror(stdin))
err_sys("input error");
exit(0);
}
[wonsik@as48-x64 mycat]$ cc -o getcputc getcputc.c ../lib/error.c
[wonsik@as48-x64 mycat]$ ./getcputc
Hi~ My name is wonsik.
Hi~ My name is wonsik.
Good morning~
Good morning~
extern int getc (FILE *__stream);
getc() - 한번에 하나의 문자를 읽음, 마지막 바이트를 읽으면 상수 EOF 반환
extern int putc (int __c, FILE *__stream);
putc() - 문자 단위로 출력
extern struct _IO_FILE *stdin; /* Standard input stream. */
extern struct _IO_FILE *stdout; /* Standard output stream. */
1.6 프로그램과 프로세스
프로그램 : 디스크의 디렉토리에 있는 실행 파일(executable file)
exec 관련 함수들 중 하나를 호출하면 커널은 실행파일의 내용을 메몰로 읽어들여 실행함.
# 프로세스와 프로세스 ID
프로세스 : 프로그램의 인스턴스 화
모든 프로세스는 각자 하나의 고유한 수치 식별자(프로세스 ID)를 가지는 것을 보장
프로세스 ID는 항상 음이 아닌 정수
예제)
#include "../include/apue.h"
int
main(void)
{
printf("hello world from process ID %d\n", getpid());
exit(0);
}
[wonsik@as48-x64 file]$ cc -o hello hello.c ../lib/error.c
[wonsik@as48-x64 file]$ ./hello
hello world from process ID 21433
# 프로세스 제어
fork()
exec()
waitpid()
예제)
[wonsik@as48-x64 proc]$ cat shell1.c
#include "../include/apue.h"
#include
int
main(void)
{
char buf[MAXLINE]; /* from apue.h */
pid_t pid;
int status;
printf("%% "); /* print prompt (printf requires %% to print %) */
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0; /* replace newline with null */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */ <----- 자식 프로세스가 호출한 fork()는 0을 반환한다.
execlp(buf, buf, (char *)0); <----- execlp()는 null로 끝나는 문자열을 인수로 받는다.
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
[wonsik@as48-x64 proc]$ cc -o shell1 shell1.c ../lib/error.c
[wonsik@as48-x64 proc]$ ./shell1
% date
Wed Jul 9 13:37:53 KST 2014
extern __pid_t waitpid (__pid_t __pid, int *__stat_loc, int __options);
인수로 받은 pid의 종료를 기다린다.
해당 프로세스가 종료되었을 때 status 변수에 종료 상태를 저장함.
# 스레드와 스레드 ID
스레드 : 프로세스의 제어 가닥, 한 시점에서 실행되는 기계어 명령들의 한 집합
한 프로세스 내 모든 스레드는 동일한 주소 공간, 파일 서술자들, 프로세스 관련 특 성 공유
여러 쓰레들이 동일한 메모리 공간을 공유하기 때문에 공유 자료에 대한 동기화 필요
쓰레드 ID가 존재하지만, 프로세스내에서만 유효함.
1.7 오류 처리
보통 함수 실행 도중 오류가 발생하면 음수를 반환,
하지만 특정 객체를 가리키는 포인터를 반환하는 함수일 경우 NULL 포인터를 반환
에러에 대한 추가적인 정보를 뜻하는 값을 errno 라는 정수 개체에 설정
[wonsik@as48-x64 asm-x86_64]$ cat errno.h
#ifndef _X8664_ERRNO_H
#define _X8664_ERRNO_H
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Arg list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
#define EDEADLK 35 /* Resource deadlock would occur */
#define ENAMETOOLONG 36 /* File name too long */
#define ENOLCK 37 /* No record locks available */
#define ENOSYS 38 /* Function not implemented */
#define ENOTEMPTY 39 /* Directory not empty */
#define ELOOP 40 /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define ENOMSG 42 /* No message of desired type */
#define EIDRM 43 /* Identifier removed */
#define ECHRNG 44 /* Channel number out of range */
#define EL2NSYNC 45 /* Level 2 not synchronized */
#define EL3HLT 46 /* Level 3 halted */
#define EL3RST 47 /* Level 3 reset */
#define ELNRNG 48 /* Link number out of range */
#define EUNATCH 49 /* Protocol driver not attached */
#define ENOCSI 50 /* No CSI structure available */
#define EL2HLT 51 /* Level 2 halted */
#define EBADE 52 /* Invalid exchange */
#define EBADR 53 /* Invalid request descriptor */
#define EXFULL 54 /* Exchange full */
#define ENOANO 55 /* No anode */
#define EBADRQC 56 /* Invalid request code */
#define EBADSLT 57 /* Invalid slot */
#define EDEADLOCK EDEADLK
#define EBFONT 59 /* Bad font file format */
#define ENOSTR 60 /* Device not a stream */
#define ENODATA 61 /* No data available */
#define ETIME 62 /* Timer expired */
#define ENOSR 63 /* Out of streams resources */
#define ENONET 64 /* Machine is not on the network */
#define ENOPKG 65 /* Package not installed */
#define EREMOTE 66 /* Object is remote */
#define ENOLINK 67 /* Link has been severed */
#define EADV 68 /* Advertise error */
#define ESRMNT 69 /* Srmount error */
#define ECOMM 70 /* Communication error on send */
#define EPROTO 71 /* Protocol error */
#define EMULTIHOP 72 /* Multihop attempted */
#define EDOTDOT 73 /* RFS specific error */
#define EBADMSG 74 /* Not a data message */
#define EOVERFLOW 75 /* Value too large for defined data type */
#define ENOTUNIQ 76 /* Name not unique on network */
#define EBADFD 77 /* File descriptor in bad state */
#define EREMCHG 78 /* Remote address changed */
#define ELIBACC 79 /* Can not access a needed shared library */
#define ELIBBAD 80 /* Accessing a corrupted shared library */
#define ELIBSCN 81 /* .lib section in a.out corrupted */
#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
#define ELIBEXEC 83 /* Cannot exec a shared library directly */
#define EILSEQ 84 /* Illegal byte sequence */
#define ERESTART 85 /* Interrupted system call should be restarted */
#define ESTRPIPE 86 /* Streams pipe error */
#define EUSERS 87 /* Too many users */
#define ENOTSOCK 88 /* Socket operation on non-socket */
#define EDESTADDRREQ 89 /* Destination address required */
#define EMSGSIZE 90 /* Message too long */
#define EPROTOTYPE 91 /* Protocol wrong type for socket */
#define ENOPROTOOPT 92 /* Protocol not available */
#define EPROTONOSUPPORT 93 /* Protocol not supported */
#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT 96 /* Protocol family not supported */
#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
#define EADDRINUSE 98 /* Address already in use */
#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
#define ENETDOWN 100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET 102 /* Network dropped connection because of reset */
#define ECONNABORTED 103 /* Software caused connection abort */
#define ECONNRESET 104 /* Connection reset by peer */
#define ENOBUFS 105 /* No buffer space available */
#define EISCONN 106 /* Transport endpoint is already connected */
#define ENOTCONN 107 /* Transport endpoint is not connected */
#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS 109 /* Too many references: cannot splice */
#define ETIMEDOUT 110 /* Connection timed out */
#define ECONNREFUSED 111 /* Connection refused */
#define EHOSTDOWN 112 /* Host is down */
#define EHOSTUNREACH 113 /* No route to host */
#define EALREADY 114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE 116 /* Stale NFS file handle */
#define EUCLEAN 117 /* Structure needs cleaning */
#define ENOTNAM 118 /* Not a XENIX named type file */
#define ENAVAIL 119 /* No XENIX semaphores available */
#define EISNAM 120 /* Is a named type file */
#define EREMOTEIO 121 /* Remote I/O error */
#define EDQUOT 122 /* Quota exceeded */
#define ENOMEDIUM 123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED 125 /* Operation Cancelled */
#define ENOKEY 126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED 129 /* Key was rejected by service */
#endif
POSIX와 ISO C에서는
errno가 정수 형식의 수정 가능한 왼쪽 값(lvalue)으로 확장되는 하나의 기호라고 정의함.
이 왼쪽 값이라고 하는 것은 에러 번호를 담은 하나의 정수 변수 일수도있고,
오류 번호를 가리키는 포인터를 돌려주는 함수 일수도 있다.
extern int errno;
쓰레드 환경에서는 쓰레드들이 프로세스 주소 공간을 공유하기 때문에
한 쓰레드가 다른 쓰레드의 errno값을 건드리는 일을 차단해줘야 하기 때문에
각 쓰레드마다 자신만의 local errno 복사본을 둘 필요가 있다.
리눅스에서는 errno를 아래와 같이 정의하여 다중 쓰레드 접근 지원
/* Function to get address of global `errno' variable. */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));
# if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value. */
# define errno (*__errno_location ())
* 주의해야 할 2가지 규칙
1) 한 루틴에서 오류가 발생하지 않았다면 errno값이 해제되는 일은 결코 없으므로,
함수가 오류를 뜻하는 값을 반환한 경우에만 errno 값을 조사해야 함.
2) 어떤 함수도 errno를 0으로 설정하지 않으며, errno.h에도 상수 값이 0인 항목은 없다.
* 오류 메시지 출력 관련 함수 리스트
#include
/* Return a string describing the meaning of the `errno' code in ERRNUM. */
extern char *strerror (int __errnum) __THROW;
__errnum 값(보통은 errno의 값)에 해당하는 오류 메시지 문자열을 가리키는 포인터를 반환
/* Print a message describing the meaning of the value of errno.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern void perror (__const char *__s);
errno의 현재 값에 해당하는 오류 메시지를 표준 오류 스트림에 출력
예제)
[wonsik@as48-x64 file]$ cat testerror.c
#include "../include/apue.h"
#include
int
main(int argc, char *argv[])
{
fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
errno = ENOENT;
perror(argv[0]);
exit(0);
}
[wonsik@as48-x64 file]$ cc -o testerror testerror.c ../lib/error.c
[wonsik@as48-x64 file]$ ./testerror
EACCES: Permission denied
./testerror: No such file or directory
# 오류 복구
치명적 오류 - 복구 작용이 없는 오류, 오류 메시지 화면 표시, 로그 파일 기록 후 프로그램 종료
비치명적 오류
자원 관련 비치명적 오류 - EAGAIN, ENFILE, ENOBUFS, ENOSPC, ENOSR, WEOULDBLOCK, ENOMEM. EBUSY, EINTR
잠시 시간을 둔 후에 다시 시도
어떤 오류가 복구 가능하지 결정하는 것은 궁극적으로 application 개발자의 몫임.
1.8 사용자 식별
# 사용자 ID
/etc/passwd 파일의 UID 값을 이용해 사용자를 식별함.
UID가 0인 사용자를 루트 또는 슈퍼 사용자(superuser)로 함
root:x:0:0:root:/root:/bin/bash
# 그룹 ID
/etc/passwd에 포함된 사용자의 group ID, 사용자 이름이 배정될 때 같이 배정됨.
한 사용자가 여러 그룹에 소속될 수 있음.
[wonsik@as48-x64 file]$ cat /etc/group | grep ws
wsalti:x:10009:
[wonsik@as48-x64 file]$ cat /etc/passwd | grep wonsik
wonsik:x:10009:10009::/home/wonsik:/bin/bash
권한체크시 UID와 Group ID를 사용하는 이유는
디스크 상의 모든 파일에 대해 파일시스템은 파일 소유자의 UID와 GID를 저장한.
이 값들이 각각 2 byte 정수인 경우 단지 4 byte만 저장하면 됨.
그러나 ASCII 로그인 이름과 그룹이름을 저장하면 그보다 많은 용량이 필요함.
권한 체크시 문자열을 비교하는 것이 정수를 비교하는 것보다 더 느림.
예제)
[wonsik@as48-x64 file]$ cat uidgid.c
#include "../include/apue.h"
int
main(void)
{
printf("uid = %d, gid = %d\n", getuid(), getgid());
exit(0);
}
[wonsik@as48-x64 file]$ cc -o uidgid uidgid.c ../lib/error.c
[wonsik@as48-x64 file]$ ./uidgid
uid = 10009, gid = 10009
# 추가 그룹 ID
Unix system에서는 추가적으로 group에 속할 수 있게 함.
4.2 BSD에서부터 이런 기능이 적용됐으며, 최대 16개 추가적인 그룹에 속할 수 있다.
POSIX 표준을 만족하는 시스템은 프로세스당 적어도 8개의 추가 그룹들을 지원해 함.
1.9 Signal (신호)
어떠한 조건이 발생했음을 프로세스에게 알려주는 메커니즘
ex) SIGFPE (FPE : floating-point exception) - 프로세스가 0으로 나누기 연산을 수행한 경우
* signal을 받은 프로세스가 할 수 있는 일 3가지
1) signal을 그냥 무시함.
2) 기본(default) 작용이 일어남 - 0으로 나누기 조건의 기본 작용은 프로세스 종료
3) 신호 발생시 호출된 함수 제공
* signal을 발생하는 조건( signal을 보내는 프로세스의 사용자가 signal을 받는 프로세스의 소유자, 또는 root)
인터럽트 키 : DELETE 키나 CTRL+C
중지 키 : quit key, CTRL + /
kill 함수 호출
signal.h 헤더 파일
#define SIGHUP 1
#define SIGINT 2
#define SIGQUIT 3
#define SIGILL 4
#define SIGTRAP 5
#define SIGABRT 6
#define SIGIOT 6
#define SIGBUS 7
#define SIGFPE 8
#define SIGKILL 9
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGPIPE 13
#define SIGALRM 14
#define SIGTERM 15
#define SIGSTKFLT 16
#define SIGCHLD 17
#define SIGCONT 18
#define SIGSTOP 19
#define SIGTSTP 20
#define SIGTTIN 21
#define SIGTTOU 22
#define SIGURG 23
#define SIGXCPU 24
#define SIGXFSZ 25
#define SIGVTALRM 26
#define SIGPROF 27
#define SIGWINCH 28
#define SIGIO 29
#define SIGPOLL SIGIO
/*
#define SIGLOST 29
*/
#define SIGPWR 30
#define SIGSYS 31
#define SIGUNUSED 31
/* These should not be considered constants from userland. */
#define SIGRTMIN 32
예제)
[wonsik@as48-x64 proc]$ cat shell2.c
#include "../include/apue.h"
#include
static void sig_int(int); /* our signal-catching function */
int
main(void)
{
char buf[MAXLINE]; /* from apue.h */
pid_t pid;
int status;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal error");
printf("%% "); /* print prompt (printf requires %% to print %) */
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = 0; /* replace newline with null */
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
execlp(buf, buf, (char *)0);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
void
sig_int(int signo)
{
printf("interrupt\n%% ");
}
[wonsik@as48-x64 proc]$ cc -o shell2 shell2.c ../lib/error.c
[wonsik@as48-x64 proc]$ ./shell2
% ls
a.out echoarg.c fork1.c linux.mk pruids.c shell2 system.c systest3.c test1.c vfork3.c
awkexample exec1.c fork2.c macos.mk shell1 shell2.c systest1.c tellwait1.c times1.c wait1.c
echoall.c exec2.c freebsd.mk pracct.c shell1.c solaris.mk systest2.c tellwait2.c vfork1.c zombie.c
% date
Wed Jul 9 15:45:33 KST 2014
% who
neonojh tty2 Jul 5 14:52
hsyang pts/1 Jul 9 13:14 (192.168.6.25)
hyunmo.k pts/5 Jul 9 13:20 (192.168.6.10)
wonsik pts/19 Jul 9 10:13 (192.168.6.26)
wonsik pts/20 Jul 9 11:08 (192.168.6.26)
niceguy pts/24 Jul 8 15:00 (192.168.6.60)
mehee.ch pts/8 Jul 9 13:24 (192.168.6.21)
mbw pts/14 Jul 9 09:46 (192.168.6.54)
khhan pts/10 Jul 8 14:52 (192.168.6.59)
wlgml337 pts/7 Jul 9 09:14 (192.168.6.2)
omegaman pts/11 Jul 9 09:18 (192.168.6.4)
wlgml337 pts/3 Jul 9 09:42 (192.168.6.2)
bluethem pts/16 Jul 9 09:49 (192.168.6.7)
hanulcat pts/17 Jul 9 10:04 (192.168.6.17)
hanulcat pts/18 Jul 9 10:10 (192.168.6.17)
wlgml337 pts/21 Jul 9 10:36 (192.168.6.2)
likepd80 pts/22 Jul 9 10:42 (192.168.6.187)
guri pts/26 Jul 9 11:25 (192.168.6.40)
wlgml337 pts/6 Jul 9 13:29 (192.168.6.2)
wlgml337 pts/2 Jul 9 13:55 (192.168.6.38)
cheol pts/9 Jul 9 13:57 (192.168.6.45)
gom pts/13 Jul 9 14:29 (192.168.6.13)
wlgml337 pts/15 Jul 9 14:42 (192.168.6.2)
% interrupt <---- CTRL+C 키 누름
1.10 시간 값
* 2 종류의 시간 값
1) 달력 시간(calendar time)
협정세계시(Coordinated Universal Time, UTC) 기준으로 1970년 1월 1일 00:00:00 시점을 기준으로 그로부터 흐른 초들의 개수를 센 건임.
파일의 최종 변경 일자를 기록하는데 쓰임. 시스템 자료형은 time_t
2) 프로세스 시간(process time)
CPU 시간이라고 함. 프로세스가 CPU를 사용한 시간을 측정한 값
측정 단위는 clock tick 수
시스템 자료형은 clock_t
[wonsik@as48-x64 ~]$ time dd if=/dev/zero of=dummy bs=8k count=131072
131072+0 records in
131072+0 records out
real 0m4.648s <--- clock 시간: 프로세스가 실행된 시간
user 0m0.023s <--- user cpu 시간 : cpu가 user mode에서 수행한 시간
sys 0m2.499s <--- system cpu 시간 : read나 write같은 시스템 서비스를 수행한 경우 커널 안에서 소비된 시간
user + sys를 합친 것이 CPU 시간이라고 함.
1.11 System Call과 Library 함수
모든 운영체제는 application 이 커널에게 서비스들을 요청할 수 있는 service point 제공한다.
UNIX에서는 이런 service point들을 system call(시스템 호출) 이라고 부른다.
이런 system call은 C 언어 형태로 정의됨.
* 각 OS별 system call 개수
Research UNIX System 버전 7 - 약 50개
4.4 BSD - 약 110개
SVR4 - 약 120개
Linux - 240 ~ 260 개
FreeBSD - 약 320개
system call은 대체 불가능,
Library 함수는 다른 함수로 대체 가능
* malloc 사례
메모리 할당을 처리하는 Unix System call인 sbrk()는 프로세스의 주소 공간을 지정된 바이트 수만큼 늘리거나 줄임.
해당 공간을 관리하는 것은 프로세스 책임
malloc은 특정 종류의 할당 방법을 구현한 것이고,필요에 따라 malloc의 작동 방식을 사용자가 직접 수정 가능
ex) 페이스북에서 개발한 JEMALLOC, 구글에서 개발한 TCMALLOC
* malloc 함수와 sbrk 시스템 호출의 분리
* 책임의 명확한 분리
커널 안의 시스템 호출( sbrk() )은 프로세스를 위해 추가적인 주소 공간을 할당하고,
malloc은 사용자 수준에서 그 공간을 관리함.
application의 필요에 따라 system call을 직접 호출할 수도 있고, library 함수를 호출 수 있다.
library 함수들도 결국엔 system call을 호출한다.
* C 라이브러리 함수와 시스템 호출의 차이
system call은 제한된 인터페이스 제공하는 반면, library 함수는 좀더 다양하고 편리한 인터페이스 제공함.
댓글 없음:
댓글 쓰기