리눅스에서 쓰레드와 프로세스


253

최근 리눅스에서 프로세스를 처리하는 데 매우 효율적이며 스레드와 관련된 많은 문제 (예 : 잠금)가 있기 때문에 Linux에서는 스레드 대신 프로세스를 사용하는 것이 거의 항상 좋다고 말하는 사람들이 있습니다. 그러나 스레드가 일부 상황에서 상당히 큰 성능 향상을 가져올 수있는 것처럼 보이기 때문에 의심됩니다.

그래서 내 질문은 스레드와 프로세스가 모두 잘 처리 할 수있는 상황에 처했을 때 프로세스 또는 스레드를 사용해야합니까? 예를 들어, 웹 서버를 작성하는 경우 프로세스 또는 스레드 (또는 조합)를 사용해야합니까?


Linux 2.4와 다른 점이 있습니까?
mouviciel 2009

3
Linux 2.4에서 프로세스와 스레드의 차이점은 스레드가 프로세스보다 상태 (주소 공간, 파일 핸들 등)의 많은 부분을 공유한다는 점입니다. Linux 2.6의 NPTL은 win32 및 Solaris의 "프로세스"와 비슷한 "스레드 그룹"을 제공하여이를보다 명확하게합니다.
MarkR

6
동시 프로그래밍이 어렵습니다. 당신이 필요로하지 않는 매우 높은 성능을, 귀하의 트레이드 오프에서 가장 중요한 측면은 종종있을 것입니다 디버깅의 어려움 . 모든 의사 소통이 명백하기 때문에 (확인하기 쉽고, 로그하기 등) 프로세스는 이와 관련하여 훨씬 쉬운 솔루션을 만듭니다. 대조적으로, 스레드의 공유 메모리는 하나의 스레드가 다른 스레드에 잘못 영향을 줄 수있는 영역을 형성합니다.
Lutz Prechelt

1
@LutzPrechelt-동시 프로그래밍은 멀티 스레드뿐만 아니라 멀티 프로세스도 가능합니다. 동시 프로그래밍이 멀티 스레드 전용이라고 가정하는 이유를 알 수 없습니다. 특정 언어 제한 때문일 수 있지만 일반적으로 둘 다일 수 있습니다.
iankit

2
Lutz는 프로세스 또는 스레드 중 하나를 선택하여 동시 프로그래밍이 어렵다고 언급했지만 프로세스를 사용한 동시 프로그래밍으로 인해 많은 경우 디버깅이 더 쉬워 졌다고 링크했다.
user2692263

답변:


322

리눅스는 1-1 스레딩 모델을 사용하는데, 커널과 프로세스와 스레드를 구분하지 않고 모든 것이 실행 가능한 작업입니다. *

