본문 바로가기

CS/유닉스프로그래밍

유닉스

프로세스

 

* 메모리 레이아웃

 

 

 

* getpid, getppid 시스템콜

#include <unistd.h>
pid_t getpid(void);  // returns : the process ID of the calling process
pid_t getppid(void); // returns : the parent process ID of the calling process

 

* fork 시스템콜

#include <unistd.h>
pid_t fork(void) ; // returns : 0 in child, process ID of child in parent, -1 on error

 

* exec family

#include <unistd.h>
int execl (const char *pathname, const char *arg0, ... /*NULL*/);
int execv (const char *pathname, char *const argv[]);
int execle (const char *pathname, const char *arg0,.../*NULL*/, char *const envp[]);
int execve (const char *pathname, char *const argv[], char *const envp[]);
int execlp (const char *filename, const char *arg0,.../*NULL*/);
int execvp (const char *filename, char *const argv[]);
//All six return : -1 on error, no return on success

l : 마지막 argument로 (char*) 0 넣어줘야함 NULL 문자

 

* close-on-exec flag

parent가 open 한 파일디스크립터가 자동으로 close 되는 것

fcntl(fd, F_SETFD, 10);

 

* exit 라이브러리

#include <stdlib.h>
void exit(int status);
void _Exit(int status);

#include <unistd.h>
void _exit(int status);

종료할 때 정상적으로 종료를 하면 대개 0을 return 

무슨 문제가 생겨서 parent에게 알려야 하면 1이나 2를 넣어서 전달

exit은 library이고, 종료 외에 추가적으로 clean up 작업을 하고 종료함

 

* atexit 라이브러리

#include <stdlib.h>
int atexit(void (*func)(void));
// returns : 0 if ok, nonzero on error

 

* wait 시스템콜

#include <sys/wait.h>
pid_t wait(int *statloc);
//return : child process ID if ok, 0(see later), or -1 on error

 

* waitpid 시스템콜

#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc, int options);
//returns : child process id if ok, 0(see later), or -1 on error

argument의 pid가 -1이라면, 특정 pid를 지정하지 않은 거라서 아무 child의 종료를 기다리는 것임

pid가 0보다 큰 숫자로 지정되면, 특정 pid를 지정한 것이고 그 pid를 가진 child의 종료를 기다리는 것임

pid가 0이면 process group id가 같은, 같은 그룹에 속한 child의 종료를 기다리는 것임

pid가 -1이 아닌 음수면 parent와 다른 process group id에 속한 child의 종료를 기다리는데, 그 child가 속한 gid를 말함

 

WNOHANG은 wait hang block이랑 비슷함 기다리지 말라는 옵션임

기다리지 않고 곧바로 return함, 지정된 child가 즉각 available(exit해서 status를 넘겨주는 것)하지 않으면 그냥 return

 

wait는 parent를 block 하고 무작정 기다리는 것임

waitpid는 WNOHANG 옵션을 주면 block 하지 않게 하고, still running 인지 exit 인지 return 값으로 알 수 있음

child가 running하는 동안 자기가 할 작업을 할 수 있는 시간적 여유를 줌

 

* 매크로

WIFEXITED(status), WEXITSTATUS(status) : 정상종료를 하고 그 status를 알 수 있는 매크로

WIFSIGNALED(status) : status 중 signal을 갖고 있는 것이 있음 그 signal 번호를 알아내는 건 WTERMSIG(status)

WCOREDUMP(status) : 프로세스 종료 시 메모리 내용을 dump 할 수 있는데, 그게 됐는지 알아보는 거

WIFSTOPPED(status) : 프로세스 status가 stop인지 알수 있는 거

WSTOPSIG(status) : stop signal을 알 수 있는 거

WCONTINUED(status) : stop 했다가 resume 했는지 알 수 있는 거

 

*pid

#include <unistd.h>
pid_t getpid(void);		// returns : process ID of calling process
pid_t getppid(void); 		// returns : parent process ID of calling process
uid_t getuid(void);		// returns : real user ID of calling process
uid_t geteuid(void);		// returns : effective user ID of calling process
gid_t getgid(void);		// returns : real group ID of calling process
gid_t getegid(void);		// returns : effective group ID of calling process

 

#include <unistd.h>
pid_t getpgrp(void);		// returns : process group ID of calling process
pid_t getpgid(pid_t pid);	// returns : process group ID if OK, -1 on error
/* getpid(0) == getpgrp() */

 

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid); 	//returns : 0 if ok, -1 on error

 

#include <unistd.h>
pid_t getsid(pid_t pid);	// returns : session leader's process group ID if ok, -1 on error

 

