일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 백엔드
- 파이썬
- Linux
- 코딩테스트
- 자바
- 운영체제
- 프로그래머스 #카카오 #IT #코딩테스트
- C
- 도커
- 프로그래머스
- springboot
- 엘라스틱서치
- 스프링
- 알고리즘
- Kakao
- docker
- Python
- 개발자
- 스프링부트
- programmers
- 카카오
- 쿠버네티스
- 캐시
- DPDK
- Spring
- Elasticsearch
- 네트워크
- 리눅스
- Java
- IT
- Today
- Total
저고데
[Linux] 하나의 프로세스에서 명령어를 사용할 때의 고려해야 할 점들 본문
들어가며
해당 내용은 필자가 회사 업무에서 겪은 문제이다.
사내에 A라는 프로세스를 통해서 ms office 파일의 암호화 여부를 판단하는 로직을 개발해야 했다.
우선은 해당 내용과 관련된 오픈 소스를 찾고 테스트하는 것부터 시작했다.
ms office의 경우, 이와 관련된 오픈 소스가 많이 존재했기에 쉽게 찾을 수 있었다.
https://github.com/nolze/msoffcrypto-tool
GitHub - nolze/msoffcrypto-tool: Python tool and library for decrypting and encrypting MS Office files using passwords or other
Python tool and library for decrypting and encrypting MS Office files using passwords or other keys - nolze/msoffcrypto-tool
github.com
해당 오픈 소스는 ms office 파일들의 암호화 여부를 판단하는 Python 코드로, msoffcrypto-tool 명령어를 입력하여 결과를 알 수 있다.
따라서, A 프로세스에서 해당 명령어를 그대로 입력하여 반환된 결과값을 토대로 로직을 완성하려 검토를 받았지만, 기각되었다.이번 시간에는 해당 로직이 기각된 이유에 대해서 알아보자.
기존의 설계 방식의 문제점
기존의 방식은 개발에 있어서는 간단하지만, 필자가 한 가지 간과한 점이 있다.
실행되고 있는 프로세스에서 특정 명령어를 실행하게 되면 fork()가 일어난다는 것이다.
아래와 같이 프로세스 안에서 명령어를 실행한다고 가정하자.
FILE* fp = popen("msoffice-tool test.docx", "r");
그렇다면 해당 명령어는 내부적으로 다음과 같은 일련의 과정을 거친다.
popen(command, mode)
↓
fork() // 현재 프로세스를 복제한다
↓
child process에서 exec() // 복제된 프로세스에서 'msoffice-tool' 실행
↓
pipe 연결해서 stdout/stderr 받아오기
필자는 처음에 의문이 들었다.
프로세스에서 바로 명령어를 사용하면 되는데, 왜 굳이 fork()가 일어나는 것일까?
해당 이유가 바로 오늘의 핵심이다.
왜 fork()가 일어나는 것일까?
명령어를 실행한다는 것 자체가 가벼운 활동일 순 있지만, 명령어도 하나의 프로그램(프로세스)으로 분류해야 한다.
Linux/Unix 시스템에서 하나의 프로세스 안에서 다른 프로세스를 실행할 수 없다.
그렇기에 기존 프로세스를 복제하여 복제된 자식 프로세스가 다른 프로세스(명령어)을 실행해야 한다.
자식 프로세스가 명령어를 실행 가능한 이유는 복제된 프로세스를 exec()를 통해 완전히 덮어써서 msoffice-tool 명령어로 바꿔 실행이 가능하기 때문이다.
이는 Linux에서의 표준적인 방식이다.
따라서, C에서 Linux에 사용 가능한 system(), popen(), execvp() 등과 같은 대부분의 명령어 실행 인터페이스는 내부적으로 fork -> exec 구조를 따른다.
그럼 여기서 하나의 의문점이 든다.
실행 중인 프로세스 안에서 다른 프로세스를 실행할 수 없는 이유는 무엇일까?
현재 프로세스가 다른 명령어를 실행하려면 왜 fork()를 통해 새로운 프로세스를 만들어야 하는가?
이를 이해하려면 운영체제에서 프로세스와 실행 중인 프로그램의 관계, 그리고 exec()의 특성을 이해해야 한다.
fork()가 실행되지 않고 Linux에서 현재 실행 중인 프로세스가 다른 프로그램을 실행하는 것은 사실 "나 자신을 통째로 다른 프로그램으로 덮어쓴다."는 것이다.
이를 실행하는 것이 아래와 같은 코드이다.
execl("/usr/bin/msoffice-tool", "msoffice-tool", "file.docx", NULL);
이를 실행하면 메모리 상의 현재 프로세스의 code, stack, heap, data 등의 모든 데이터가 사라지고 그 자리에 msoffice-tool이 자리잡고 실행된다.
즉, 현재 프로세스는 죽고, 새로운 프로그램이 대체된다는 것이다.
따라서, 현재 프로세스에서는 죽었기 때문에 명령어 실행 결과를 받을 수 없다.
그러므로 fork()가 필요하다.
현재 프로세스는 살아있고, 자식 프로세스를 생성하여 그 자식 프로세스를 exec()를 통해 해당 명령어로 바꾸고 부모는 자식의 결과를 기다리거나, 출력으로 결과를 읽는다.
int main() {
printf("Before exec\n");
execl("/bin/ls", "ls", NULL);
printf("After exec\n"); // 절대 실행되지 않음
}
위와 같이 fork()를 사용하지 않고 직접 명령어를 실행하는 exec() 함수를 사용한다면 처음에 실행된 프로세스가 죽기 때문에 영원히 "After exec"를 볼 수 없다.
마치며 - 그렇다면 왜 fork()가 일어나면 문제가 되는 것일까?
fork() + exec()를 통해 간단하게 새로운 기능을 추가할 수 있는데, 왜 문제가 되는 것일까?
이는 간단하게 fork()의 성질을 통해 알 수 있다.
fork()는 부모와 동일한 자식 프로세스를 생성하기 때문에 메모리 사용량이 2배가 된다.
굳이 짧은 명령어 한 줄을 위해서 메모리를 더 써야한다는 것이다.
A 프로세스의 자원 소모량이 매우 크기 때문에 상당히 비효율적이라서 필자의 의견이 기각된 것이다.
따라서, 필자는 오픈 소스 코드를 참고하여 프로세스 안에서 실행되는 로직을 따로 만들 수 밖에 없었다.
꽤나 비효율적이라 생각했지만, 프로세스에 대해서 공부할 수 있는 좋은 시간이었다.
'Linux' 카테고리의 다른 글
[Linux] apt 명령어에 대해서 알아보자 (1) | 2024.12.11 |
---|---|
[Linux] service와 systemd에 대해서 알아보자 (1) | 2024.11.06 |
[Linux] DPDK 라이브러리에서 Cuckoo Hash Table 사용하기 (0) | 2024.09.17 |
[Linux] DPDK가 무엇인지 알아보고 직접 설치해보자 (2) | 2024.09.16 |
[Linux] 1. Linux 부팅 과정에 대해서 알아보자 (4) | 2024.08.28 |