리눅스에서 시스템 호출은 clone그중 공유의 구성 수준으로, 작업을 복제합니다 :

  • CLONE_FILES: 사본을 작성하는 대신 동일한 파일 디스크립터 테이블을 공유하십시오.
  • CLONE_PARENT: 새 작업과 이전 작업간에 부모-자식 관계를 설정하지 마십시오 (그렇지 않으면 childs getppid()= parent 's getpid())
  • CLONE_VM: COW 사본 을 작성하는 대신 동일한 메모리 공간을 공유하십시오.

fork()호출 clone(이상 공유 )pthread_create()통화 clone(대부분의 공유 ). **

forking pthread_create은 테이블을 복사하고 메모리에 대한 COW 매핑을 생성하기 때문에 ing 보다 약간 더 많은 비용이 들지만 Linux 커널 개발자는 이러한 비용을 최소화하려고 노력했습니다.

동일한 메모리 공간과 다양한 테이블을 공유하는 작업 사이의 전환은 데이터가 이미 캐시에로드되어 있기 때문에 공유하지 않는 것보다 약간 저렴합니다. 그러나 아무 것도 공유하지 않아도 작업을 전환하는 것은 여전히 ​​매우 빠릅니다. 이것은 Linux 커널 개발자가 확인하려고 노력하는 것입니다.

실제로 다중 프로세서 시스템을 사용하는 경우 공유 하지 않는 것이 실제로 성능에 도움이 될 수 있습니다. 각 작업이 다른 프로세서에서 실행중인 경우 공유 메모리를 동기화하는 데 많은 비용이 듭니다.


* 단순화. CLONE_THREAD신호 전달이 공유되도록합니다 ( CLONE_SIGHAND필수, 신호 핸들러 테이블을 공유 함).

** 간체. 이 모두 존재 SYS_fork하고 SYS_clone콜을하지만, 커널의를 sys_forksys_clone같은 주위 모두 매우 얇은 래퍼 do_fork자체가 주위에 얇은 래퍼입니다 기능 copy_process. 예, 용어 process, thread그리고 task리눅스 커널에 오히려 의미로 사용됩니다 ...


6
나는 우리가 1 포인트를 놓치고 있다고 생각합니다. 웹 서버에 대해 여러 프로세스를 수행하는 경우 소켓을 열고 다른 스레드에 '작업'을 전달하기 위해 다른 프로세스를 작성해야합니다. 스레딩은 단일 프로세스 다중 스레드, 깔끔한 디자인을 제공합니다. 많은 상황에서 스레드는 자연스럽고 다른 상황에서는 새로운 프로세스가 자연 스럽습니다. 문제가 회색 영역에 빠지면, 에피 엔트가 설명한 다른 절충점이 중요해집니다.
Saurabh

26
@Saurabh 실제로는 아닙니다. 당신은 쉽게 할 수 있습니다 socket, bind, listen, fork, 다음, 여러 프로세스가 accept동일한 청취 소켓에 연결합니다. 프로세스가 사용 중이면 프로세스가 수락을 중지 할 수 있으며, 커널은 들어오는 연결을 다른 프로세스로 라우팅합니다 (아무도 듣지 않으면 listen백 로그 에 따라 커널이 대기 또는 종료됩니다 ). 당신은 그것보다 작업 분배에 대해 훨씬 더 많은 통제력을 가지고 있지 않지만 일반적으로 충분합니다!
ephemient

2
@Bloodcount Linux의 모든 프로세스 / 스레드는 기존 프로세스 / 스레드를 복제하는 동일한 메커니즘으로 생성됩니다. clone()공유 할 자원 을 결정 하기 위해 전달되는 플래그 . 작업은 unshare()나중에 언제든지 리소스를 제공 할 수 있습니다 .
ephemient

4
@KarthikBalaguru 커널 자체에는 task_struct각 작업에 대한가 있습니다. 이를 커널 코드 전체에서 "프로세스"라고 부르지 만 실행 가능한 각 스레드에 해당합니다. 없습니다 process_struct; 의 묶음이 task_struct그들의 thread_group목록에 의해 서로 연결되어 있다면 , 그것들은 사용자 공간과 동일한 "프로세스"입니다. "스레드"에 대한 약간의 특수 처리가 있습니다. 예를 들어 모든 형제 스레드는 포크와 exec에서 중지되며 "주"스레드 만에 나타납니다 ls /proc. /proc/pid목록에 있든 없든 모든 스레드에 액세스 할 수 있습니다 /proc.
ephemient

5
@KarthikBalaguru 커널은 스레드와 프로세스 간의 연속적인 동작을 지원합니다. 예를 들어, clone(CLONE_THREAD | CLONE_VM | CLONE_SIGHAND))작업 디렉토리, 파일 또는 잠금을 공유하지 않는 새로운 "스레드"를 clone(CLONE_FILES | CLONE_FS | CLONE_IO)제공 하는 반면 "프로세스"는 제공합니다. 기본 시스템은 복제하여 작업을 만듭니다. fork()pthread_create()호출 단지 라이브러리 함수이다 clone()(나는이 대답에 쓴) 다르게는.
ephemient

60

Linux (실제로 Unix)는 세 번째 옵션을 제공합니다.

