학부에서 Linux 시스템 프로그래밍을 배울 때 멀티프로세싱을 다루었었다. 이때 fork
, wait
의 system call을 사용하였었다. 그런데 이 system call들을 python에서도 사용할 수 있다. 이 systam call들은 os
모듈을 사용하여 접근할 수 있다. 이를 이용하면 파이썬에서도 c에서와 유사하게 multi-processing을 할 수 있다. 단, Ms Windows처럼 이 system call들을 지원하지 않는 운영체제에서는 사용할 수 없다.
fork
c에서 fork
는 fork
를 사용한 시점에서 새로운 프로세스를 생성하는 system call이다. fork
이후에는 기존 프로세스와 새롭게 생성된 프로세스 모두에서 이후 코드들이 실행되게 한다. python에서의 fork
도 이와 동일하다.
- fork
- 인자 : 없음
- 반환 값
- 0 : 새롭게 생성된 자식 프로세스에서 코드가 실행되는 경우
- pid > 0 : 자식 프로세스를 생성한 부모 프로세스에서 코드가 실행되는 경우 자식 프로세스의 pid 값을 반환한다.
아래 예제 코드를 보자.
import os
print("Started");
if os.fork() == 0:
# child process
print("Child Process pid : ", os.getpid(), " ppid : ", os.getppid())
else :
# parent process
print("Parent Process pid : ", os.getpid(), " ppid : ", os.getppid())
print("END")
이 코드를 실행하면 아래와 같은 출력을 얻을 수 있다.
Started
Parent Process pid : 278 ppid : 93
END
Child Process pid : 279 ppid : 278
END
Parent process의 pid
값이 Child Process pid
의 getppid()
로 얻어온 값과 같은 것을 볼 수 있다. 여기서 코드상에서는 print("END")
는 한 줄인데 출력에는 두 번 출력된 것을 볼 수 있다. 이는 아까 언급하였듯이 fork()
실행 이후 기존 프로세스와 새로 생성된 프로세스에서 모두 아래의 코드들을 실행하기 때문이다. child process에서는 fork()
가 0
을 반환하였고, parent process에서는 fork
가 양수값을 반환하였기 때문에 두 프로세스가 fork
이후의 코드를 실행한다고 하더라도 이 반환 값을 기준으로 다른 행동을 하게 할 수 있다.
fork
가 발생하면 process의 메모리 영역을 복사하여 새로운 프로세스를 만들게 된다. 코드상에서 보았을 때 변수명이 같더라도 각 프로세스에 존재하는 다른 변수가 되는 것이다. 아래 예제를 보자.
import os
print("Started");
x = 0
if os.fork() == 0:
# child process
print("Child Process pid : ", os.getpid(), " ppid : ", os.getppid())
x += 5;
else :
# parent process
print("Parent Process pid : ", os.getpid(), " ppid : ", os.getppid())
x -= 5;
print("END x : ",x," pid : ",os.getpid())
분명 두 프로세스에서 x
를 조작하고 있다. 한 프로세스에서는 x
에 5
를 더하고, 다른 곳에서는 5
를 빼고 있다. 하지만 x
는 두 어느 프로세스에서도 0
이 아니다. 아래는 위 코드의 출력 결과이다.
Started
Parent Process pid : 512 ppid : 93
END x : -5 pid : 512
Child Process pid : 513 ppid : 512
END x : 5 pid : 513
하나의 프로세스에서는 5
, 다른 프로세스에서는 -5
로 x
가 변경되어있는 것을 볼 수 있다. fork
가 진행된 이후에는 변수명이 같아도 한 프로세스의 변수를 건드려서 다른 프로세스의 동일 이름의 변수의 값이 변하게 할 수 없다. 만약 이를 원한다면 IPC를 이용하여 두 프로세스 간 통신을 하여야 한다.
아래 다른 예제를 보자.
import os
print("A")
os.fork()
print("B")
os.fork()
print("C")
os.fork()
print("D")
A, B, C, D는 각각 몇 번 출력될까?
이 python 코드의 실행 결과는 다음과 같다.
A
B
B
C
C
C
D
C
D
D
D
D
D
D
D
A,B,C,D 가 각각 1, 2, 4, 8번 출력되었다. fork()
실행 후 기존 프로세스와 새로 생성된 프로세스 둘 다 뒤에 나오는 코드들을 모두 실행하기 때문이다. A 출력 후 프로세스가 하나 더 생겨 2개가 되고, 이 두 프로세스가 B를 출력한다. 이 두 프로세스가 fork
를 실행하면 각각의 프로세스가 새로운 프로세스를 생성하게 되어 2개가 더 생길 것이다. 총 4개의 프로세스가 실행하게 되고 이들은 C를 출력하게 될 것이다. 이후 이 4개의 프로세스가 fork
를 만나 각각 새로운 프로세스를 하나 더 만들게 되고 이후 총 프로세스는 8개가 된다. 이 8개의 프로세스는 각각 D를 출력하게 된다.
wait
child process가 끝나기 전에 parent process가 먼저 끝날 수 있다. 이를 원치 않을 때 우리는 child process를 기다려 줄 수 있다.
- wait
- 인자 : 없음
- 반환 값 : (종료된 프로세스의 pid, exit status를 유추할 수 있는 정수 값)을 담은
tuple
이다.
아래와 같은 코드가 있다고 하자.
import os
import time
print("Started")
if os.fork() == 0:
# child process
print("Child process")
time.sleep(3)
print("child ended")
os._exit(0)
else :
print("Parent process")
print("Parent ended")
child process의 실행 시간은 3초 이상이고, parent process의 실행 시간은 그보다 짧을 것이다. 이 때문에 이 코드에서는 parent process가 먼저 종료되어 아래와 같은 출력 결과를 볼 수 있다.
Started
Parent process
Parent ended
Child process
$ child ended
parent가 먼저 끝나 shell의 $가 뜨고 약간의 시간이 지난 뒤에 "child ended" 문구가 출력 된다.
wait
를 사용하여 child process를 기다리게 해보자.
import os
import time
print("Started")
if os.fork() == 0:
# child process
print("Child process")
time.sleep(3)
print("child ended")
os._exit(0)
else :
print("Parent process")
os.wait()
print("Parent ended")
Parent process
Child process
child ended
Parent ended
$
"child ended" 이후에 "Parent ended" 문구가 출력되는 것을 볼 수 있다.
다른 system call들
fork
와 wait
이외에도 다른 system call들과 관련 기능을 사용할 수 있다. os
모듈의 waitpid
도 사용할 수 있으며, WIFEXITED
, WEXITSTATUS
, WIFSIGNALED
등을 통하여 프로세스 종료에 관한 정보를 얻는 것 또한 python에서 사용할 수 있다. 이외에도 다양한 것들을 python에서 사용할 수 있다.
'Study > Python' 카테고리의 다른 글
Cython으로 속도 향상 꾀하기 (0) | 2022.06.30 |
---|---|
Python unittest module 사용 (0) | 2021.12.23 |
컴퓨터와 가위바위보 하기 1 (0) | 2021.08.12 |
python argparse를 사용하여 커맨드 라인 인자 처리하기 (0) | 2021.08.04 |
python functools.partial 사용 (0) | 2021.07.22 |
댓글