Ctrl-C는 어떻게 자식 프로세스를 종료합니까?
CTRL+ C가 자식을 종료하는 방법을 이해하려고 노력하고 있지만 부모 프로세스는 아닙니다. bash
장기 실행 프로세스를 시작한 다음 CTRL- 를 입력하여 종료 할 수 있는 일부 스크립트 셸에서이 동작을 확인 C하고 컨트롤이 셸로 돌아갑니다.
어떻게 작동하는지, 특히 부모 (쉘) 프로세스가 종료되지 않는 이유를 설명해 주시겠습니까?
쉘은 CTRL+ C이벤트에 대한 특별한 처리를 해야합니까? 그렇다면 정확히 무엇을합니까?
기본적으로 신호는 커널에 의해 처리됩니다. 구형 Unix 시스템에는 15 개의 신호가있었습니다. 이제 그들은 더 많이 있습니다. 확인 </usr/include/signal.h>
(또는 kill -l) 할 수 있습니다 . CTRL+ C는 이름이있는 신호입니다 SIGINT
.
각 신호를 처리하기위한 기본 동작은 커널에도 정의되어 있으며 일반적으로 신호를 수신 한 프로세스를 종료합니다.
모든 신호 (그러나 SIGKILL
)는 프로그램에서 처리 할 수 있습니다.
그리고 이것은 쉘이하는 일입니다 :
- 셸이 대화 형 모드에서 실행될 때이 모드에 대한 특수 신호 처리가 있습니다.
- 예를 들어 프로그램을 실행할 때
find
셸은 다음과 같습니다.fork
s 자체- 자식의 경우 기본 신호 처리를 설정합니다.
- 자식을 주어진 명령 (예 : find)으로 바꿉니다.
- CTRL+ 를 누르면 C부모 쉘이이 신호를 처리하지만 자식은 기본 동작으로 종료합니다. (아이도 신호 처리를 구현할 수 있습니다)
trap
쉘 스크립트에서도 신호를 보낼 수 있습니다 .
또한 대화 형 셸에 대한 신호 처리를 설정할 수도 있습니다 ~/.profile
. 맨 위에 입력 해보세요 . (이미 로그인했는지 확인하고 다른 터미널로 테스트하십시오. 직접 잠글 수 있습니다.)
trap 'echo "Dont do this"' 2
이제 쉘에서 CTRL+ 를 누를 때마다 C메시지가 인쇄됩니다. 줄을 제거하는 것을 잊지 마십시오!
관심이 있다면 여기/bin/sh
에서 소스 코드에서 일반 이전 신호 처리를 확인할 수 있습니다 .
위의 주석 (현재 삭제됨)에 잘못된 정보가 있었으므로 여기에 관심이있는 사람 은 신호 처리 작동 방식에 대한 매우 좋은 링크 가 있습니다.
먼저 POSIX 터미널 인터페이스에 대한 Wikipedia 기사를 끝까지 읽으 십시오 .
SIGINT
신호는 단말 제어 규칙에 의해 생성하고, 상기 단말의 모든 프로세스에 방송되는 전경 프로세스 기 . 셸은 이미 실행 한 명령 (또는 명령 파이프 라인)에 대한 새 프로세스 그룹을 생성했으며 터미널에 해당 프로세스 그룹이 (터미널의) 포 그라운드 프로세스 그룹임을 알 렸습니다. 모든 동시 명령 파이프 라인에는 자체 프로세스 그룹이 있으며 포 그라운드 명령 파이프 라인은 쉘이 터미널의 포 그라운드 프로세스 그룹으로 터미널에 프로그래밍 한 프로세스 그룹이있는 것입니다. 전경과 배경 사이에서 "작업"을 전환하는 것은 (일부 세부 사항은 제외하고) 어떤 프로세스 그룹이 이제 전경 그룹인지 터미널에 알리는 셸 문제입니다.
쉘 프로세스 자체는 자체적으로 또 다른 프로세스 그룹에 있으므로 해당 프로세스 그룹 중 하나가 포 그라운드에있을 때 신호를 수신하지 않습니다. 그렇게 간단합니다.
터미널은 현재 터미널에 연결된 프로세스로 INT (인터럽트) 신호를 보냅니다. 그런 다음 프로그램은이를 수신하고이를 무시하거나 종료 할 수 있습니다.
강제로 닫히는 프로세스는 없습니다 (기본적으로 sigint를 처리하지 않으면 동작이를 호출한다고 생각 abort()
하지만 조회해야 함).
물론 실행중인 프로세스는 해당 프로세스를 시작한 셸과 분리되어 있습니다.
당신이 경우 원하는 갈 수있는 부모 쉘을, 당신의 프로그램을 실행합니다 exec
:
exec ./myprogram
이렇게하면 부모 셸이 자식 프로세스로 대체됩니다.
setpgid
POSIX C 프로세스 그룹 최소 예
기본 API의 실행 가능한 최소한의 예제로 이해하는 것이 더 쉬울 수 있습니다.
이것은 자식이 프로세스 그룹을 변경하지 않은 경우 신호가 자식에게 전송되는 방법을 보여줍니다 setpgid
.
main.c
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
volatile sig_atomic_t is_child = 0;
void signal_handler(int sig) {
char parent_str[] = "sigint parent\n";
char child_str[] = "sigint child\n";
signal(sig, signal_handler);
if (sig == SIGINT) {
if (is_child) {
write(STDOUT_FILENO, child_str, sizeof(child_str) - 1);
} else {
write(STDOUT_FILENO, parent_str, sizeof(parent_str) - 1);
}
}
}
int main(int argc, char **argv) {
pid_t pid, pgid;
(void)argv;
signal(SIGINT, signal_handler);
signal(SIGUSR1, signal_handler);
pid = fork();
assert(pid != -1);
if (pid == 0) {
is_child = 1;
if (argc > 1) {
/* Change the pgid.
* The new one is guaranteed to be different than the previous, which was equal to the parent's,
* because `man setpgid` says:
* > the child has its own unique process ID, and this PID does not match
* > the ID of any existing process group (setpgid(2)) or session.
*/
setpgid(0, 0);
}
printf("child pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)getpgid(0));
assert(kill(getppid(), SIGUSR1) == 0);
while (1);
exit(EXIT_SUCCESS);
}
/* Wait until the child sends a SIGUSR1. */
pause();
pgid = getpgid(0);
printf("parent pid, pgid = %ju, %ju\n", (uintmax_t)getpid(), (uintmax_t)pgid);
/* man kill explains that negative first argument means to send a signal to a process group. */
kill(-pgid, SIGINT);
while (1);
}
다음으로 컴파일 :
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -Wpedantic -o setpgid setpgid.c
없이 실행 setpgid
CLI 인수 setpgid
가 없으면 완료되지 않습니다.
./setpgid
가능한 결과 :
child pid, pgid = 28250, 28249
parent pid, pgid = 28249, 28249
sigint parent
sigint child
프로그램이 중단됩니다.
보시다시피 두 프로세스의 pgid는 fork
.
그런 다음 때마다 :
Ctrl + C
다시 출력됩니다.
sigint parent
sigint child
이것은 방법을 보여줍니다 :
- 전체 프로세스 그룹에 신호를 보내려면
kill(-pgid, SIGINT)
- 터미널의 Ctrl + C는 기본적으로 전체 프로세스 그룹에 킬을 보냅니다.
두 프로세스 모두에 다른 신호를 전송하여 프로그램을 종료합니다 (예 :를 사용하는 SIGQUIT) Ctrl + \
.
함께 실행 setpgid
인수로 실행하는 경우, 예 :
./setpgid 1
then the child changes its pgid, and now only a single sigint gets printed every time from the parent only:
child pid, pgid = 16470, 16470
parent pid, pgid = 16469, 16469
sigint parent
And now, whenever you hit:
Ctrl + C
only the parent receives the signal as well:
sigint parent
You can still kill the parent as before with a SIGQUIT:
Ctrl + \
however the child now has a different PGID, and does not receive that signal! This can seen from:
ps aux | grep setpgid
You will have to kill it explicitly with:
kill -9 16470
This makes it clear why signal groups exist: otherwise we would get a bunch of processes left over to be cleaned manually all the time.
Tested on Ubuntu 18.04.
CTRL+C is a map to the kill command. When you press them, kill sends a SIGINT signal, that's interrupts the process.
Kill : http://en.wikipedia.org/wiki/Kill_(command)
SIGINT : http://en.wikipedia.org/wiki/SIGINT_(POSIX)
참고 URL : https://stackoverflow.com/questions/6108953/how-does-ctrl-c-terminate-a-child-process
'code' 카테고리의 다른 글
일련의 텍스트 항목에서 공통 / 중요 구문을 추출하는 방법 (0) | 2020.11.28 |
---|---|
요청 MVC와 구성 요소 MVC의 차이점 (0) | 2020.11.28 |
클래스 경로 항목 org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER은 내보내지지 않습니다. (0) | 2020.11.28 |
프레임 워크에 의존하지 않는 MVC 튜토리얼? (0) | 2020.11.28 |
VB6 및 VBA 용 대체 IDE (0) | 2020.11.28 |