옵션 1-프로세스

응용 프로그램의 일부 (또는 모든 부분)를 처리하는 독립 실행 형 실행 파일을 만들고 각 프로세스마다 별도로 호출합니다. 예를 들어, 프로그램은 자체 복사본을 실행하여 작업을 위임합니다.

옵션 2-스레드

단일 스레드로 시작하는 독립형 실행 파일을 작성하고 일부 태스크를 수행하기 위해 추가 스레드를 작성하십시오.

옵션 3-포크

Linux / Unix에서만 사용할 수 있으며 약간 다릅니다. 분기 프로세스는 실제로 자체 주소 공간이있는 자체 프로세스입니다. 자식이 부모 또는 형제 주소 공간 (스레드와 달리)에 영향을주기 위해 (일반적으로) 수행 할 수있는 작업이 없으므로 견고성이 추가됩니다.

그러나 메모리 페이지는 복사되지 않고 기록 중 복사되므로 일반적으로 생각보다 적은 메모리가 사용됩니다.

다음 두 단계로 구성된 웹 서버 프로그램을 고려하십시오.

  1. 구성 및 런타임 데이터 읽기
  2. 페이지 요청 제공

스레드를 사용한 경우 1 단계는 한 번 수행되고 2 단계는 여러 스레드에서 수행됩니다. "전통적인"프로세스를 사용한 경우 각 프로세스에 대해 1 단계와 2 단계를 반복해야하며 구성 및 런타임 데이터를 저장하기위한 메모리가 복제됩니다. fork ()를 사용한 경우 1 단계를 한 번 수행 한 다음 fork ()를 수행하여 런타임 데이터와 구성을 메모리에 그대로두고 그대로 그대로 복사 할 수 있습니다.

따라서 실제로 세 가지 선택이 있습니다.


7
@Qwertie forking은 그다지 멋진 것이 아니며, 부모 프로세스에서 사용하는 경우 많은 라이브러리를 미묘한 방식으로 중단합니다. 숙련 된 프로그래머조차도 혼란스럽게 만드는 예기치 않은 동작을 만듭니다.
MarkR

2
@MarkR 당신은 forking이 라이브러리를 깨고 예기치 않은 동작을 만드는 방법에 대한 몇 가지 예나 링크를 줄 수 있습니까?
Ehtesh Choudhury

18
프로세스가 열린 mysql 연결로 포크하면 소켓이 두 프로세스간에 공유되므로 나쁜 일이 발생합니다. 한 프로세스 만 연결을 사용하더라도 다른 프로세스는 연결이 닫히지 않습니다.
MarkR

1
fork () 시스템 호출은 POSIX (모든 유닉스 시스템에서 사용 가능함)에 의해 지정됩니다. clone () 시스템 호출 인 기본 Linux API를 사용한 경우 실제로는 Linux에서 세 개보다 훨씬 더 많은 선택이 있습니다 .
Lie Ryan

2
@MarkR 소켓 공유는 의도적으로 설계된 것입니다. 또한 프로세스 중 하나는 소켓에서 close ()를 호출하기 전에 linux.die.net/man/2/shutdown 을 사용하여 소켓을 닫을 수 있습니다 .
Lelanthran

53

그것은 많은 요인에 달려 있습니다. 프로세스는 스레드보다 무겁고 시작 및 종료 비용이 높습니다. 프로세스 간 통신 (IPC)도 스레드 간 통신보다 어렵고 느립니다.

반대로 각 프로세스는 자체 가상 주소 공간에서 실행되므로 프로세스는 스레드보다 안전하고 안전합니다. 하나의 프로세스가 충돌하거나 버퍼 오버런이 발생하면 다른 프로세스에는 전혀 영향을 미치지 않지만 스레드가 충돌하면 프로세스의 다른 스레드가 모두 중단되고 스레드에 버퍼 오버런이 있으면 스레드가 열립니다. 모든 스레드의 보안 구멍.

