시그널 ? - minishell

YP J·2022년 5월 2일
0

배낀곳
시그널처리
godseododdle
개발새발

시그널 : 어떤 이벤트(인터럽트, 타이머종료) 가 발생했을때 운영체제가 프로그램에 이를 알리는것.
시그널 종류

Interrupt 란? ( godseododdle philosophers 참고 )

  • CPU로 하여금 현재 진행중인 프로그램 처리를 중단하고 다른 프로그램을 처리하도록 요구하는 매커니즘.
  • 외부 장치와 상호작용을 위해 반드시 필요하다.

현재 실행되고 있는 프로그램에서 시그널이 전달 되었을시 4가지 상황이 발생.
1. (운영체제에 의해) 시그널 무시, 프로세서는 시그널이 도착한 것을알지 못한다.
2. 운영체제는 프로그램을 강제종료한다.
3. 프로그램 실행시 인터럽트되면 이후에 프로글맴이 저장한 시그널 처리루틴이 실행 된다.
4. 시그널이 블로킹된다, 프로그램이 시그널을 허용할때까지 아무런 영항을 미치지 못한다. 해당 프로세서에는 어떤 시그널이 블록되었는지 마스크를 가지고 있다.

  • 시그널은 큐(queue)에 대기 되지 않고 계류되거나 또는 버려지거나 둘중 하나입니다. 만약 시그널이 처리되는 도중에 똑같은 종류의 시그널이 두개이상 도착한다면 시그널 핸들러는 기존에 진행하던 핸들러를 마치고 한번더 실행하게 됩니다.

sigaction 구조체를 이용한 sigaction 함수 그리고 원형

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
// 성공시 0, 실패시 -1 리턴
struct sigaction
{
	void (*sa_hanlder)(int) // 시그널 처리할 함수
    sigset_t sa_mask;      // 시그널 처리함수가 실행되는 동안 블로킹될 시그널을 설정
    int 	 sa_flags;     // 옵션 설정
}

ex)

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int my_signal(); // 새로운 시그널 처리함수 선언
int count = 0; // Ctrl + C 입력 횟수 카운터

int main(void)
{
	int i = 0;
	struct sigaction act; // sigaction 구조체 변수
	act.sa_handler = my_signal; // 시그널 처리 함수 지정
	sigemptyset(&act.sa_mask); // sm_mask의 모든 비트를 0으로 설정
	act.sa_flags = 0;
	if (sigaction(SIGINT, &act, 0) == SIG_ERR)
	{
		printf("sigaction() err \n");
		exit(1);
	}
	while (count<3)
	{
		sleep(1);
		printf("%d\n",i++);
	}

	return (0);
}

int my_signal()
{
	printf("\nCtrl+C pressed\n");
	count++;
	return (0);
}

실행결과


시그널이란 일종의 IPC(프로세스간 데이터통신) 이면서 동시에 Interrupt 이다.
시그널은 소프트웨어 측면에서 발생되는 Interrupt라 부른다.
원래 Interrupt의 발생 주체가 하드웨어 인데 소프트웨어 상에서 Ctrl 조합키를 입력하여 Interrupt 를 발생시키는 것은 터미널의 제어를 이요하므로 결과적으로 하드웨어를 이용하는 Interrupt가 된다.
즉 시그널이라는 소프트웨어 측면의 Interrupt 발생은 일종의 Interrupt Delegation 이라 볼수있다.

#define SIG_DFL			(void (*)(int))0
#define SIG_IGN			(void (*)(int))1
#define SIG_HOLD		(void (*)(int))5
#define SIG_ERR			(void (*)(int))-1)

siganl 함수는 특정 시그널에 대해 수행할 동작을 정의하는데 사용된다.
시그널의 처리동작은 SIG_IGN(무시), SIG_DFL(기본정의), 핸들러(사용자 정의) 로 나뉘는데,
위 코드에서 알수있듯이 signal함수 를 통해 특정 시그널의 처리 동작을 정할 수 있다.

  1. SIGNAL 보내기
    실행중인 프로세스의 제어 터미널에서 특정 키 조합을 입력하면 시스템이 특정 신호를 보낸다.

Ctrl+c

  • INT 신호 (SIGINT)를 내보낸다.
  • 기본적으로 프로세스를 종료하는 역할을 한다.

Ctrl+z

  • TSTP 신호 (SIGTSTP)를 내보낸다.
  • 기본적으로 프로세스가 실행을 유예시키는 역할을 한다.

Ctrl+z

  • INFO 신호 (SIGINFO)를 내보낸다.
  • 명령에서 지원하는 경우 기본적으로 운영 체제가 실행중인 명령에 대한 정보를 표시한다.
  • 모든 종류의 유닉스에서 지원하지는 않는다.

    현대 운영체제에서 이러한 기본 키 조합들은 stty 명령으로 변경할수 있다.

  1. SIGNAL 종류

