시그널에 대한 더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

시그널에 대해서 이야기하는 3번째 시간이 되겠네요. 지난 번에는 시그널 개념과 시그널 관련함수까지 다루어 봤습니다. 

시그널 개념과 시그널 핸들러와 시그널 관련 함수(sigfillset, sigemptyset, sigaddset, sigdelset, sigprocmask)는 지난 번 포스팅을 참고하시기 바랍니다.

 

이번에는 시그널에 대한 sigpending과 sigismember에 대해서 알아보도록 하겠습니다.

  •  sigpending
int sigpending(sigset_t *set);

 

현재 블록되어 대기중인 signal들을 set에 담게 됩니다. 만일 블록될 signal들이 {SIGINT, SIGTSTP} 라고 하고 SIGTSTP가 블록된 상태라면 set에는 {SIGTSTP}가 들어가게 되지요. 이 함수가 성공했다면 0, 실패했다면 -1을 반환합니다.

  • sigisempty
int sigismember(sigset_t *set, int signo);

 

현재 set에 signo(시그널 번호)가 포함되어 있는지 알아냅니다. 만약 set에 해당하는 시그널 번호가 존재한다면 1, 없다면 0을 반환하게 됩니다. -1을 반환할때도 있는데 이 경우는 시그널을 확인할 수 없다는 의미랍니다. 간단하죠?

 

이제 간단하게 예제를 보도록 합시다.

 

#include <stdio.h>
#include <signal.h> 
#include <unistd.h>  