따라서 통신 모듈을 거의 사용하지 않고 응용 프로그램 모듈을 대부분 독립적으로 실행할 수있는 경우 시작 및 종료 비용을 감당할 수있는 경우 프로세스를 사용해야합니다. IPC의 성능 저하는 최소화되며 버그 및 보안 허점에 대해 약간 더 안전합니다. 모든 약간의 성능이 필요한 경우 복잡한 데이터 구조와 같은 많은 공유 데이터를 얻거나 가질 수 있다면 스레드와 함께하십시오.


9
Adam의 답변은 경영진 브리핑 역할을합니다. 자세한 내용은 MarkR과 ephemient가 좋은 설명을 제공합니다. 예제가 포함 된 매우 자세한 설명은 cs.cf.ac.uk/Dave/C/node29.html 에서 찾을 수 있지만 부분적 으로 약간 날짜가있는 것으로 보입니다.
CyberFonic

2
CyberFonic은 Windows에 해당됩니다. ephemient가 말했듯이 리눅스 프로세스는 무겁지 않습니다. 또한 Linux에서는 스레드 (futex, 공유 메모리, 파이프, IPC) 간의 통신에 사용 가능한 모든 메커니즘을 프로세스에 사용할 수 있으며 동일한 속도로 실행할 수 있습니다.
Russell Stuart

IPC는 사용하기 어렵지만 누군가 "공유 메모리"를 사용하면 어떻게됩니까?
abhiarora

11

다른 사람들은 고려 사항을 논의했습니다.

아마도 중요한 차이점은 Windows 프로세스에서는 스레드에 비해 무겁고 비싸고 Linux에서는 차이가 훨씬 작기 때문에 방정식의 균형이 다른 점입니다.


9

옛날 옛적에 유닉스가 있었고이 오래된 유닉스에는 프로세스에 대한 오버 헤드가 많았 기 때문에 일부 영리한 사람들은 스레드를 만들었습니다.이 스레드는 부모 프로세스와 동일한 주소 공간을 공유하고 컨텍스트가 줄어 들었습니다. 컨텍스트 전환을보다 효율적으로 만드는 스위치입니다.

현대 리눅스 (2.6.x)에서는 스레드와 비교하여 프로세스의 컨텍스트 전환간에 성능에 큰 차이가 없습니다 (MMU 만 스레드에 추가됨). 공유 주소 공간에 문제가 있습니다. 이는 스레드의 잘못된 포인터가 상위 프로세스의 메모리 나 동일한 주소 공간 내의 다른 스레드를 손상시킬 수 있음을 의미합니다.

프로세스는 MMU에 의해 보호되므로 잘못된 포인터는 신호 11 만 발생하고 손상되지 않습니다.

일반적으로 프로세스 (Linux의 컨텍스트 스위치 오버 헤드는 아니지만 MMU로 인한 메모리 보호)를 사용하지만 실시간 스케줄러 클래스가 필요한 경우 pthread는 다른 차 한잔입니다.

Linux에서 스레드가 그렇게 큰 성능 향상을 가져 온다고 생각하는 이유는 무엇입니까? 이것에 대한 데이터가 있습니까, 아니면 단지 신화입니까?


1
예, 데이터가 있습니다. 100,000 개의 프로세스를 생성하는 테스트와 100,000 개의 스레드를 생성하는 테스트를 실행했습니다. 스레드 버전은 약 9 배 더 빠릅니다 (프로세스의 경우 17.38 초, 스레드의 경우 1.93). 이제는 생성 시간 만 테스트하지만 수명이 짧은 작업의 경우 생성 시간이 중요 할 수 있습니다.
user17918 2016 년

4
@ user17918-위에서 언급 한 타이밍을 계산하기 위해 사용 된 코드를 공유 할 수 있습니까?
codingfreak

(가) 모든 프로세스 페이지 테이블을 만들 커널과 내가 생각하는, 그래서 theads는, 단 하나의 페이지 테이블을 사용하는 프로세스와 하나의 큰 가지가 정상입니다 빠른 다음 처리 할 수있는 스레드
c4f4t0r

