1. fork <unistd.h>
fork 함수를 호출하는 프로세스는 부모 프로세스가 되고 새롭게 생성되는 프로세스는 자식 프로세스가 된다.
fork 함수에 의해 생성된 자식 프로세스는 부모 프로세스의 메모리를 그대로 복사하여 가지게 되며, fork 함수 호출 이후 코드부터 부모 프로세스와 자식 프로세스는 각자의 메모리를 사용하여 실행한다.
fork 함수는 부모 프로세스에게는 자식프로세스의 PID를 반환하며 자식 프로세스에게는 0을 반환하므로 이 값을 기준으로 별개의 명령을 적용할 수 있다.
출처 : codetravel.tistory.com/23
fork 함수 사용하여 프로세스 생성
프로세스를 생성하고자 할 때 fork 함수를 사용하면 됩니다. fork 함수를 호출하는 프로세스는 부모 프로세스가 되고 새롭게 생성되는 프로세스는 자식 프로세스가 됩니다. fork 함수에 의해 생성
codetravel.tistory.com
2. exit <stdlib.h>
일반적으로 함수를 종료할 때 사용하는 return과 exit()은 시스템 입장에서 보았을 때 동일하다.
또한 exit()와 _exit() (유닉스에서 _Exit()와 동일)의 차이점은 exit()함수는 atexit() 함수로 등록한 종료 핸들러가 있다면 이 핸들러를 모두 처리하고, 표준 입출력 스트림을 닫는 작업을 수행한 후 커널의 종료 작업을 실행하지만 _exit() 함수는 바로 커널에서 종료작업을 실행한다는 것입니다. 여기서 커널 종료 작업이라고 하면 프로세스가 사용하던 메모리를 해제하고 열어놓았던 파일의 descriptor등 을 닫는 작업 등을 의미한다.
자식 프로세스가 비정상적으로 종료되었을 경우에는 커널에서 비정상 종료 상태를 별도로 설정하게 된다. 부모 프로세스는 자식의 종료 상태(정상/비정상)를 wait() 함수 또는 waitpid() 함수로 얻을 수 있다.
3. wait( ) <sys/wait.h>
pid_t wait(int *statloc);
return : 프로세스 ID 반환
실패시 : -1
부모 프로세스가 fork() 함수를 사용하여 자식 프로세스를 생성하였을 때, fork() 함수가 리턴되는 시점부터 2개의 프로세스가 동작한다. 부모 프로세스에서 자식 프로세스의 종료 상태를 얻고 싶다면 wait() 함수를 사용하면 된다. wait() 함수를 사용하여 자식 프로세스가 종료 될 때까지 기다릴 수 있다.
[동작 순서]
1. 자식 프로세스가 동작 중이면 호출 차단이 차단되기 때문에 상태를 얻어올 때까지 대기
2. wait() 함수 호출자가 시그널을 받을 때까지 대기
3. 자식 프로세스가 종료된 상태라면 즉시 호출이 반환되어 상태를 얻음, 이 때 wait() 함수는 자식 프로세스의 프로세스 ID를 반환
4. 자식 프로세스가 없다면 호출이 즉시 반환되며, 에러값을 반환
[wait 함수의 상황 별 반환값 및 statloc 값]
wait 반환값 | statloc | |
자식 프로세스가 정상적으로 종료 | 자식 프로세스의 pid | - WIFEXITED(statloc) 매크로가 true를 반환 |
자식 프로세스가 비정상적으로 종료 | 자식 프로세스의 pid | - WIFSIGNALED(statloc) 매크로가 true를 반환 - 비정상 종료 이유를 WTERMSIG(statloc) 매크로를 사용하여 구할 수 있음 |
wait 함수 오류 | -1 |
- ECHILD : 호출자의 자식 프로세스가 없는 경우 |
**statlog의 사용법
waitPid = wait(&status);
WEXITSTATUS(status); ==> 자식프로세스가 반환한 값을 가져올 수 있다.
유닉스의 정의에 따라 wait()는 '느린' 시스템 호출 함수로서, 호출 도중 다른 신호를 받는다면 EINTR 에러를 반환한다. 따라서 wait()가 자식 프로세스가 종료되는 동안 다른 신호를 잡지 않고 기다리게 하려면 다음과 같이 처리한다.
while((((waitPid = wait(&status)) == -1) && errno == EINTR));
|
cs |
wait 함수 사용하여 자식 프로세스 종료시까지 대기하기
부모 프로세스가 fork() 함수를 사용하여 자식 프로세스를 생성하였을 때, fork() 함수가 리턴되는 시점부터 2개의 프로세스가 동작하게 됩니다. 부모 프로세스가 자식 프로세스의 종료 상태를 얻
codetravel.tistory.com
4. waitpid() <sys/wait.h>
pid_t waitpid(pid_t pid, int *statloc , int options);
리턴 : 프로세스 ID 반환
오류 : -1
waitpid 함수는 wait 함수처럼 자식 프로세스를 기다릴때 사용하는 함수입니다. waitpid의 세 번째 인자에 아무것도 주지 않았을 때 (0을 주었을 때) wait 함수와 동일하게 동작합니다.
하지만 waitpid 함수는 자식 프로세스가 종료될 때 까지 차단되는 것을 원하지 않을 경우 옵션을 사용하여 차단을 방지할 수 있으며, 기다릴 자식 프로세스를 좀더 상세히 지정할 수 있습니다.
[첫 번째 인자]
pid가 -1 일 경우 (pid == -1) | 임의의 자식 프로세스를 기다림 |
pid가 0 보다 클 경우 (pid > 0) | 프로세스 ID가 pid인 자식 프로세스를 기다림 |
pid가 -1 보다 작을 경우 (pid < -1) | 프로세스 그룹 ID가 pid의 절댓값과 같은 자식 프로세스를 기다림 |
pid가 0일 경우 (pid == 0) | waitpid를 호출한 프로세스의 프로세스 그룹 PID와 같은 프로세스 그룹 ID를 가진 프로세스를 기다림 |
[세 번째 인자]
세 번째 인자로 쓰이는 상수 | statlog 값 |
WCONTINUED | 중단 되었다가 재개된 자식 프로세스의 상태를 받음 |
WNOHANG | 기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수 할 수 없는 상황에서 호출자는 차단되지 않고 반환값으로 0을 받음 |
WUNTRACED | 중단된 자식 프로세스의 상태를 받음 |
출처 : codetravel.tistory.com/42?category=993122
waitpid 함수 사용하기(wait함수와 비교)
waitpid 함수는 wait 함수처럼 자식 프로세스를 기다릴때 사용하는 함수입니다. 즉, 자식 프로세스의 종료상태를 회수할 때 사용합니다. 하지만 waitpid 함수는 자식 프로세스가 종료될 때 까지 차
codetravel.tistory.com
5. getcwd <unistd.h>
char *getcwd(char *buf, size_t size);
현재 작업디렉토리의 이름을 size 만큼 길이로 buf에 복사한다.
실패했을경우 NULL 을 되돌려준다. 주로 현재 디렉토리에 대한 읽기 권한이 없을 경우 발생한다.
6. chdir <direct.h>
int chdir( const char *dirname );
- dirname : 변경할 디렉토리의 경로
- 반환값 : 정상 일 때 0, 에러 시 -1
7. stat <sys/types.h> lstat <sys/stat.h>, fstat <unistd.h>
int stat(const char *file_name, struct stat *buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *file_name, struct stat *buf);
stat() 함수는 첫번째 인자로 주어진 file_name 의 상태를 얻어와서 두번째 인자인 buf 에 채워 넣는다.
lstat() 함수는 심볼릭 링크파일의 원본파일의 상태를 얻어온다는 것을 제외하고는 stat() 함수와 동일하다.
fstat() 는 open(2) 등을 통해서 만들어진 파일지시자를 인자로 받아들인다는 점 외에는 stat() 와 동일한 일을 수행한다.
이들 함수는 성공적으로 수행될경우 파일의 정보를 stat구조체에 복사한다. stat구조체는 다음과 같이 정의되어 있다.
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* Inode number */
mode_t st_mode; /* File type and mode */
nlink_t st_nlink; /* Number of hard links */
uid_t st_uid; /* User ID of owner */
gid_t st_gid; /* Group ID of owner */
dev_t st_rdev; /* Device ID (if special file) */
off_t st_size; /* Total size, in bytes */
blkcnt_t st_blocks; /* Number of 512B blocks allocated */
blksize_t st_blksize; /* Block size for filesystem I/O */
struct timespec st_atim; /* Time of last access */
struct timespec st_mtim; /* Time of last modification */
struct timespec st_ctim; /* Time of last status change */
};
|
cs |
8. opendir, readdir, closedir <dirent.h>
DIR * opendir(const char *name) : 목록을 읽을 디렉토리명으로 DIR *를 return
struct dirent *readdir(DIR *dir) : 목록을 읽을 디렉토리명으로 DIR *를 return
int closedir(DIR *dir) : open된 directory 정보를 close
9. execve <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[]);
실행가능한 파일인 filename의 실행코드를 현재 프로세스에 적재하여 기존의 실행코드와 교체하여 새로운 기능으로 실행한다. 즉, 현재 실행되는 프로그램의 기능은 없어지고 filename 프로그램을 메모리에 loading하여 처음부터 실행한다.
execve()는 filename이 가리키는 프로그램을 실행한다. filename은 binary 실행파일이거나, 다음 형식으로 시작하는 스크립트이다 :
#! interpreter [optional-arg]
argv는 새 프로그램에 전달되는 인수들의 문자열이다. 첫 번째 문자열 (즉, argv [0])에는 실행되고 있는 파일과 관련된 filename이 들어있어야 한다. envp는 일반적으로 key = value 형식의 문자열 배열이며, 새로운 프로그램에 환경으로 전달된다. 만약 기 설정된 환경변수를 사용하려면 environ 전역변수를 그냥 사용한다.
argv와 envp 배열은 각각 배열 끝에 널 포인터를 포함해야 한다.
10. dup, dup2 <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup()와 dup2()는 파일 지정자 oldfd에 대한 복사본을 생성한다.
dup()를 이용해서 복사되어지는 새로운 파일 지정자는 사용되지 않는 가장 작은 파일 지정자를 이용한다.
dup2()는 디스크립터는 파일 지정자를 지정할 수 있는데, 이전에 열린 newfd가 있다면 닫고 나서, oldfd를 newfd에 복사하면 된다.
10. pipe <unistd.h>
int pipe(int filedes[2]);
pipe 를 이용하면 2개의 파일 지시자를 생성할수 있다. 2개가 생성되는 이유는 읽기전용과 쓰기전용의 파이프를 생성하기 위함이다. filedes[0] 은 읽기 전용, filedes[1] 은 쓰기전용의 파이프로 사용된다.
이들 파이프는 주로 부모프로세스와 자식프로세스간의 통신을 위한 목적으로 사용된다.
출처 : 42kchoi.tistory.com/259?category=953120
Minishell 허용 함수 정리
알아볼 함수. fork, wait, waitpid, wait3, wait4, kill, exit, getcwd, chdir, stat, stat, fstat, execve, dup, dup2, pipe, opendir, readdir, closed, strerror, errno * pid란 process id의 줄임말이다. fork..
42kchoi.tistory.com
11. signal <signal.h>
void (*signal(int signum, void (*func)(int)))(int);
typedef void (*sig_t) (int);
sig_t signal(int signum, sig_t func);
signal(signum, (*)f) ==> signum에 해당하는 signal이 전달되었을 때, f가 실행된다. 이 f는 사용자가 원하는대로 지정할 수 있다.
ctrl + C : SIGINT(2) - interrupt program
ctrl + / : SIGQUIT(3) - quit program
ctrl + z : SIGSTOP - stop (cannot be ignored or caught) / cannot be handled by sighandler.
비신뢰성
시그널을 보내면 그 시그널이 제대로 전달되었는지 확인하지 않는다.
대기하지 않음
만약 시그널 처리 함수가 시그널을 처리하는 중 다른 시그널을 받으면, 새로 받은 시그널은 무시된다.
출처 : itqomcom.blogspot.com/2018/11/linuxunix-signal.html