#include <unistd.h>
pid_t setsid(void); 	// returns : process group ID if ok, -1 on error

 

데몬 프로세스 controlling terminal을 갖지 않는 프로세스를 말함

세션 내의 모든 프로세스는 그 세션에 연결된 controlling terminal을 공유한다고 했음

세션 독립해버리면 controlling terminal을 사용할 수 없게 됨

controlling terminal이 없고 표준 출력이 없음. 영원히 돌게 할 수 있음 telnet, ftpd, cron 

 

#include <unistd.h>
int setuid(uid_t uid);
int setgid(gid_t gid);
// Both return : 0 if ok, -1 on error

 

* changing root directory

#include <unistd.h>
int chroot(const char *path);	//return : 0 if ok, -1 on error

 

* nice

$ no hup nice cc prog.c 2>err &
$ exit

하면 프로그램이 되게 오래 돌아버림

2>err은 에러 출력은 2라는 파일에 하라는 말

nice를 붙여서 우선순위를 낮춰줌

no hup은 exit이 나오면 이 프로세스에 signal을 보내서 kill을 하는데, 그 signal을 차단하는거임

그래서 죽지는 않지만 priority를 조금 낮춤

 

- process간 같은 파일 open 시, 디스크립터 파일과 파일테이블 공유 관계
  system call은 kernel의 기능을 service하기 위한 interface이다. 직접 kernel의 코드를 실행하지만 library routine은 user space에 코드가 할당되며 그 안에서 다시 system call을 호출하는 형식으로, 즉 간접적인 방식으로 kernel의 기능을 실행한다.
Open, read, write 등은 system call로서 unbuffered I/O라 한다. 그 이유는 같이 user space내에 buffer를 만들지 않기 때문이다. fopen, fread, fwrite등은 다음과 같은 방식에 의해 user space내에 buffer를 만든다.


시그널

1번 SIGHUP은 SIG HANG UP

nohup nice 해서 long, big 프로그램을 실행할 때, background에서 실행하게 하면 $ exit을 해도 죽지 않음

$ exit을 하면 SIGHUP이 옴 

모든 프로그램에 SIGHUP이 날라가는데, nohup으로 차단되면 안 먹힘

 

2번 SIGINT는 키보드에서 ^C를 하면 날라가는 시그널

 

6번 SIGABRT는 abort 시스템콜을 사용하면 abort를 실행한 프로세스에게 보내는 거

자기가 스스로 죽고자하는 애임 SIGABRT를 받으면 default action이 termination임 

core는 프로세스가 실행하던 당시의 메모리를 말함, core 실행 당시 변수 값 등을 보고 

이 프로세스가 어떻게 작동했는지, 어떤 error가 발생했는지 등 디버깅 용도로 사용함

 

14번 SIGALRM은 alarm 시스템콜을 사용할 때 초 단위의 시간을 사용해서,

이 시간 경과 후 이 프로세스에게 SIGALRM을 보냄 마찬가지로 default action은 termination임

그 외에 유용한 handler를 사용해서 interrupt 방식을 이용하면 죽지 않음

 

17번 SIGCHLD는 그 child를 termination하는 것 

signal을 받아서 종료가 되는 경우 abnormal termination도 있고, stop signal을 받아서 잠시 중단일 수도 있음

이 때 부모 프로세스에게 SIGCHLD 시그널을 커널이 보내줌

부모가 wait하다가 SIGCHLD를 받으면 child에게 termination이나 stop이 됐다는 것을 알아차리고 맞는 action을  취함

 

18번 SIGCONT

stop signal에는 SIGSTOP과 SIGTSTP가 있는데, 이 시그널을 받으면 프로세스가 잠시 stop 함

그럼 언젠가 다시 continue해야하는데, 그런 목적으로 보내는 시그널임

stop된 프로세스에 보내서 suspend에서 실행

 

8번 SIGFPE는 floating point exception 계산 중 오류가 발생할 때 cpu에서 체크하고 커널에게 보내는 시그널임

 

4번 SIGILL도 하드웨어 시그널. machine instruction이 잘못됐을 때, 

cpu에서 이런 명령은 실행할 수 없다고 error 메시지를 보내면 커널이 SIGILL 시그널을 그 프로세스에 보냄

 

11번 SIGSEGV 메모리 참조 오류가 발생 했을 때 사용

c 프로그램에서 포인터를 잘못 사용하면 발생함

danggling reference 참조하는 곳에 할당된 변수가 없는 것임

 

19번 SIGSTOP 20번 SIGTSTP는 비슷한데,

SIGTSTP를 보내면 받은 프로세스는 잠시 중단하고, ^Z로 보낼 수 있음

foreground에다가 돌리다가 prompt가 뜨지 않아서 답답해서 background로 돌리고 싶을 때,