int main(){
        sigset_t pendingset;
        sigset_t set;

        sigemptyset(&set);
        sigemptyset(&pendingset);

        sigaddset(&set,SIGQUIT);
        sigaddset(&set,SIGINT);
        sigaddset(&set,SIGTSTP);

        sigprocmask(SIG_BLOCK,&set,NULL);

        printf("SIGQUIT, SIGINT, SIGTSTP를 발생시켜보세요.\n");
        sleep(3);

        if(sigpending(&pendingset)==0){
                printf("\n\nBlock되어 대기중인 SIGNAL\n");
                if(sigismember(&pendingset,SIGQUIT))
                        printf("SIGQUIT\n");
                if(sigismember(&pendingset,SIGINT))
                        printf("SIGINT\n");
                if(sigismember(&pendingset,SIGTSTP))
                        printf("SIGTSTP\n");
        }

        sleep(3);

        sigprocmask(SIG_UNBLOCK,&set,NULL);

        printf("SIGQUIT OR SIGINT OR SIGTSTP 신호를 발생시켰으면 이 메
시지가 보이지 않습니다.\n");
        return 0;
}

이 프로그램은 set에 있는 시그널들이 발생하면 블록하고 만약 이 중 시그널이 발생하면 블록된 시그널의 목록을 보여주는 프로그램입니다. 우선 set에는 SIGQUIT, SIGINT, SIGTSTP라는 시그널이 들어있고 sigprocmask로 블록시키라고 명령합니다. 이 후 3초가 지난후 sigpending으로 블록되어 대기중인 signal을 pendingset에 설정합니다. 함수가 성공적으로 호출되었다(반환값 0)면 sigismember에서 어떤 시그널이 대기중인지 확인합니다.

 

확인해봅시다. gcc 소스코드 -o signal 로 컴파일하고 실행파일을 실행시켜봅시다.

# ./signal

SIGQUIT, SIGINT, SIGTSTP를 발생시켜보세요.

^C^Z

Block되어 대기중인 SIGNAL

SIGINT

SIGTSTP

저는 3초이내 Ctrl+C(SIGINT)와 Ctrl+Z(SIGTSTP)를 발생시키고 메시지를 확인해보니 SIGINT와 SIGTSTP가 대기중이라고 나옵니다. 이해되셨나요?

 

  •  sigsuspend
int sigsuspend(const sigset_t *mask);

시그널을 BLOCK시킴과 동시에 대기합니다. sigprocmask같은 경우 how를 SIG_BLOCK이나 SIG_SETMASK로 설정하면 블록하기만 할뿐 대기하지는 않는데, sigsuspend는 블록과 대기를 동시에 할 수 있는 것이죠. 성공시 0, 실패시 -1을 반환합니다.

아래의 예제가 있습니다.

 

 

#include <stdio.h> 
#include <signal.h> 
#include <unistd.h> 

int main(){    
        sigset_t set;    
   
        sigemptyset(&set);   
 
        sigaddset(&set,SIGQUIT);   
        sigaddset(&set,SIGINT); 
        sigaddset(&set,SIGTSTP);   
 
        printf("SIGQUIT, SIGINT, SIGTSTP이 5초간 BLOCK됩니다.\n");  
 
        sigprocmask(SIG_SETMASK,&set,NULL);    
   
        sleep(5);     
        printf("\n\nSIGNAL 블록이 해제되고 시그널이 발생했다면 종료됩니다.\n");        
        printf("시그널을 발생시키지 않았다면 발생시켜 종료하세요.\n");      
        sigemptyset(&set);      
        sigsuspend(&set);   
  
        return 0;
}


SIGQUIT, SIGINT, SIGTSTP를 sigprocmask의 SIG_SETMASK로 블록시키려고 하는군요. 5초 후에 set을 빈 집합으로 설정합니다. 만일 SIGQUIT, SIGINT, SIGTSTP가 5초 이내에 발생했다면 프로그램은 5초후 바로 종료하게 되고, 발생시키지 않았다면 위 3개의 시그널이 발생할때까지 대기하게 되는 것입니다.

  •  5초 내에 SIGINT 신호를 전달한 경우 5초가 지난 후 메시지 출력후 바로 종료
# ./a.out 
SIGQUIT, SIGINT, SIGTSTP이 5초간 BLOCK됩니다.
^C

SIGNAL 블록이 해제되고 시그널이 발생했다면 종료됩니다.
시그널을 발생시키지 않았다면 발생시켜 종료하세요.
  • 5초내에 3개의 신호 아무것도 를 주지 않은 경우 신호를 줄때까지 계속 대기 
# ./a.out 
SIGQUIT, SIGINT, SIGTSTP이 5초간 BLOCK됩니다.


SIGNAL 블록이 해제되고 시그널이 발생했다면 종료됩니다.
시그널을 발생시키지 않았다면 발생시켜 종료하세요.

 

이상으로 3개의 signal 관련 함수를 알아보았습니다.

 

반응형
블로그 이미지

REAKWON

와나진짜

,

시그널에 대한 더 많은 정보를 담은 리눅스 교재를 배포했습니다. 아래의 페이지에서 리눅스 교재를 받아가세요.

https://reakwon.tistory.com/233

 

리눅스 프로그래밍 note 배포

티스토리에 리눅스에 관한 내용을 두서없이 여지껏 포스팅했었데요. 저도 제 포스팅을 찾기가 어렵기도 하고 티스토리에서 코드삽입을 하게 되면 이게 일자로 쭉 쓰여져있는 x같은 현상이 생겨

reakwon.tistory.com

 

시그널 관련 함수

지난 시간에는 간단하게 시그널 기본적인 설명과 어떻게 핸들링하는지에 대해서 알아보았죠?  

시그널 개념과 시그널 핸들러는 지난 포스팅을 참고하시기 바랍니다.

https://reakwon.tistory.com/46

 

 

[리눅스] 시그널 (SIGNAL)1 시그널 다루기(시그널 핸들러)

시그널 의미전달에 사용되는 대표적인 방법은 메세지와 신호입니다. 메세지는 여러가지 의미를 갖을 수 있지만 복잡한 대신 , 신호는 1:1로 의미가 대응되기 때문에 간단합니다. 컴퓨터에서 신

reakwon.tistory.com

 

여러개의 신호를 간편하게 다루기 위해서는 신호를 집합으로 표시하는 자료 형식이 필요하게 됩니다. 단순히 생각해보면 int 형식을 한비트마다 하나의 신호로 대응시켜 집합으로 표시할 수도 있지만, int의 비트보다 더 많은 신호가 존재할 수 있기 때문에 신호만의 새로운 자료 형식이 필요하게 되었습니다. int의 비트 32비트이고 신호는 32개가 넘게되니까요.  이러한 자료 형식이 sigset_t라는 자료 형식입니다. sigset_t는 신호의 집합임을 기억합시다.

이런 신호 집합을 왜 쓰게 될까요? 앞에서 얘기했다시피 많은 신호를 간편하게 다루기 위함입니다. 모든 신호를 막는다거나(BLOCK), 막은 신호를 다시 푼다던가(UNBLOCK), 신호가 발생했지만 Block되어서 대기(PENDING) 중인 신호가 무엇이 있는가, 이러한 작업을 쉽게 할 수 있습니다. 프로세스는 신호 집합을 가지고 있고 관리합니다. 이와 연관된 함수가 추가로 필요하게 되는데, 그것이 오늘 소개할 함수들입니다.

 

시그널 중에서 SIGSTOP와 SIGKILL은 절대 제어할 수 없으므로 알아두시기 바랍니다.

 

o sigfillset, sigemptyset

int sigfillset(sigset_t *set);

int sigemptyset(sigset_t *set);

 

이 두 함수는 setset_t라는 집합에서 모든 시그널을 추가하거나 모든 시그널을 제거합니다. 성공시 0, 실패시 -1을 반환하구요. 아래는 sigfillset과 sigemptyset의 동작과정을 말해줍니다. 

sigfillset을 사용하면 모든 시그널을 집합에 추가하는데요. 반대로 sigemptyset은 set이라는 집합에서 시그널을 전부 깨끗이 지워줍니다.

 

 

 

sigaddset, sigdelset

int sigaddset(sigset_t *set, int signum);

int sigdelset(sigset_t *set, int signum);

 

이 함수들은 이름에서 알 수 있듯 signal을 추가하거나 삭제합니다. 역시 성공시 0, 실패시 -1을 반환합니다.

기존의 SIGSEGV와 SIGHUP이 있는 set에서 SIGINT를 추가하면 set은 {SIGINT, SIGSEGV, SIGHUP}가 있게 됩니다. 거기서 SIGHUP을 sigdelset으로 제거하면 결국 set에는 {SIGSEGV, SIGINT}가 존재하게 되는 것입니다.

 

 

 

o sigprocmask

int sigprocmask(int how, const sigset_t *set, sigset_t oldset);

sigprocmask는 시그널을 블록시킬지 말지를 결정합니다. 

 

how : 시그널을 어떻게 제어할지를 말해줍니다. how에는 세가지 동작이 있는데요. 

   1. SIG_BLOCK : 기존에 블록된 시그널이 있다면 두 번째 인자는 set의 시      그널을 추가하라는 의미입니다.

   2. SIG_UNBLOCK : 기존의 블록된 시그널에서 set의 시그널을 제거합니       다.

   3. SIG_SETMASK : 기존의 블록된 시그널을 전부 제거시키고 새로운 set      의 시그널들을 블록시킵니다.

 

set : how가 동작하는 방식에 따라 기존의 block된 시그널에 대해 set을 추가시킬지, 제거시킬지, 아니면 전부 set으로 설정할지를 의미합니다. 그러니 set은 이번에 설정할 시그널이라고 기억해두세요.

oldset : 이 전에 블록된 시그널들을 저장합니다.

 

예제 코드 1)

