개발 · 컴퓨터공학 / / 2021. 11. 5. 19:51

UNIX - Signal blocking system call [sigprocmask], Sending signals system call [kill, raise, alarm, pause]

728x90
반응형

Signal blocking

The sigprocmask(2) system call

#include <signal.h>

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

// Returns: 0 if OK, -1 on error

process에 도착하지 못하고 차단될 signal들의 목록을 가지고 있는 signal mask. sigprocmask를 이 signal mask의 내용을 변경시킨다. 

 

how 매개변수는 SIG_BLOCK / SIG_UNBLOCK / SIG_SETMASK 등의 방법에 대한 값

SIG_BLOCK add a collection of signals to those that will be blocked
SIG_UNBLOCK delete a collection of signals from those that will be blocked
SIG_SETMASK set the collection of signals being blocked to the specified set

set은 newset, oset은 oldset 값을 전달한다. 

restrict는 메모리를 공유하지 않는다는 의미이다.

 

example

#include <signal.h>

main(){
 sigset_t set1, set2;

 /* 시그널 집합을 완전히 채운다. */
 sigfillset (&set1);

 sigfillset (&set2);
 sigdelset (&set2, SIGINT);
 sigdelset (&set2, SIGQUIT);

 /* 중대하지 않은 코드를 수행 ... */

 /* 봉쇄를 설정한다. */
 sigprocmask(SIG_SETMASK, &set1, NULL);

 /* 극도로 중대한 코드를 수행한다. */

 /* 하나의 봉쇄를 제거한다. */
 sigprocmask(SIG_UNBLOCK, &set2, NULL);

 /* 덜 중대한 코드를 수행한다 ... */

 /* 모든 시그널 봉쇄를 제거한다. */
 sigprocmask(SIG_UNBLOCK, &set1, NULL);
}

sigprocmask를 SIG_SETMASK로 모든 set mask에 대해서 설정하여 중대한 코드를 수행한다.

SIG_UNBLOCK으로 SIGINT, SIGQUIT에 대한 block만 제거한 후 덜 중대한 코드를 수행한다.

SIG_UNBLOCK으로 모든 set mask에 대해서 block 제거를 수행한다.

 

 

Sending signals

kill(2) and raise(2) system call

#include <signal.h>

int kill(pid_t pid, int signo);
int raise(int signo);

// Both return: 0 if OK, -1 on error

 kill system call은 process나 process group의 모든 process에게 signal을 전달한다.

kill의 pid 값에 따라서 다음과 같이 분류된다.

pid >  0 The signal is sent to the process whose process ID is pid.
특정 한 개의 process
pid == 0 The signal is sent to all processes whose process group ID equals the process group ID of the sender
sender와 같은 group에 속한 process들
pid <  0 The signal is sent to all processes whose process group ID equals the absolute value of pid.
절댓값에 해당하는 group에 속한 process들
pid ==-1 The signal is sent to all processes on the system for which the sender has permission to send the signal.
sender가 permission을 가진 모든 process들

raise는 스스로에게 signal을 전달한다.

 

signal을 보내는 permission에 대해서 알아보자

super-user는 모든 process에게 signal을 보낼 수 있다.

sender의 real or effective user ID가 receiver의 real or effective user ID와 같아야 한다. 

 

example

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

int ntimes = 0;

main(){
  pid_t pid, ppid;
  void p_action (int), c_action (int);
  static struct sigaction pact, cact;

  /* 부모를 위해 SIGUSR1 행동을 지정한다*/
  pact.sa_handler = p_action;
  sigaction (SIGUSR1, &pact, NULL);

  switch (pid = fork()){
    case -1;		/* 오류 */
      perror ("synchro");
      exit (1);
    case 0: 		/* 자식 */
      /* 자식을 위해 행동을 지정 */
      cact.sa_handler = c_action;
      sigaction (SIGUSR1, &cact, NULL);

      /* 부모의 프로세스 식별번호를 얻음 */
      ppid = getppid();
      for (;;)
      {
        sleep (1);
        kill (ppid, SIGUSR1);
        pause();
      }
      /* 결코 퇴장(exit) 않음. */
    default: 		/*  부모 */
      for(;;)
      {
        pause();
        sleep (1);
        kill (pid, SIGUSR1);
      }
      /* 결코 퇴장(exit) 않음 */
  }
}

