다중 코어에서 컴파일할 때 make가 중단되는 원인은 무엇입니까?

다중 코어에서 컴파일할 때 make가 중단되는 원인은 무엇입니까?

어제는 정리를 하려고 했는데뿌리소스에서 패키지. 6코어 몬스터 머신에서 컴파일하고 있었기 때문에 make -j 6. 처음에는 컴파일이 원활하고 매우 빨랐지만 어느 시점에서는 make단 하나의 코어에서 100% CPU를 사용하여 중단되었습니다.

구글링 좀 하다가 찾아낸건데이것ROOT 게시판에 글을 올려주세요. 이 컴퓨터를 제가 직접 만들다보니 방열판을 제대로 적용하지 않아서 CPU가 과열되는구나 하는 걱정이 들었습니다. 안타깝게도 직장에는 냉장고를 넣어둘 수 있는 냉장고가 없습니다. ;-)

패키지를 설치 lm-sensors하고 다시 실행했는데 make -j 6, 이번에는 CPU 온도를 모니터링했습니다. 온도가 높아졌지만(60C 가까이), 결코 높거나 임계 온도를 넘지 않았습니다.

실행을 시도했지만 컴파일하는 동안 이번에는 다른 지점에서 make -j 4다시 멈췄습니다.make

결국 그냥 실행해서 컴파일했는데 make잘 작동했습니다. 내 질문은: 왜 매달려 있었나요? 두 개의 다른 지점에서 멈췄다는 사실로 인해 일종의 경쟁 조건으로 인한 것이라고 추측할 수 있지만 make옵션을 제공하므로 모든 것을 올바른 순서로 정렬할 만큼 영리해야 한다고 생각합니다 -j.

답변1

이 정확한 문제에 대한 답변은 없지만 무슨 일이 일어날 수 있는지에 대한 힌트를 드릴 수는 있습니다. Makefiles에 종속성이 누락되었습니다.

예:

target: a.bytecode b.bytecode
    link a.bytecode b.bytecode -o target

a.bytecode: a.source
    compile a.source -o a.bytecode

b.bytecode: b.source
    compile b.source a.bytecode -o a.bytecode

호출하면 make target모든 것이 올바르게 컴파일됩니다. 의 컴파일이 a.source(임의로, 그러나 결정적으로) 먼저 수행됩니다. 그런 다음 컴파일이 b.source수행됩니다.

그러나 두 명령이 동시에 실행 되면 두 명령이 make -j2 target모두 실행됩니다. compile그리고 실제로 Makefile의 종속성이 손상되었음을 알 수 있습니다. 두 번째 컴파일에서는 a.bytecode이미 컴파일되었다고 가정하지만 종속성에는 표시되지 않습니다. 그래서 오류가 발생할 가능성이 높습니다. 에 대한 올바른 종속성 줄은 다음 b.bytecode과 같아야 합니다.

b.bytecode: b.source a.bytecode

문제로 돌아가서 운이 좋지 않으면 종속성 누락으로 인해 명령이 100% CPU 루프에서 중단될 수 있습니다. 이것이 아마도 여기서 일어나고 있는 일일 것입니다. 누락된 종속성은 순차 빌드에서는 공개될 수 없었지만 병렬 빌드에서는 공개되었습니다.

답변2

이것이 정말 오래된 질문이라는 것을 알고 있지만 여전히 검색 결과 상단에 표시되므로 내 해결책은 다음과 같습니다.

GNU make에는 make와 재귀 하위 항목이 지정된 코어 수보다 많은 코어를 소비하지 않도록 보장하는 작업 서버 메커니즘이 있습니다. http://make.mad-scientist.net/papers/jobserver-implementation/

모든 프로세스가 공유하는 파이프에 의존합니다. 추가 하위 항목을 포크하려는 각 프로세스는 먼저 파이프에서 토큰을 소비한 다음 완료되면 이를 포기해야 합니다. 하위 프로세스가 소비한 토큰을 반환하지 않으면 최상위 레벨은 해당 토큰이 반환될 때까지 영원히 기다리면서 만듭니다.

https://bugzilla.redhat.com/show_bug.cgi?id=654822

"sed"가 GNU sed가 아닌 Solaris 상자에서 GNU make를 사용하여 binutils를 빌드할 때 이 오류가 발생했습니다. sed==gsed가 시스템 sed보다 우선순위를 갖도록 PATH를 조작하여 문제를 해결했습니다. 하지만 sed가 왜 파이프에서 토큰을 소비하는지 모르겠습니다.

답변3

make교착상태를 일으키는 것 같습니다. 를 사용하면 ps -ef다음 프로세스가 원인인 것으로 보입니다.

루트 695 615 1 22:18 ? 00:00:00 PREBUILD -j32 만들기
루트 2127 695 20 22:18 ? 00:00:04 make -f Makefile.prenobuild

각각의 작업을 확인하면 하위 프로세스는 파일 설명자 4에 쓰고 있으며 상위 프로세스는 모든 하위 프로세스가 종료되기를 기다리고 있습니다.