^Z로 SIGTSTP를 보내고, 다시 &로 background에 돌리면 처음부터 안돌려도됨

$ bg명령어로 SIGCONT 보내서 background에 돌게 함

 

10번 SIGUSR1 12번 SIGUSR2는 user define하는 시그널임

 

SIGINT는 core dump를 하지 않고 SIGQUIT은 core dump를 함

SIGTERM은 kill cmd나 kill system call을 통해서 signal을 지정하지 않으면 default로 가는 시그널이고,

SIGKILL은 SIGTERM을 보냈을 때 죽지 않으면 보냄. kill 시스템콜에 -9 붙이면 됨

 

* 시그널 life cycle

시그널은 생성, 전달, 도달해서 pending, 프로세스가 catch, install

 

* 매크로

비정상 종료이면 signal을 받고 종료했는지를 WIFSIGNALED에서 true가 나오면 그럼

stop 시그널을 받아서 잠시 종료돼도 parent가 깨어나는데, WIFSTOPPED가 true가 됨

WIFEXITED는 child가 exit하고 번호를 보내주고, status에 종료 이유에 대한 정보도 있음

WTERMSIG 어떤 시그널을 받았는지, 시그널 번호가 나옴

WCOREDUMP로 core dump 했는지 안했는지 알 수 있음

 

* 시그널 핸들링

대부분 default action이 termination이지만 아닌 예외는 SIGSTOP, SIGTSTP, SIGUSR1, SIGUSR2, SIGCONT

SIGUSR의 default action은 ignore action임 

SIGKILL과 SIGSTOP은 ignore와 catch 둘다 못함. SIGTSTP는 ignore, catch 모두 가능

 

signal handler return 하면 interrupt가 걸린 위치에서 다시 시작함

signal handler에서는 프로세스가 실행한 곳이 어디인지 알수 없음 asynchronous

 

* 프로세스 시그널 마스크

signal mask는 block 할 시그널의 목록을 갖고 있음

프로세스당 시그널 메시지가 attribute처럼 갖고 있는 거임

sigprocmask 시스템콜로 block할 signal을 선택적으로 setting하고 특정 signal을 변경시킬 수 있음

fork 해서 child를 만들면 child는 parent의 signal mask를 상속받음

 

* signal 시스템콜

#include <signal.h>

void signal (* signal(int signo, void (*func)(int)))(int);
// returns : previous disposition of signal if ok, SIG_ERR on error

*func 함수 포인터에 대한 것은 SIG_IGN, SIG_DFL 이렇게 정의되어 있음 

sigaction 인자로 signo와 sighandler가 들어감

 

시그널을 block 시킬 수 있는데 자동으로 시그널을 외부에서 procsigmask로 강제적으로 바꿀 수 있음

 

* signal sets

#include <signal.h>

int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
// return : 0 if ok, -1 on error

int sigismember(const sigset_t *set, int signo);
// returns : 1 if true, 0 if false, -1 on error

전부다 0으로 채우는 게 sigemptyset

sigfillset은 모두 1로 채움

sigaddset은 특정 위치에 set할 필요가 있을 때 사용

sigdelset은 특정 위치를 clear해줌 

sigismember는 해당 위치에 1이 setting이 되어있는지 알 수 있는 매크로임

 

* sigaction 시스템콜

#include <signal.h>
int sigaction(int signo, const struct sigaction *restrict act, struct sigaction *restrict oact);
// returns : 0 if ok, -1 on error

 

struct sigaction{
	void (*sa_handler) (int);
    	sigset_t sa_mask;
        int sa_flags;
        void(*sa_sigaction) (int, siginfo_t *, void *);
}

sa_handler : 앞에서 본 handler와 같음 signo가 인자로 필요함

sa_sigaction : 새로운 action, 인자 3개가 필요함 signo와 추가적인 정보

추가적인 정보에는 시그널을 보낸 사람, 언제 보냈는지 등 시그널을 보낸 상황에 대한 추가적인 정보

sa_mask : 시그널의 catch로 설정된 handler에 들어갈 때 똑같은 시그널을 차단하는 역할

추가적으로 block 시킬 시그널들을 setting 해줌, sig_emptyset, sigfillset, sigaddset, sigdelset 사용

sa_flags : 각종 옵션, 이 옵션에 따라서 다양한 액션 취할 수 있음 SA_RESTART, SA_SIGINFO, 등

SA_SIGINFO가 setting 되어 있으면 sa_sigaction을 사용하고, 아니면 sa_handler 사용

 

 

new action에 null을 넣으면 옛날 action 대신 null로 overwrite해서 default action 

 