그것을 보는 또 다른 간단한 방법은 TCB가 PCB보다 매우 작기 때문에 PCB와 관련된 프로세스 컨텍스트 스위치는 스레드 전환보다 약간 더 많은 시간을 소비한다는 것입니다.
Karthik Balaguru

5

당신의 작업은 얼마나 밀접하게 결합되어 있습니까?

그들이 서로 독립적으로 살 수 있다면 과정을 사용하십시오. 서로 의존하는 경우 스레드를 사용하십시오. 이렇게하면 다른 작업의 작동을 방해하지 않고 나쁜 프로세스를 종료하고 다시 시작할 수 있습니다.


4

설상가상으로, thread-local storage 및 Unix 공유 메모리와 같은 것이 있습니다.

스레드 로컬 저장소를 사용하면 각 스레드가 별도의 전역 개체 인스턴스를 가질 수 있습니다. 내가 사용한 유일한 시간은 RTOS에서 실행되는 응용 프로그램 코드를 위해 Linux / windows에서 에뮬레이션 환경을 만들 때였습니다. RTOS에서 각 작업은 자체 주소 공간이있는 프로세스 였고 에뮬레이션 환경에서 각 작업은 스레드 (공유 주소 공간이있는 스레드)였습니다. 싱글 톤과 같은 것에 TLS를 사용함으로써 우리는 '실제'RTOS 환경에서와 같이 각 스레드에 대해 별도의 인스턴스를 가질 수있었습니다.

공유 메모리는 여러 프로세스가 동일한 메모리에 액세스 할 때 성능 이점을 제공하지만 프로세스를 올바르게 동기화해야하는 비용 / 위험이 있습니다. 이를 수행하는 한 가지 방법은 하나의 프로세스가 공유 메모리에 데이터 구조를 작성한 다음 기존의 프로세스 간 통신 (명명 된 파이프)을 통해 해당 구조에 핸들을 보내는 것입니다.


1
마지막으로 스레드 네트워크 프로그램을 작성할 때 일부 통계 수집을 위해 스레드 로컬 스토리지를 사용했습니다. 각 스레드는 자체 카운터에 작성하고 잠금이 필요하지 않으며 메시지가 표시 될 때만 각 스레드가 통계를 전체 합계에 결합합니다. 그러나 TLS는 일반적으로 사용되거나 필요하지 않습니다. 반면에 공유 메모리는 효율적으로 데이터를 보내는 것 외에도 프로세스를 POS 메모리로 공유하여 프로세스간에 POSIX 세마포어를 공유 할 수도 있습니다. 꽤 놀랍습니다.
ephemient

4

LINUX와의 최근 작업에서 알아야 할 것은 라이브러리입니다. 스레드를 사용하는 경우 여러 스레드에서 사용할 수있는 라이브러리가 스레드로부터 안전해야합니다. 이로 인해 몇 번 화상을 입었습니다. 특히 libxml2는 스레드로부터 안전하지 않습니다. 스레드 안전으로 컴파일 할 수 있지만 적성 설치로 얻을 수있는 것은 아닙니다.


3

당신이 듣고있는 것에 동의해야합니다. 클러스터 xhpl등 을 벤치마킹하면 스레드보다 프로세스 성능이 항상 향상됩니다.</anecdote>


3

쓰레드 / 프로세스 사이의 결정은 당신이 무엇을 사용할 것인지에 따라 약간 다릅니다. 프로세스의 이점 중 하나는 PID가 있고 상위를 종료하지 않고도 종료 될 수 있다는 것입니다.

웹 서버의 실제 예를 들어, 아파치 1.3은 여러 프로세스 만 지원하는 데 사용되었지만 2.0 에서는 추상화 를 추가 하여 둘 중 하나를 전환 할 수있었습니다. 댓글이 보인다 하는 프로세스가 더 강력하지만 조금 줄 수있는 스레드가 더 나은 성능을 비트 (프로세스의 성능 짜증 창을 제외하고는 스레드를 사용하고자하는) 것에 동의합니다.