root@ltzj2-6hl3t-b98zz:/# strace -p 2127
strace: 프로세스 2127 첨부됨
쓰기(4, "+", 1
root@ltzj2-6hl3t-b98zz:/# strace -p 695
strace: 프로세스 695 첨부
{{wait4(-1, }}

파일 설명자 4는 파이프입니다.

root@ltzj2-6hl3t-b98zz:/# ls -la /proc/2127/fd/4
l-wx------ 1 루트 루트 64 Sep 3 22:22 /proc/2127/fd/4 -> 'pipe:[1393418985]'

해당 파이프는 상위 프로세스와 하위 프로세스 사이에만 있습니다.

root@ltzj2-6hl3t-b98zz:/# lsof | grep 1393418985
695 루트 3r FIFO 0,12 0t0 1393418985 파이프 만들기
695 루트 4w FIFO 0,12 0t0 1393418985 파이프 만들기
2127 루트 3r FIFO 0,12 0t0 1393418985 파이프 만들기
2127 루트 4w FIFO 0,12 0t0 1393418985 파이프 만들기

따라서 2127이 695에 다시 파이프에 출력을 추가하려고 시도하는 동안 중단된 것처럼 보이지만 695는 보류되어 있으므로 wait4()해당 파이프를 비울 수 없습니다.

cat을 사용하여 셸에서 파이프를 비우면 빌드가 재개되고 예상대로 완료됩니다.

root@ltzj2-6hl3t-b98zz:/# 고양이 /proc/695/fd/3
++++++++++++++++++++++++++++++++

빌드가 차단 해제되고 계속 실행됩니다...


내 원래 이해는 틀렸지만 추가 조사 끝에 결국 다음과 같은 Linux 커널 결함이 발생했습니다.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=46c4c9d1beb7f5b4cec4dd90e7728720583ee348

make가 어떻게 중단되는지에 대한 정확한 설명은 다음과 같습니다.https://lore.kernel.org/lkml/1628086770.5rn8p04n6j.none@localhost/.

gnu make 소스 코드에 적용되는 다음 해결 방법을 사용하여 커널 패치가 보류 중인 이 문제를 해결할 수 있습니다.

--- a/src/posixos.c 2020-01-02 23:11:27.000000000 -0800
+++ b/src/posixos.c 2021-09-18 09:12:02.786563319 -0700
@@ -179,8 +179,52 @@
 jobserver_release (int is_fatal)
 {
   정수 r;
- EINTRLOOP(r, 쓰기(job_fds[1], &token, 1));
- if (r != 1)
+ 정수 n;
+ 문자 b[32];
+
+ /* 다중 make 자식으로 인한 교착 상태를 피하기 위해 비차단 쓰기를 사용합니다.
+ * 동시에 작업을 해제합니다. */
+ set_blocking(job_fds[1], 0);
+ memset(b,token,sizeof(b));
+ n = 1;
+ 동안(n > 0)
+ {
+ r = 쓰기(job_fds[1], b, n);
+ /* 시스템 호출이 중단되었습니다. 다시 시도하세요 */
+ if ( r == -1 )
+ {
+ if ( 오류 번호 == EINTR )
+ 계속;
+
+ /* 이 프로세스와 다른 프로세스가 모두 파이프에 쓰기를 시도했기 때문에 여기까지 왔습니다.
+ * 정확히 같은 시간이며 파이프에는 1페이지만 포함됩니다. 우리는 졌어, 다른 하나는
+ * 프로세스가 성공했습니다(파이프에 기록됨). 먼저 이 조건을 재설정할 수 있습니다.
+ * 파이프에서 읽는 중입니다. 물론 이는 우리가 추가 항목을 반환해야 함을 의미합니다.
+ * 토큰. */
+ if ( errno == EWOULDBLOCK || errno == EAGAIN )
+ {
+ if ( jobserver_acquire(0) )
+ {
+n++;
+ /* 아마도 불가능에 가까울 것입니다... */
+ if (n > 32)
+ 휴식;
+ 계속;
+ }
+ }
+ }
+ if ( r == 0 ) /* 0바이트를 썼지만 오류는 아니므로 다시 시도하세요 */
+ 계속;
+ if ( r > 0 )
+ {
+ n -= r;
+ 계속;
+ }
+ 휴식; /* 기타 모든 오류는 중단됩니다. */
+ }
+ set_blocking(job_fds[1], 1);
+
+ if (n != 0)
     {
       if (치명적)
         pfatal_with_name (_("작업 서버 쓰기"));

답변4

시스템은 괜찮을 수 있지만 make빌드를 병렬로 실행할 때 경쟁 조건이 발생할 수 있습니다.

시스템에 문제가 있는 경우 병렬 빌드를 수행할 때뿐만 아니라 다른 시나리오에서도 시스템이 중단되거나 충돌할 수 있습니다.

관련 정보