* 시그널과 시스템콜

 slow system call이 실행되고 있을 때, 프로세스가 signal을 catch한다면,

마치 정상적인 user process를 돌고 있던 것처럼 interrupt가 됨

시스템콜 실행 중 interrupt 되면, 다시 돌아올 때 정확하게 중단된 위치로 갈 수 없음.

그래서 slow system call을 실행하던 중에 catch를 하면 정확하게 못 돌아가서 return error가 됨

errorno는 EINTER

 

slow system call은 영원히 block 될 수 있는 시스템콜, pipe, terminal device IO, network device IO, pause, IPC 

disk IO는 아님. fast system call 임.

 

4.2 BSD에서 automatic restart를 도입함  sa_flags에 SA_RESTART를 설정

slow system call 수행 중 인터럽트 발생하면, 인터럽트 서비스 루틴 수행하고, 시스템콜을 자동으로 재시작함

option을 setting 해줘야 자동적으로 재시작해주는 거임. ioctl, read, readv, write, writev, wait, waitpid 이런게 있음

 

똑같은 signal이 들어오면 pending 되어 있는데, 여러 개 pending 되어 있어도 대기는 한 개만 되어있음

 

* sigsetjump, siglongjmp 라이브러리

 

 1) 직접호출에 의한 리턴: 0을 리턴

 2) siglongjmp에 의한 리턴: siglongjmp의 val

sigsetjmp는 label을 설정하는 거고, siglongjmp는 goto 하는 거

현재 signal mask를 save하면서 sigsetjmp하는거임

 

#include <setjmp.h>

int sigsetjmp(sigjmp_buf env, int savemask);
//returns : 0 if called directly, nonzero if returning from a call to siglongjmp

void siglongjmp(sigjmp_buf env, int val);

 

Shell같은 경우 명령처리 시 에러가 발생하면 다시 입력모드로 돌아가야 한다.

이 때 에러가 발생한 명령의 처리는 더 이상 진행하지 말아야 한다.

 

setjump, longjump사용하여 더 이상 에러가 발생한 명령어를 실행하지 않고 입력모드로 점프할 수 있다.

 

* sigprocmaks 시스템콜

#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
// returns : 0 if ok, -1 on error

how

SIG_BLOCK은 새로 block할 시그널을 추가하는 거

SIG_UNBLOCK은 반대로 기존 지정된 시그널에서 뭘 뺄지 지정함

SIG_SETMASK는 기존 거를 완전히 clear 하고 set에 지정된 시그널들로만 새로 setting 하라는 말

 

* kill, raise 시스템콜

#include <signal.h>

int kill(pid_t pid, int signo);
int raise(int signo);
//return : 0 if ok, -1 on error

pid가 0보다 크면 특정 프로세스를 가리키는거,

0 이면 보내는 프로세스와 같은 그룹에 있는 프로세스들 모두에게 보냄

-1이 아닌 음수면, 그 수의 절댓값과 같은 그룹 아이디를 가진 프로세스에게 보냄

-1인 경우는 send permission을 가진 모든 프로세스에게 보냄

raise는 자기 자신한테 보내는 거임, 그래서 kill(getpid(),)와 같음

 

* null signal

특정 프로세스에게 kill로 보낼 수는 있는데, 목적이 대상이 된 프로세스가 지금 살아있는지 죽었는지를 확인하는 용도

죽어있고 이미 없으면 -1이 return 됨. ESRCH라는 errno가 setting 되어 있음.

그 프로세스가 살아있다면 0 이 return 됨.

프로세스의 existence를 test하기 위한 용도로 사용한 거인데 이 행동이 atomic 하지 않음

 

* alarm 시스템콜

#include <unistd.h>
unsigned int alarm (unsigned int seconds);
// returns : 0 or number of seconds until previously set alarm

return 값은 0이거나 그전에 settng된 alarm의 시간이 됨.

0은 0초뒤에 곧바로 시그널을 받는 다는 말이 아니고, alarm clock setting 된 시간을 cancel 하고, signal을 받지 않음

 

* pause 시스템콜

#include <unistd.h>
int pause(void);
//returns : -1 with errno set to EINTR

아무것도 안하는 slow systemcall임 suspend 하는 거임

signal catch 해서 핸들러를 실행하고, 핸들러 돌아오면 실패되고 다음걸 실행함

근데 catch 안하면 무조건 죽는거임.  
실패하고 return 함. 

 


PIPE

PIPE로 데이터를 주고 받을 수 있는 텍스트 데이터 스트림 채널을 제공함

파이프도 file로 간주됨, special file로 표현됨 open하지는 않고 read write를 함

 

#include <unistd.h>
int pipe(int filedes[2]);
// returns : 0 if ok, -1 on error

 