sigprocmask는 우선 이해하기가 조금 까다로운데요. 주석을 통해서 설명을 하긴 했지만 그림이 조금 필요하겠네요. 아래는 sigprocmask를 통해서 시그널을 제어하는 코드입니다.

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

int main(){
        sigset_t set, oldset;

        sigemptyset(&set);
        sigemptyset(&oldset);

        sigaddset(&set,SIGINT);
        sigaddset(&set,SIGQUIT);
        sigprocmask(SIG_BLOCK,&set,NULL);

        printf("SIGINT와 SIGQUIT는 블록되었습니다.\n");
        printf("Ctrl+C와 Ctrl+\\ 눌러도 반응이 없습니다.\n");

		//만약 Ctrl + \(SIGQUIT)을 눌렀다면 5초후 Coredump가 생기고 종료
        //SIGQUIT의 기본동작은 Coredump + 종료
        sleep(5);

		//현재 set에서 SIGINT를 뺌. set에는 SIGQUIT만 있는 상태
        //중요한것은 프로세스에 적용하지 않은 상태
        sigdelset(&set,SIGINT);
        
        //프로세스에 Unblock을 set에 적용. SIGQUIT은 이제 Block되지 않음
        sigprocmask(SIG_UNBLOCK,&set,&oldset);

        printf("만약 Ctrl+\\을 눌렀다면 종료합니다.\n");
        printf("현재 남은 시그널은 SIGINT입니다.\n");
        printf("Ctrl+C를 눌러도 반응이 없습니다.\n");

        sleep(5);

        set=oldset;
        sigprocmask(SIG_SETMASK,&set,NULL);

        printf("다시 SIGINT와 SIGQUIT이 블록됩니다.\n");
        printf("Ctrl+C와 Ctrl+\\ 눌러도 반응이 없습니다.\n");

        sleep(5);

        sigprocmask(SIG_UNBLOCK,&set,NULL);
        //아무 시그널(Cntl +C 혹은 Cntl+\)을 주지 않았다면 아래의 메시지가 출력되고 종료
        printf("모든 시그널이 해제되었습니다.\n");

}


이제 하나하나씩 까보도록 합시다.

 

1.

초기에 현재 프로그램의 블록된 시그널은 없습니다. set과 oldset도 역시 깨끗이 비워줍니다. 

sigset_t set, oldset; 
sigemptyset(&set); 
sigemptyset(&oldset);
 

아래 그림을 보세요. 저의 머리처럼 비어있는 걸 알 수 있습니다.

 

 

이후 sigaddset으로 set에 SIGINT와 SIGQUIT을 추가합니다. 중요한것은 blocked signal에 추가한 것이 아닙니다. SIGINT의 단축키는 Ctrl+C, SIGQUIT의 단축기는 Ctrl+\랍니다.

2.

 sigaddset(&set,SIGINT);  
 sigaddset(&set,SIGQUIT);
 

아래 그림에도 set 집합에서 SIGINT와 SIGQUIT이 추가된 것을 알 수 있군요.

 

 

 