SIGHUP

  • 터미널과 연결이 끊겼을 때 발생.
  • 기본적으로 프로세스가 종료된다.
  • 이식 가능 번호 : 1

SIGINT

  • 인터럽트가 발생했을 때 발생
  • 기본적으로 프로세스가 종료된다.
  • 이식 가능 번호 : 2
  • 키보드로 부터 입력받은 인터럽트 시그널 (Ctrl+c)

SIGQUIT

  • 터미널 종료 신호
  • 코어덤프를 남기고 종료된다.(컴퓨터 프로그램이 특정 시점에서 작업중이던 메모리 상태를 기록하는것,비정상적으로 종료되었을때 만들어진다.)
  • 이식 가능 번호 : 3
  • 키보드에서 입력받은 실행중지 시그널 (Ctrl + )

SIGKILL

  • 프로세스를 무조건 종료
  • 절대 무시할 수 없으며 제어할 수 없다.
  • 이식 가능 번호 : 9

SIGSEGV

  • 프로세스가 잘못된 메모리를 참조했을 때 발생
  • 코어덤프를 남기고 종료된다.
  • 이식 가능 번호 : 11

SIGSTOP

  • 프로세스 중단
  • 종료된 상태가 아니면 제어할 수 없다.
  • 이식 가능 번호 : 17
  • 실행 정지 후 다시 실행을 위해 대기시키는 시그널 (Ctrl+z)

Ctrl+d

  • EOF 를의미
  • 시그널이 아니다!.
  • stdin pipe를 닫는다.(read(STDIN) return 0)

리눅스 상에서 kill -l 명령어를 입력하면 모든 시그널 종류를 알수있다.

  1. SIGNAL 성질

비신뢰성

  • 시그널을 보내면 그 시그널이 제대로 도착했는지, 잘 전달 되었는지 확인하지 않는다.
  • 때문에 신뢰성이 낮다.

대기하지 않음

  • 만약 시그널 처리함수를 시그널 처리하고 있는데 그 사이에 다시 시그널을 주게 되면 그 시그널을 무시된다. 다른자료에선 나중에 처리한다 하지않음? 팩트체크 필요
  1. 예제
#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
	printf("give signal...\n");
	while (1)
    	;
	exit(0);
}


Ctrl+C -> SIGINT 신호를 보낸다.
kill -시그널번호 프로세스id (kill -2(or -SIGINT) 프로세스id)

리눅스에서 signal핸들러를 제공한다 -> 제어할수 있다.

  1. signal함수
  • 의존성
#include <signal.h>
  • 함수 원형
typedef void (*sighanlder_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

첫번째 인자 signum 은 시그널을 발생시키는 번호이다.
아니면 매크로를 쓸 수도 있다. SIGINT가 그대로 매크로로 정의 되어있다.
두번째 인자 handler라는 함수 포인터에 함수를 인자로 주게 되면 시그널을 받았을때 그 함수가 호출된다.

ex) sigaction 과 다른 signal 함수로 ~

void	interruptHandler(int sig)
{
	printf(" This program will be exited in 3 seconds ...\n");
	sleep(3);
	exit(0);
}

int main()
{
	int i = 0;
	signal(SIGINT, interruptHandler);
	printf(" input ctrl+c\n");
	while (1)
	{
		sleep(1);
		printf("%d\n",i++);
	}
}

ex) SIGSTOP

#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void stopHandler(int sig)
{
	printf("this program will be exited in 3 seconds...\n");
	sleep(3);
	exit(0);
}

int main()
{
	signal(SIGSTOP, stopHandler);
	printf("input ctrl+z\n");
	while (1);
}

ctrl+z 입력해서 SIGSTOP 을 보내면 그냥 멈춰버린다. 핸들링 되지 않는 것이다. SIGKILL과 SIGSTOP은 사용자가 절대 제어할 수 없다.
(만약 좀비프로세스를 계속해서 생성하는 프로세스 가 있는데 이프로세스를 죽이지 못하면 안 되닌까? )

핸들러 전달인자 sig 는 시그널의 종류를 나타낸다.-> 종류에 따라 처리할 수 있다.

#include <signal.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void signalHandler(int sig)
{
	if (sig==SIGINT)
	{
		printf("this program will stop in 3 seconds...\n");
		sleep(3);
		exit(0);
	}
	if (sig==SIGQUIT)
	{
		printf("signal SIGQUIT\n");
	}
}

int main()
{
	signal(SIGINT, signalHandler);
	signal(SIGQUIT, signalHandler);
	printf("input ctrl+c or ctrl+\\ \n");
	while (1);
}

ctrl+\ 입력해서 SIGQUIT 신호 보내도 종료하지 않고 SIGINT 신호 보내야만 3초안에 종료되도록 정의

profile
be pro

0개의 댓글