filedes[0]은 pipe의 read 쪽이고, filedes[1]은 pipe의 write 쪽

데이터가 들어가는 순서는 바뀌지 않고, lseek도 사용되지 않음, 디스크에서만 쓸 수 있음

 

read를 하려는데 파이프가 비어있다면  written 될 때까지 block 됨 데이터가 있다면 그 데이터를 즉각 받아서 return

write를 하려는데 파이프가 꽉 차있다면 write해야하는 프로세스가 여유공간이 생길 때까지 block 

 

write쪽이 먼저 close하면 read 프로세스는 남은 데이터를 read하고,

pipe가 empty가 되면 마지막에 EOF를 읽고 0을 즉각 반환

read쪽이 먼저 close하면 커널에서 write하는 프로세스에 SIGPIPE 시그널을 보냄

그럼 write하는 프로세스는 SIGPIPE의 default action으로 termination 

 

pipe에 대한 write signal은 slow device system call로 간주함

그래서 이 시스템콜이 실패하면 -1을 return 하고 interrupt

 

* blocking & non-blocking

open 할 때 O_NONBLOCK flag를 설정하면 open 이 block 되는 상황에도 불구하고 block 안됨

곧 바로 return 한다는 뜻임. 성공하든 실패하든 블락안됨

fcntl로 O_NONBLOCK flag 를 turn on 해주면 블락이 안되게 설정할 수 있음

 

fcntl(p[1], F_SETFL, O_NONBLOCK)

p[1] write할 때 block되는데 write하다가 꽉 차면 곧바로 return 하는데 기다리지 않고 return 하면 실패한거라서 -1 
errno은 다시 하라는 걸로 EAGAIN이 세팅이 됨

 

fcntl(p[0], F_SETFL, O_NONBLOCK)

read할 때 블락되는 상황이어도 안 되게 할라면 이럼됨 
절대 블락이 안되는데 비어있기 때문에 곧바로 return 하면 -1이 return 되고 errno는 EAGAIN

 

* popen, pclose 라이브러리

#include <stdio.h>
FILE *popen (const char *cmdstring, const char *type);
// returns : file pointer if ok, NULL on error
int pclose(FILE *fp);
// returns : termination status of cmdstring, or -1 on error

FIFO

f를 공유하는 프로세스랑 공유하지 않는 프로세스간의 unrelated 이런 관계를 unrelated라고 표현함

파이프에다가 read할 때는 not empty인 경우 read함 immediately, return / empty인 경우 block until write

write는 pipe가 not full 이면 write immediately, return / full 인 경우 block until read and make room

 

write가 terminate인 상태에서 pipe가 read하려면 empty일때 return 0 

read가 terminate인 상태에서 write를 하려고 하면 kernel에서 sigpipe를 보내서 default action으로 terminate

 

* mkfifo 시스템콜

#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
// returns : 0 if ok, -1 on error

file 형태의 파이프를 만드는 명령어 mkfifo (2) 시스템콜

read가 없는 상태에서 write하면 pipe가 아니면 sigpipe가 날라옴 그러면 무조건 실패됨 error no는 ENXIO

non block 으로 setting 하면 기다리지 않음 이 경우에는 실패로 리턴 x

 

non block 으로 setting 한 경우, block 될 상황에서도 block 안되고 바로 return  
read only인데 write가 아직 open 안하면 에러 안생기고 0을 return 함 
write only인데 read가 아직 open 안하면 커널에서 sigpipe가 날라오는 심각한 상황이 발생하니까 실패로 return 함

 

쓸데없이 cpu 계속 사용함 이걸 방지하기 위해서 

read only 대신에 rdwr함 rdwr 하면 sendmsg가 없다고 하더라도 write only가 들어 있기 때문에

sendmsg가 아직 없고 fifo 가 empty여도 write가 존재함.


IO multiplexing

receive 할때 서버가 채널이 각각 데이터를 통신하는 채널이 각각 다른 여러개가 있을때 

 

해결하는 방법으로 non block 하는 방법이 있음 blocking 하면 다른데 체크 못함 
non blocking 하면 문제는 뭐냐면 채널이 여러개일때 계속 왔다갔다 하면서 계속 루프를 돌아야함 
cpu time이 100%가 됨 wastiing 됨 그래서 해법이 될수 없음 

 

* select 시스템콜

#include <sys/time.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
			fd_set *exceptfds, struct timeval *timeout);
// returns : count of ready descriptors, 0 on timeout, -1 on error

 

nfd는 파일디스크립터가 아니라 파일디스크립터 갯수임, 엄밀히 갯수는 아니고 가장 높은 파일디스크립터 번호를 말함

