9.1 프로세스 생성
fork()시스템 호출
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
새로운 프로세스를 생성하는 유일한 방법이다.
fork()시스템 호출 시 부모 프로세스를 똑같이 복제하여 새로운 자식 프로세스를 생성한다.
자식 프로세스는 pid를 제외하고 모든 것(코드,데이터,스택,힙 등)이 똑같다.
fork()는 pid를 반환하는데,
부모 프로세스는 자식 프로세스의 id를 반환하고
자식 프로세스에서는 0을 리턴한다
(부모, 자식 총 2번 리턴함)
fork()이후 부모와 자식은 병행적으로 각각 실행을 계속한다.
#include <stdlib.h>
#include <stdio.h>
int main()
{
int pid;
pid=fork();
if(pid==0)//자식 프로세스
{
printf("난 자식임 pid=%d",getpid());
}
else//부모
{
printf("난 부모야, pid는 %d야",getpid());
}
}
(정말 참고로 자식의경우 fork()리턴값이 0인거지 pid가 0인게 아니다)
(추가)
자식은 부모의 fd 테이블을 복사하므로 같은 파일 오프셋을 공유한다.
프로세스 기다리기: wait()
자식 프로세스 중의 하나가 끝날 때까지 기다린다.
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);//특정 pid프로세스의 종료 대기
실행이 끝난 자식 프로세스의 종료코드를 status에 저장하고, 해당 자식프로세스 pid를 리턴함.
#include <unistd.h> ...
int main()
{
int pid,child,status;
printf("[%d] 부모 프로세스 시작\n",getpid());
pid=fork();
if(pid==0)
{
printf("[%d]자식 프로세스 시작 \n",getpid());
exit(1);
}
child=wait(&status);//자식 프로세스가 끝나기를 기다리고 자식pid를 반환받아 child에 저장
printf("[%d] 자식 프로세스 %d 종료\n",getpid(),child);//부모가 수행하게됨.
printf("종료코드: %d",status>>8);
}
9.2 프로그램 실행
fork()이후 자식 프로세스에게 새 프로그램을 실행 시켜야 한다.
이 때 exec()시스템호출을 사용한다.
exec()는 프로세스 내의 프로그램을 새 프로그램으로 대치한다.
pid는 그대로,
PC는 처음으로 올라간다.
프로세스 내의 프로그램이 완전히 새로운 프로그램으로 대치되므로,
exec() 이후에 있던 코드들은 실행되지 않고, 새 프로그램만 실행된다
#include <unistd.h>
int execl(char* path, char* arg0, char* arg1,...char* argn,NULL);
int execv(char* path, char* argv[]);//명령어 경로 + 명령줄 인자
int execlp(char* file,char* arg0, char* arg1,...char* argn,NULL);
int execvp(char* file, char* argv[]);
exec
뒤에 l 이 붙는 경우 : 인자를 하나하나 작성해서 넘김.(arg0, arg1, . . . .NULL) 꼭 NULL까지 입력할 것 주의.
뒤에 v가 붙는 경우 : 인자를 리스트로 넘김(argv)
뒤에 p가 붙는 경우 : 명령어 경로가 아닌 프로그램 이름을 주고, 환경변수 PATH에서 찾음. (그냥 명령어이름 넘기면됨)
예시 코드)
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("시작\n");
execl("/bin/echo", "echo", "hello", NULL);
printf("exec 실패");
}
exec 실행에 성공하면
"exec 실패" 부분은 실행되지 않고,
시작
hello
의 결과가 나오게 된다.
execv로는 argv를 넘겨주고,
./a.out (명령어제외) 인자1 인자2 . . .
이렇게 실행시키면 정상작동한다.
(예시 오류: argv[0]에는 ./a.out이 들어있으므로 두번째 인자는 &argv[1]로 주는것이 마땅함
execl은 코드에서 명령어, 인자1 인자2. . .NULL을 줘야한다.
./a.out 만 입력하면 코드에 인자 입력했던 대로 명령어가 실행된다.
뒤에 p가 붙은 것은 맨 앞 인자로 명령어파일명을 입력하면 된다
ls의 경우.. 그냥 "ls"로 넣어주면 된다.
fork()를 수행하고 exec를 연속으로 수행하는 것은 상당히 번거롭다.
이러한 과정을 자동으로 수행하는 C라이브러리 함수 system() 이 있다.
#include <stdlib.h>
int system(const char *cmdstring);
예를 들어 system("ls -asl");을 하면 자식 프로세스를 생성하고 /bin/sh로 하여금 ls -asl이 수행되도록 한다.
9.4 프로세스 그룹
프로세스 그룹은 여러 프로세스들의 집합이다.
보통 부모 프로세스가 생성하는 자손 프로세스들은 부모와 같은 프로세스 그룹에 속한다.
프로세스 그룹 리더는 프로세스 GID와 PID가 동일하다
#include <sys/types.h>
#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
//프로세스 pid의 프로세스 그룹 id를 pdid로 설정한다. 성공시 0, 실패시 -1 반환.
이 때 pid와 pgid가 같은 경우 => 새로운 그룹을 생성하고, 그 그룹의 리더가 됨
pid와 pgid가 다른 경우 => pgid 그룹으로 이동
pid가 0인 경우 => 호출자의 pid값을 그대로 사용
pgid가 0인 경우 => pid가 새로운 그룹 리더가 됨.
호출자가 새로운 프로세스 그룹을 생성하고 그룹의 리더가 되는 방법은
setpgid(getpid(),getpid())혹은
setpgid(0,0)으로 할 수 있다
9.5 시스템 부팅
시스템 부팅은 fork/exec 호출을 통해 이루어진다.
'2022-2 > 시스템 프로그래밍' 카테고리의 다른 글
백도어 (0) | 2022.12.06 |
---|---|
계정과 권한/패스워드 크래킹 (0) | 2022.11.22 |
10장 메모리 관리 (0) | 2022.11.11 |
8장 프로세스 (1) | 2022.11.08 |
5. 파일 시스템 (0) | 2022.11.02 |
댓글