2

대부분의 경우 스레드보다 프로세스를 선호합니다. 스레드는 상대적으로 작은 작업 (프로세스 오버 헤드 >> 분할 된 각 작업 단위에 걸린 시간)이 있고 이들간에 메모리 공유가 필요한 경우 유용 할 수 있습니다. 큰 배열을 생각하십시오. 또한 (주제) CPU 사용률이 100 % 이상인 경우 멀티 스레딩 또는 처리의 이점이 없습니다. (사실 악화 될 것입니다)


이익이 없다는 것은 무엇을 의미합니까? GUI 스레드에서 무거운 계산을 수행하는 것은 어떻습니까? CPU 가로 드되는 방법에 관계없이 사용자 경험의 관점에서 병렬 스레드로 옮기는 것이 훨씬 좋습니다.
olegst

2

스레드-> 스레드는 메모리 공간을 공유하며 CPU의 추상화이며 경량입니다. 프로세스-> 프로세스에는 자체 메모리 공간이 있으며 컴퓨터의 추상화입니다. 작업을 병렬화하려면 CPU를 추상화해야합니다. 그러나 스레드보다 프로세스를 사용하는 이점은 보안, 안정성이며 스레드는 프로세스보다 메모리를 적게 사용하고 대기 시간을 줄입니다. 웹과 관련한 예로 크롬과 파이어 폭스가 있습니다. Chrome의 경우 각 탭은 새로운 프로세스이므로 Chrome의 메모리 사용량은 firefox보다 높으며 제공된 보안 및 안정성은 firefox보다 좋습니다. 크롬이 제공하는 보안은 각 탭이 새로운 프로세스이므로 다른 탭이 주어진 프로세스의 메모리 공간으로 스누핑 할 수 없기 때문에 더 좋습니다.


2

모든 사람이 귀하의 질문에 응답하여 훌륭한 일을했다고 생각합니다. 커널의 맥락에서 이전 응답 중 일부를 명확히하고 요약하기 위해 Linux의 스레드 대 프로세스에 대한 추가 정보를 추가하고 있습니다. 그래서 내 대답은 Linux의 커널 특정 코드와 관련이 있습니다. Linux Kernel 문서에 따르면 스레드가 프로세스와 달리 공유 가상 주소 공간을 사용한다는 점을 제외하면 스레드와 프로세스는 명확하게 구분되지 않습니다 . 또한 Linux Kernel은 "task"라는 용어를 사용하여 일반적으로 프로세스 및 스레드를 나타냅니다.

"프로세스 또는 스레드를 구현하는 내부 구조가 없으며 대신 task라는 추상 스케줄링 단위를 설명하는 struct task_struct가 있습니다."

또한 Linus Torvalds에 따르면 프로세스와 스레드를 전혀 생각해서는 안되며 너무 제한적이기 때문에 유일한 차이점은 "부모 공간과 부모 주소와의 분리"또는 공유 주소 공간과 관련하여 COE 또는 실행 컨텍스트 만 다릅니다. 실제로 그는 웹 서버 예제를 사용하여 여기를 지적 합니다 (읽기를 권장합니다).

리눅스 커널 문서에 대한 완전한 신용


-3

리소스를 공유해야하는 경우 실제로 스레드를 사용해야합니다.

또한 스레드 간 컨텍스트 전환이 프로세스 간 컨텍스트 전환보다 훨씬 저렴하다는 사실을 고려하십시오.

보안, 검증 된 성능 테스트 등 적절한 이유가없는 한 명시 적으로 별도의 프로세스를 수행 할 이유가 없습니다.


3
편집 담당자가 있지만 동의하지 않습니다. 리눅스에서 프로세스 간의 컨텍스트 스위치는 거의 컨텍스트 싼 스레드 사이를 전환한다.
ephemient
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.