파일디스크립터가 3,4 open 되어있다면 nfds는 5가 됨.

readfds는 멀티플렉싱에서 기다리는 read를 하는 파일디스크립터들의 array임 포인터로 되어있음 
writefds는 write하기 위한 파일디스크립터의 array  
exceptfds는 예외처리를 하기 위한거임 에러처리를 위한 파일디스크립터 
timeval은 timeout을 setting 하는거임 무한정 기다릴 수 없기 때문에

 

 timeout 변수가 null 값을 가지면 wait forever영원히 기다리는 거임

 

#include <sys/select.h>

int FD_ISSET (int fd, fd_set *fdset);
// returns : nonzero if fd is in set, 0 otherwise

 

* join

int join (char *com1[], char *com2[]){
  int p[2], status;
  
  switch (fork()){
  case -1: fatal ("1st fork call in join");
  case 0: break;
  default: wait(&status); return(status);
  }
  if (pipe(p) == -1) fatal ("pipe call in join");

  switch (fork()){
  case -1: fatal ("2nd fork call in join");
  case 0:
    dup2 (p[1],1); /*표준 출력이 파이프로 가게 한다*/
    close (p[0]);  /*화일 기술자를 절약한다. */
    close (p[1]);
    execvp (com1[0], com1); /* com1: ls */
    fatal("1st execvp call in join");
  default:
    dup2(p[0], 0); /* 표준 입력이 파이프로부터 오게 한다 */
    close (p[0]);
    close (p[1]);
    execvp (com2[0], com2); /* com2: grep */
    fatal ("2nd execvp call in join");
  }
}
#include <stdio.h>

main()
{
  char *one[4] = {"ls", "-l", "/usr/lib", NULL};
  char *two[3] = {"grep", "∧d", NULL};
  int ret;

  ret = join (one, two);
  printf ("join returned %d\n", ret);
  exit (0);
}

 

* ftok 시스템콜

#include <sys/ipc.h>
key_t ftok(const char *path, int id);
// returns : key if ok, (key_t)-1 on error

리턴되는 키가 유니크하게 유니크한 키가 리턴되도록 해주는 시스템콜임

key에는 key를 주는 세가지 방법이 있음

첫번째는 값을 직접 주는 방법 - 위험함 유니크하게 지정하지 못할 가능성이 높음.

두번째는 ftok로 pathname과 identifier를 넣어서 더 유니크하게 얻는 방법은 있지만 백퍼센트는 아님

세번째로 key 값에 ipc_private 넣으면 return 되는 identifier 값이 무조건 유니크한 값임

 

* ipc get

#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>

int msgget(key_t key, int permflags);
int semget(key_t key, int nsems, int permflags); 
int shmget(key_t key, size_t size, int permflags); 

// returns : identifier if ok, -1 on error

 

ipc_creat랑 ipc_excl 가 동시에 setting 되면 파일이 있으면 get을 안하고 error, 

파일이 없으면 permflag로 만들어서 get 

 

* ipc ctl

#include <sys/msg.h>
#include <sys/sem.h>
#include <sys/shm.h>

int msgctl(int msqid, int command, struct msqid_ds *buf ); 
int semctl(int semid, int semnum, int command,…/*union semun arg*/);
int shmctl(int shmid, int command, struct shmid_ds *buf); 
// returns : 0 if ok, -1 on error

 

stat으로 가져온 정보중에서 일부를 변경할수있는거임 
rm은 remove, id 가 갖고 있는거 remove하는 거임 시스템내에서 지워버리는거임 그 ipc를

 

ipcs [-qms][-a | -bcopt]

ipcs는 현재 ipc 내용이 다 디스플레이 됨  
owner 가 누구고 퍼미션은 뭐고 암튼 어쩌고 저쩌고임 
-s는 세마포어 -q는 msgq  
shm은 -m일거라함


Message queue

단위가 메시지라는 단위로 주고받아서 메시지간 분명한 경계가 있음 

링크드 리스트 형태

struct msqid_ds {		/* <sys/msg.h> */
	struct ipc_perm 	 msg_perm;   /* see ch08_ipc1-p.29 */
	struct msg	*msg_first;  /* ptr to first message on queue */
	struct msg	*msg_last;   /* ptr to last message on queue */
	msglen_t	 msg_cbytes; /* current #bytes on queue */
	msgqnum_t 	 msg_qnum;   /* # of messages on queue */
	msglen_t 	 msg_qbytes; /* max # of bytes on queue */
	pid_t 		 msg_lspid;  /* pid of last msgsnd() */
	pid_t 		 msg_lrpid;  /* pid of last msgrcv() */
	time_t 		 msg_stime;  /* last-msgsnd() time */
	time_t 		 msg_rtime;  /* last-msgrcv() time */
	time_t 		 msg_ctime;  /* last-change time */
};

 

