linux에서는 fork(), vfork(), clone() 시스템 콜로 자식 프로세스를 생성 할 수 있다.
fork, vfork, clone 모두 do_fork()를 호출한다.
fork, vfork, clone 모두 인터럽트를 이용해서 커널의 sys_fork, sys_clone, sys_vfork를 호출하고 이 세 함수 모두 특정 플래그를 첫번째 인자로 넘겨 결국엔 do_fork()를 호출한다.
세번 째 인자인 pt_regs는 커널스택의 레지스터값이고 스택에도 pt_regs구조체에 맞게 레지스터 값이 들어간다.(pt_regs는 윈도우즈 context구조체같이 레지스터값들을 변수로 갖고있다)
do_fork()함수는 부모 프로세스로부터 clone_flags값에 따라 여러 자원들을 복사하는 일을하고 실제 처리는 copy_process()에서 일어난다.
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
fork, vfork, clone 모두 부모 프로세스와 동일한 프로세스를 자식 프로세스로 생성하였다.
execve 는 사용하던 공간을 해제하고 새로운 공간에 다른 프로세스를 로드하고 실행한다.
커널안에 sye_execve()로 정의되어 있는 execve() 는 do_execve()를 호출한다. do_fork()처럼 커널 스택의 pt_regs의 주소를 인자로 넘겨준다. do_execve()도 여러 자원을 복사해두고 바이너리 로더를 하나하나 테스트한다.
* exe*()함수 전부 execve시스템콜 호출
elf파일은 elf 바이너리 로더를 사용하고 elf_load_binary()함수를 통해서 바이너리를 로딩한다.
elf_load_binary()함수의 개요
1. 헤더 체크
2. 프로그램 헤더를 읽는다.
3. 파일 디스크립터 구조체의 공유를 중지 - unshare_files()
4. ELF 헤더에서 인터프리터가 지정되어 있을 경우 대상파일을 오픈하고 헤더체크
5. 새 프로그램을 로드하기 위한 준비 - flush_old_exec()
6. 실행 파일등을 스택에 매핑 - setup_arg_pages()
7. 헤더에 따라 elf_map함수를 호출해 프로그램 매핑
8. 인터프리터 지정시 매핑 - load_elf_interp()
9. 프로그램 실행할 때 레지스터(pt_regs)초기화.
리눅스의 bash도 위의 fork와 execve를 사용하여 명령어를 실행한다.
기본적으로 bash는 무한루프를 돌면서 사용자의 입력을 기다리고 있는다.
사용자가 명령어를 입력하면 fork()를 이용하여 bash 자신과 똑같은 자식프로세스를 하나 생성한다.
fork()의 return 값에 따라 부모는 자식 프로세스가 끝날 때까지 기다리는 루틴으로 진입하고 자식프로세스는 execve(사용자의입력)를 호출하게된다.
execve()가 호출되면서 bash 자식프로세스는 메모리에서 내려가고 execve()함수에 의해서 사용자의입력 프로그램이 새롭게 메모리에 올라가게된다.
bash 자식 프로세스가 죽었으니 bash 부모 프로세스는 다시 사용자의 입력을 기다리게된다.
이런식으로 명령어를 실행하게 된다.
참고문헌 : 리눅스커널2.6구조와원리
Read more
fork, vfork, clone 모두 do_fork()를 호출한다.
fork, vfork, clone 모두 인터럽트를 이용해서 커널의 sys_fork, sys_clone, sys_vfork를 호출하고 이 세 함수 모두 특정 플래그를 첫번째 인자로 넘겨 결국엔 do_fork()를 호출한다.
세번 째 인자인 pt_regs는 커널스택의 레지스터값이고 스택에도 pt_regs구조체에 맞게 레지스터 값이 들어간다.(pt_regs는 윈도우즈 context구조체같이 레지스터값들을 변수로 갖고있다)
do_fork()함수는 부모 프로세스로부터 clone_flags값에 따라 여러 자원들을 복사하는 일을하고 실제 처리는 copy_process()에서 일어난다.
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
fork, vfork, clone 모두 부모 프로세스와 동일한 프로세스를 자식 프로세스로 생성하였다.
execve 는 사용하던 공간을 해제하고 새로운 공간에 다른 프로세스를 로드하고 실행한다.
커널안에 sye_execve()로 정의되어 있는 execve() 는 do_execve()를 호출한다. do_fork()처럼 커널 스택의 pt_regs의 주소를 인자로 넘겨준다. do_execve()도 여러 자원을 복사해두고 바이너리 로더를 하나하나 테스트한다.
* exe*()함수 전부 execve시스템콜 호출
elf파일은 elf 바이너리 로더를 사용하고 elf_load_binary()함수를 통해서 바이너리를 로딩한다.
elf_load_binary()함수의 개요
1. 헤더 체크
2. 프로그램 헤더를 읽는다.
3. 파일 디스크립터 구조체의 공유를 중지 - unshare_files()
4. ELF 헤더에서 인터프리터가 지정되어 있을 경우 대상파일을 오픈하고 헤더체크
5. 새 프로그램을 로드하기 위한 준비 - flush_old_exec()
6. 실행 파일등을 스택에 매핑 - setup_arg_pages()
7. 헤더에 따라 elf_map함수를 호출해 프로그램 매핑
8. 인터프리터 지정시 매핑 - load_elf_interp()
9. 프로그램 실행할 때 레지스터(pt_regs)초기화.
리눅스의 bash도 위의 fork와 execve를 사용하여 명령어를 실행한다.
기본적으로 bash는 무한루프를 돌면서 사용자의 입력을 기다리고 있는다.
사용자가 명령어를 입력하면 fork()를 이용하여 bash 자신과 똑같은 자식프로세스를 하나 생성한다.
fork()의 return 값에 따라 부모는 자식 프로세스가 끝날 때까지 기다리는 루틴으로 진입하고 자식프로세스는 execve(사용자의입력)를 호출하게된다.
execve()가 호출되면서 bash 자식프로세스는 메모리에서 내려가고 execve()함수에 의해서 사용자의입력 프로그램이 새롭게 메모리에 올라가게된다.
bash 자식 프로세스가 죽었으니 bash 부모 프로세스는 다시 사용자의 입력을 기다리게된다.
이런식으로 명령어를 실행하게 된다.
참고문헌 : 리눅스커널2.6구조와원리