void p_action (int sig){
  printf("Parent caught signal #%d\n", ++ntimes);
}

void c_action (int sig){
  printf("Child caught signal #%d\n", ++ntimes);
}

child에서 kill을 통해 SIGUSR1을 전달하면, parent의 pause에서 signal을 받아 p_action을 실행한다. 

이후 parent에서 kill로 child에게 SIGUSR1을 전달하면, child는 kill이후의 pause에서 이를 받아 c_action을 실행한다. 

이과 같은 과정을 계속 반복한다.

 

 

Null signal

POSIX 1에서 null signal을 signo 0번으로 정의하였다.특정 프로세스가 존재하는지 확인하는 용도로 사용한다. 존재하지 않으면 kill의 return으로 -1을 반환하고, errno에 ESRCH가 설정된다. atomic이 보장되지 않는데, 이는 kill의 return이 하나의 결과로 유효하지 않음을 의미한다. 

 

 

The alarm(2) system call

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

// Returns: 0 or number of seconds until previously set alarm

지정된 Timer 시간이후 스스로에게 SIGALRM을 전달하는 system call

return은 0이나 이전에 설정했던 alarm의 남은 시간이다. 

 

signal alarm의 default action은 terminate이다. 

process당 alarm clock은 하나만 존재한다. 

alarm을 호출했을 때, 이전에 할당된 alarm은 만료되지 않은 상태라면

  • 남은 시간이 return된다
  • 이전의 할당되었던 alarm은 새 값으로 할당된다

alarm(0)의 경우 이전 알람을 cancel하는 역할을 한다. 

SIGALRM을 catch 하였을 때, alarm을 호출 하기 전에 signal handler를 설정하지 않았다면 terminate가 실행된다. 

 

example

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

#define TIMEOUT    5 /* 초단위 */
#define MAXTRIES   5
#define LINESIZE   100
#define CTRL_G	'\007' /*벨*/
#define TRUE       1
#define FALSE      0

/*타임아웃이 발생했는지 알아보기 위해 사용*/
static int timed_out;

/* 입력줄을 보관한다. */
static char answer[LINESIZE];	

char *quickreply (char *prompt){
  void catch (int);
  int ntries;
  static struct sigaction act, oact;

  /* SIGALRM을 포착, 과거 행동을 저장*/
  act.sa_handler = catch;
  sigaction (SIGALRM, &act, &oact);
    for (ntries=0;ntries<MAXTRIES;ntries++)  {
    timed_out = FALSE;
    printf ("\n%s > ", prompt);

    alarm (TIMEOUT); /* 얼람 시계를 설정*/
    gets (answer); /* 입력줄을 가져온다*/
    alarm (0); /* 얼람을 끈다. */

    /* timed_out이 참이면,응답 없는 경우*/
    if (!timed_out) break;
  }

  /*과거행동 복원*/
  sigaction (SIGALRM, &oact, NULL);

  /* 적절한 값을 복귀*/
  return (ntries==MAXTRIES ?             ((char *)0):answer);
}
 /* SIGALRM을 받으면 수행한다. */
void catch (int sig) {
  timed_out = TRUE; /*타임아웃 플래그 설정*/
  putchar (CTRL_G); /* 벨을 울린다. */
}

 

 

The pause(2) system call

#include <unistd.h>

int pause(void);
 
// Returns: -1 with errno set to EINTR

signal이 catch 될 때까지 process 호출을 중단시킨다.

signal catching 함수가 실행되면, signal catching 함수의 return 이후에 pause가 return한다. 

 

 

728x90
반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유