* msgget  시스템콜

#include <sys/msg.h>
int msgget(key_t key, int flag);
// returns : identifier if ok, -1 on error

permission 만족하지 않으면 EACCES 에러

EEXIST 에러는 flag에다가 create하고 exclusive 를 동시에 줄수도 있는데, 그 경우에는 key 가 있으면 open 실패

ENOENT 는 ipc_creat이런게 없다면 지정한 key 가 없으면, 없는데 get하라고 했다고 나오는 에러

ENOSPC 는 메시지큐 최대 갯수 한계에 도달하면 더이상 만들지 못한다고 나오는 에러 메시지

 

* msgsnd 시스템콜

#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);
// returns : 0 if ok, -1 on error

identifier가 가리키는 메시지큐에 메시지를 쓴다는 거임

메시지는 ptr이 가리키는 곳에 메시지가 들어있음 근데 그 사이즈가 nbytes인거임

flag 중 IPC_NOWAIT 기다려야 할 때 기다리지 말라는 뜻 

flag옵션에 nowait 옵션을 넣어주면

msg큐 사이즈가 full이 돼서 메시지큐 send가 블락이 되어야 할 상황에서도 블락이 안된다는 것

send를 못한거고 실패를 해서 error msg

 

* msgrcv 시스템콜

#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);
// returns : size of data portion of message if ok, -1 on error

type == 0 : 메시지 큐에서 첫번  message를 받아온다.

type >   0 : 메시지 큐에서 주어진 type과 동일한 message를 받아온다.

type <   0 : 주어진 type보다 작은 type message들 중 가장 작은 type을 갖는 message를 받아온다. (이를 이용해 우선순위 큐를 만드는 것이 가능하다)

 

* msgctl 시스템콜

#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
// returns : 0 if ok, -1 on error

cmd 에는 IPC_STAT이 명령을 주면, msqid_ds 의 정보를 이 구조에 대한 정보를 포인터로 버퍼에 가져오라는 거임

그 값을 변경하고 save할 수 있어야함 그게 IPC_SET

IPC_RMID는 아이디를 다 쓰고 지우는 명령

 

ipc_creat : 없으면 만들어서 identifier를 return 함 처음에는 없는거를 만들거임 
두번째 부를때는 이미 있어서, 있는거는 그냥 get하면 됨 그래서 얻어진 identifier를 return 해주는 함수임

 

mstat의 msg_qnum : 메시지 안에 있는 총 몇개가 있는지 메시지가 
mstat의 msg_lspid : ls는 last send 임 이 메시지큐에 가장 마지막에 send 한 프로세스 아이디가 뭐냐 이거임


세마포어

#include <sys/sem.h>

struct semid_ds {

  struct ipc_perm  sem_perm;
  struct sem   *sem_base;  /* ptr to array of semphores in set */
  ushort_t    sem_nsems; /* # of semaphores in set */
  time_t  	sem_otime; /* last-semop() time */
  time_t  	sem_ctime; /* last-change time */

};

struct sem {

  ushort_t    semval;    /* semaphore value, nonegative*/
  short  	sempid;    /* PID of last successful semop(), SETVAL, SETALL*/
  ushort_t  semncnt;  /* # awaiting semval > current value */
  ushort_t  semzcnt;  /* # awaiting semval = 0 */

};

 

* semget 시스템콜

#include <sys/sem.h>
int semget(key_t key, int nsems, int flag);
// returns : semaphore identifier if ok, -1 on error

 

* semctl 시스템콜

#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, .. /*union semun arg*/);

 

자료구조가 sem 에 따라 가변적임 union이 가변적이라서 union으로 되어있음

value, buffer, array 매번 cmd에 따라서 사용되는 셋 중의 사용되는 변수가 달라짐

 

union semun {
     int              val;    /* for SETVAL */
     struct semid_ds *buf;    /* for IPC_STAT and IPC_SET */
     unsigned short  *array;  /* for GETALL and SETALL */
};

 

ipc_stat, set, rmid 정보를 가져오고 변경하고 이럴 때 union의 semid_ds를 씀  
명령이 getval setval 이거는 특정 sem 를 지정해줘야함 두번째 argument에 지정함

네번째 argument에는 val을 사용 
밑에는 안봐도 됨
getall setall 은 array를 사용함

 

#include <sys/sem.h>

int removesem(int semid) {
   return semctl(semid, 0, IPC_RMID);
 }

removesem 은 전체 지우는 거라서 특정 세마포어 필요없고 id 하고 rmid 하는거임

 