3.

이 set에 있는 시그널들을 block 시키기 위해서 sigprocmask를 호출하는데 how의 인자는 SIG_BLOCK입니다.

sigprocmask(SIG_BLOCK,&set,NULL);

현재 블록된 시그널에 set의 시그널이 추가되었음을 알 수 있네요.

 

아까도 봤다시피 SIG_BLOCK은 현재 블록된 시그널에서 set의 시그널을 추가하는 겁니다.

만약 blocked signal에 SIGHUP이 존재한다면 sigprocmask(SIGBLOCK, &set, NULL) 호출 후 blocked signal 집합에는 {SIGHUP, SIGINT, SIGQUIT)이 됩니다. 

 

그렇기 때문에 이제 SIGINT와 SIGQUIT는 이제 블록됩니다.

 

 

 

이후 5초간 SIGINT와 SIGQUIT 신호를 보내도 아무런 반응이 없는 걸 알 수 있나요?

 

4.

 

sigdelset(&set,SIGINT); 
sigprocmask(SIG_UNBLOCK,&set,&oldset);

5초가 지나면 set에서 SIGINT를 제거하는 군요. 그렇다면 set에는 SIGQUIT만 남게 되죠.  sigprocmask로 set에 있는 집합을 blocked signal에서 제거합니다. 

 

하지만 oldset이 설정되어있었네요. 그러니까 oldset은 기존에 블록된 시그널이 잡히게 됩니다. 기존에 블록된 시그널은 위의 그림에서 SIGINT와 SIGQUIT이었군요. 그 후 blocked signal에서 set에 있는 시그널을 제거하게 되면 SIGINT만 남겠네요.

아래 그림에서 보여주듯 blocked signal에는 결국 SIGINT만 남게됩니다. 따라서 5초이내에 SIGQUIT을 보냈다면 프로그램이 코어덤프와 함께 종료됩니다. SIGQUIT의 블록 상태가 풀리게 되니까요.

 

 

5.

이후 set은 oldset의 값을 복사하네요.

 set=oldset; 

 

 

6.

sigprocmask(SIG_SETMASK,&set,NULL); 

 

이제 SIG_SETMASK로 set에 있는 시그널들을 blocked signal에 설정합니다. 설정하는 것입니다. 추가하는 것이 아니에요.

 

그러니 blocked signal에는 SIGINT와 SIGQUIT이 다시 설정되는 것을 알 수 있죠.

 

 

 

그래서 다시 SIGINT와 SIGQUIT을 보내도 5초간 아무 응답이 없게 되지요.

7.

 

sigprocmask(SIG_UNBLOCK,&set,NULL); 

5초가 지난 후 set에 있는 시그널들을 blocked signal에서 제거하게 되는데요. 만약 5초 전에 SIGINT나 SIGQUIT 신호를 주었다면 바로 종료하게 되는 겁니다.

왜냐면 5초가 지나면 아래의 그림처럼 blocked signal에서 SIGINT와 SIGQUIT이 존재하지 않으니 블록된 시그널은 풀리게 되면서 프로그램이 종료가 됩니다. 

 

 

 

만약 SIGINT 또는 SIGQUIT를 보내지 않았다면 프로그램은 "모든 시그널이 해제되었습니다" 라는 메세지와 함께 종료될겁니다.

 

process의 signal 정보는 어디에?

그렇다면 프로그램 실행시, 이 process의 block된 시그널의 정보는 어디에 저장이 되고 관리가 될까요? 즉, 위의 그림에서 blocked signal은 누가 관리해주냐는 것입니다. 이에 대한 정보들은 kernel의 task_struct 구조체로 인해 관리가 됩니다. task_struct는 process나 thread가 실행될때마다 하나씩 생성이 되는데, 여기에 정말 많은 정보들이 들어있습니다. 다음은 signal 관련 field들입니다.

// linux/sched.h

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
...

	/* Signal handlers: */
	struct signal_struct		*signal;
	struct sighand_struct		*sighand;
	sigset_t			blocked;
	sigset_t			real_blocked;
	/* Restored if set_restore_sigmask() was used: */
	sigset_t			saved_sigmask;
	struct sigpending		pending;
	unsigned long			sas_ss_sp;
	size_t				sas_ss_size;
	unsigned int			sas_ss_flags;
    
 };

 

task_struct의 멤버를 보게되면 blocked, real_blocked라는 이름이 보이시죠? sigset_t 어디서 많이 보았죠? 이처럼 kernel에서 task_struct를 통해서 관리하여진다는 것입니다.

 

이상으로 signal 집합을 제어하는 함수들을 소개하고 알아보았습니다. 다음 포스팅은 sigpending과 관련된 함수에 대해서 알아보도록 하겠습니다.

반응형
블로그 이미지

REAKWON

와나진짜

,