Cron에 의해 스폰 된 프로세스가 왜 소멸됩니까?
-
19-09-2019 - |
문제
나는 몇 가지 과정이 나타납니다 <defunct>
안에 top
(그리고 ps
). 나는 실제 대본과 프로그램에서 물건을 끓였다.
내 crontab
:
* * * * * /tmp/launcher.sh /tmp/tester.sh
의 내용 launcher.sh
(물론 실행 파일로 표시) :
#!/bin/bash
# the real script does a little argument processing here
"$@"
의 내용 tester.sh
(물론 실행 파일로 표시) :
#!/bin/bash
sleep 27 & # the real script launches a compiled C program in the background
ps
다음을 보여줍니다.
user 24257 24256 0 18:32 ? 00:00:00 [launcher.sh] <defunct>
user 24259 1 0 18:32 ? 00:00:00 sleep 27
주목하십시오 tester.sh
나타나지 않습니다. 배경 작업을 시작한 후 종료되었습니다.
왜 launcher.sh
주위를 붙잡고 표시합니다 <defunct>
? 그것은 시작했을 때만 이것을하는 것 같습니다 cron
-내가 직접 실행할 때.
추가 참고 사항 : launcher.sh
이 시스템에서 공통 스크립트로, 쉽게 수정되지 않습니다. 다른 것들 (crontab
, tester.sh
, 내가 대신 실행하는 프로그램조차도 sleep
) 훨씬 더 쉽게 수정할 수 있습니다.
해결책
그들은 a의 주제가 아니었기 때문입니다 wait(2)
시스템 호출.
누군가가 미래에 이러한 프로세스를 기다릴 수 있기 때문에 커널은 완전히 제거 할 수 없거나 실행할 수 없습니다. wait
시스템 호출은 출구 상태 나 그 존재의 증거가 더 이상 없기 때문에 통화합니다.
쉘에서 하나를 시작하면 쉘이 Sigchld를 덫을 놓고 다양한 대기 작업을 수행하므로 오랫동안 아무것도 소진되지 않습니다.
그러나 Cron은 대기 상태가 아니며 자고 있으므로 Cron이 깨어날 때까지 소멸 된 어린이는 잠시 동안 고집 할 수 있습니다.
업데이트: 의견에 응답 ... 흠. 문제를 복제 할 수있었습니다.
PPID PID PGID SESS COMMAND
1 3562 3562 3562 cron
3562 1629 3562 3562 \_ cron
1629 1636 1636 1636 \_ sh <defunct>
1 1639 1636 1636 sleep
그래서 일어난 일은 다음과 같습니다.
- Cron Forks와 Cron Child가 쉘을 시작합니다
- Shell (1636)은 SID 및 PGID 1636을 시작하여 수면을 시작합니다.
- 쉘 출구, Sigchld가 Cron 3562로 보냈습니다
- 신호는 무시되거나 잘못 처리됩니다
- 쉘은 좀비를 바꿉니다. 수면은 초기에 기소되므로 수면 출구가 시작되면 신호를 얻고 청소할 수 있습니다. 나는 여전히 좀비가 거두는시기를 알아 내려고 노력하고 있습니다. 아마도 활동적인 어린이가 없으면 Cron 1629가 종료 될 수 있습니다. 그 시점에서 좀비는 기판이 시작되어 다시 나타납니다. 이제 우리는 Cron이 처리 해야하는 누락 된 sigchld에 대해 궁금합니다.
- 반드시 Vixie Cron의 잘못은 아닙니다. 여기에서 볼 수 있듯이 Libdaemon은 Sigchld 핸들러를 설치합니다 ~ 동안
daemon_fork()
, 그리고 이것은 중간 1629의 빠른 출구에서 신호 전달을 방해 할 수 있습니다.지금, 나는 우분투 시스템의 Vixie Cron이 Libdaemon으로 만들어 졌는지조차 모르지만 적어도 새로운 이론이 있습니다. :-)
- 반드시 Vixie Cron의 잘못은 아닙니다. 여기에서 볼 수 있듯이 Libdaemon은 Sigchld 핸들러를 설치합니다 ~ 동안
다른 팁
CRON이 세션의 모든 하위 프로세스가 종료되기를 기다리고 있다고 생각합니다. 부정적인 PID 인수와 관련하여 대기 (2)를 참조하십시오. Sess를 다음과 같이 볼 수 있습니다.
ps faxo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
다음은 내가 보는 것 (편집)입니다.
STAT EUID RUID TT TPGID SESS PGRP PPID PID %CPU COMMAND
Ss 0 0 ? -1 3197 3197 1 3197 0.0 cron
S 0 0 ? -1 3197 3197 3197 18825 0.0 \_ cron
Zs 1000 1000 ? -1 18832 18832 18825 18832 0.0 \_ sh <defunct>
S 1000 1000 ? -1 18832 18832 1 18836 0.0 sleep
SH와 수면은 같은 부분에 있습니다.
setSid (1) 명령을 사용하십시오. 여기 Tester.sh :
#!/bin/bash
setsid sleep 27 # the real script launches a compiled C program in the background
필요하지 않습니다 &
, SetSid는 그것을 백그라운드에 넣습니다.
내 생각에, 그것은 Crontab의 명령의 stdout/stderr에 파이프를 입은 stdin의 입력을 기다리는 프로세스 crond (모든 작업에 대해 crond에 의해 생성됨)에 의해 발생합니다. CRON이 메일을 통해 결과 출력을 사용자에게 보낼 수 있기 때문에 수행됩니다.
따라서 Crond는 사용자 명령이있을 때까지 EOF를 기다리고 있으며 모든 어린이 프로세스가 파이프를 닫았습니다. 이 작업이 완료되면 Crond는 대기 경감으로 계속되고 소진 된 사용자 명령이 사라집니다.
따라서 스크립트에서 스폰 된 모든 하위 프로세스를 명시 적으로 분리하여 파이프를 형성합니다 (예 : 파일 또는 /dev /null로 리디렉션하여 파이프를 리디렉션합니다.
따라서 다음 줄은 Crontab에서 작동해야합니다.
* * * * * ( /tmp/launcher.sh /tmp/tester.sh &>/dev/null & )
두 가지 별도의 프로세스가 없으면 문제를 해결하는 것이 좋습니다. launcher.sh
마지막 줄 에서이 작업을 수행하십시오.
exec "$@"
이것은 불필요한 과정을 제거합니다.
비슷한 문제가있는 해결책을 찾는 동안이 질문을 찾았습니다. 불행히도이 질문에 대한 답변은 내 문제를 해결하지 못했습니다.
부모 프로세스를 찾아 죽여야하므로 소멸 프로세스를 죽이는 것은 옵션이 아닙니다. 나는 다음과 같은 방식으로 소멸 된 프로세스를 죽였습니다.
ps -ef | grep '<defunct>' | grep -v grep | awk '{print "kill -9 ",$3}' | sh
"grep ''에서는 검색을 특정 소멸 프로세스로 좁힐 수 있습니다.
나는 같은 문제를 여러 번 테스트했습니다. 그리고 마침내 해결책이 있습니다. 아래와 같이 Bash 스크립트 앞에 '/bin/bash'를 지정하면됩니다.
* * * * * /bin/bash /tmp/launcher.sh /tmp/tester.sh