* semop 시스템콜

#include <sys/sem.h>
int semop(int semid, struct sembuf semoparray[], size_t nops);
// returns : 0 if ok, -1 on error

semid가 가리키는 세마포어 set이 있고 그 안에 세마포어가 여러 개

semop 함수는 atomic함 id로 가리키는 세마포어 set에 여러 개 sem

operation을 나타내는 변수가 있어야하는데 그게 이제 array로 들어감 semoparray

nops는 array에서 지정된 세마포어가 총 몇 개냐

 

sem buf

void setsembuf(struct sembuf *s, int num, int op, int flg) {
   s->sem_num = (short)num;
   s->sem_op = (short)op;
   s->sem_flg = (short)flg;
   return;
}

첫번째 멤버 sem_num : sem set에 있는 몇 번째 sem을 가리키고 있는 지를 나타내는 거임 
두번째 멤버 sem_op : 특정 세마포어 번호에 대해서 wait나 signal을 해야하는데 그걸 지정하는 변수
세번째 멤버 flag : ipc_nowait나 sem_undo


Shared memory

#include <sys/shm.h>
int shmget(key_t key, size_t size, int flag);
// returns : shared memory ID if ok, -1 on error
void *shmat(int shmid, const void *addr, int flag);
// returns : pointer to shared memory segment if ok, -1 on error
int shmdt(void *addr);
// returns : 0 if ok, -1 on error
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
// returns : 0 if ok, -1 on error

* 예시

/* reader -- 화일 읽기를 처리한다. */
#include "share_ex.h"

/* 이들은 두 세마포를 위해 p()와 v()를 정의한다. */
struct sembuf p1 = {0, -1, 0}, p2 = {1, -1, 0};
struct sembuf v1 = {0, 1, 0}, v2 = {1, 1, 0};

void reader (int semid, struct databuf *buf1, struct databuf *buf2){
   for(;;){
       /* 버퍼 buf1으로 읽어들인다. */
       buf1->d_nread = read(0, buf1->d_buf, SIZ);
       /* 동기화 지점 */
       semop (semid, &v1, 1);
       semop (semid, &p2, 1);

       /* writer가 수면하는 것을 피하기 위해 검사한다. */
       if (buf1->d_nread <=0) return;
       buf2->d_nread = read(0, buf2->d_buf, SIZ);
       semop(semid, &v1, 1);
       semop(semid, &p2, 1);
       if(buf2->d_nread <=0) return;
   }
}

 

#include "share_ex.h"

extern struct sembuf p1, p2; /* reader.c에 정의되어 있음. */
extern struct sembuf v1, v2; /* reader.c에 정의되어 있음. */

void writer (int semid, struct databuf *buf1,truct databuf *buf2){
   for(;;){
 	semop (semid, &p1, 1);
 	semop (semid, &v2, 1);

 	if (buf1->d_nread <= 0)
 		return;

 	write (1, buf1->d_buf, buf1->d_nread);

 	semop (semid, &p1, 1);
 	semop (semid, &v2, 1);

 	if (buf2->d_nread <= 0)
 		return;

 	write (1, buf2->d_buf, buf2->d_nread);
   }
}

소켓

#include <sys/socket.h>

//소켓 구현
int socket(int domain, int type, int protocol);
// returns : file (socket) descriptor if ok, -1 on error

//주소 할당, ip port
int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
// returns : 0 if ok, -1 on error

//연결 요청 대기 상태로 진입
int listen(int sockfd, int backlog);
// returns : 0 if ok, -1 on error

//연결 요청 수락
int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
// returns : file(socket) descriptor if ok, 1 on error

//클라이언트 연결 요청
int conncet(int sockfd, const struct sockaddr *addr, socklen_t len);
// returns : 0 if ok, -1 on error

 

* TCP

#include <sys/socket.h>

ssize_t recv (int sockfd, void *buf, size_t nbytes, int flags);
/* returns : length of message in bytes, 0 if no messages are available
			and peer has done an orderly shutdown, or -1 on error */
            
ssize_t send (int sockfd, const void *buf, size_t nbytes, int flags);
// returns : number of bytes sent if ok, -1 on error

 

*UDP

#include <sys/socket.h>

ssize_t recvfrom (int sockfd, void *restrict buf, size_t len, int flags, 
					struct sockaddr *restrict send_addr, socklen_t *restrict addrlen);
/* returns : length of message in bytes, 0 if no messages are available
			and peer has done an orderly shutdown, or -1 on error */
            
ssize_t sendto (int sockfd, const void *buf, size_t nbytes, int flags,
				const struct sockaddr *dest_addr, socklen_t destlen);
// returns : number of bytes sent if ok, -1 on error