POSIX Signals

 

Standard Signals

  • Queueing and delivery semantics for standard signals
    • If multiple standard signals are pending for a process, the order in which the signals are delivered is unspecified
    • Standard signals do not queue → 시그널은 저장되지 않고, 그냥 도착했다는 사실밖에 알 수 없다.
  • e.g., signal(), kill()

 

Real-Time Signals

  • SIGRTMIN ~ SIGRTMAX
    • Support at least _POSIX_RTSIG_MAX (8) real-time signals → 적어도 8개의 RT signal을 지원해야 한다.
    • Lower number signals are given priority over higher number signals → 낮은 번호의 시그널에 대한 핸들러먼저 정의해야한다.
  • e.g, sigaction(), sigqueue()

 

Structures for Real-Time Signals

 

struct sigaction{
    void (*sa_handler) (int); /* standard sig handler, for standard signal */
    void (*sa_sigaction) (int, siginfo_t *, void *); /* for RT signal */
    sigset_t sa_mask; /* for signal masking */
    int sa_flags;
    void (*sa_restorer) (void);
}

struct siginfo_t {
    int si_signo; /* signal number */
    ...
    int si_int; /* number data */
    void *si_ptr; /* pointer */
    ...
}

union sigval {
    int sival_int;
    void *sival_ptr;
}

 

+ sigaction의 sa_handler는 standard signal handler를 위한 함수 포인터이고, sa_sigaction은 RT signal handler를 위한 함수 포인터이다.

 

+ siginfo_t * 를 통해 signal을 받았을 때, 같이 온 데이터들을 볼 수 있다.

 

+ siginfo_t * 를 통해 볼 수 있는 데이터를 union sigval을 통해 정의한다. sival_int는 숫자 데이터로 siginfo_t의 si_int와 연결된다. *sival_ptr은 보내고 싶은 데이터가 숫자가 아니어서 그 데이터의 주소값을 보내야할 경우 사용하는 포인터 변수이다. 하지만, 다른 프로세스끼리는 다른 가상공간을 사용하기에 그냥 주소값을 보내면 안되고, 다른 방법을 이용해야한다.

 

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

 

아래의 코드는 signal()을 sigaction()을 이용해서 구현한 코드이다:

handler_t *Signal(int signum, handler_t *handler)
{
    struct sigaction action, old_action;
    
    action.sa_handler = handler;
    sigemptyset(&action.sa_mask); /* Block sig of type being handled */
    action.as_flags = SA_RESTART; /* Restart syscalls if possible */
    
    if(sigaction(signum, &action, &old_action) < 0){
    	unix_error("signal error!");
    return old_action.as_handler;
}

 

+ action.sa_handler를 handler로 설정해줘서 standard signal에 대한 handler를 설정해준다.

 

+ action.as_mask를 sigemptyset을 통해 초기화해줘서 handler가 동작하는 동안 signal을

받을 수 있게 해준다.

 

+ sa_flags에 SA_RESTART를 넣어주어서 signal이 오면 가능한 재시작할 수 있게 해준다.

 

+ sigaction()은 기존 siganl()과 다르게 정상적으로 동작하면 0을 return, 실패시 -1을 return한다.

 

Real-Time Signals

 

Real-Time signals can carry additional signal data

  • sigqueue(pid_t pid, int sig, const union sigval value)
    • value data: data(either an integer or a pointer value) to be sent with the signal
    • The receiver of the signal can obtaion the accompanying data by installing a signal handler with the sigaction() SA_SIGINFO flag → SA_SIGINFO를 sigaction.sa_flags에 넣어야 signal과 같이 오는 정보를 받아볼 수 있다. 또한 SA_SIGINFO flag가 켜져있지 않으면 sa_sigaction이 아닌, sa_handler를 호출한다. 이때 SA_SIGINFO와 SA_RESTART를 같이 flags값이 넣어주고 싶다면 | ('or' bit-wise operation)를 통해 같이 넣어줄 수 있다. 

https://velog.io/@oio337a/sigaction

 

sigaction

sigaction 은 signal 함수보다는 조금 더 다양한 기능을 지원하는 시그널 함수입니다. sigaction 함수는 <sys/signal.h>에 정의된 sigaction 구조체를 활용하며, sigaction 구조체가 지원하는 다양한 기능을 사용

velog.io

 

 

Real-Time Signal Example

 

int count = 0;

void print_siginfo(siginfo_t *info){
    printf("si_signo : %d\n", info->si_signo);
	printf("si_errno : %d\n", info->si_errno);
	printf("si_code : %d\n", info->si_code);
	
    printf("si_pid : %d\n", info->si_pid);
	printf("si_uid : %d\n", info->si_uid);
	printf("si_status : %d\n", info->si_status);
	
    printf("union sigval si_value (int si_int, void *si_ptr)\n");
    printf("-> si_int : %d\n", info->si_int);
	printf("-> si_ptr : %p\n", info->si_ptr);
	printf("\n");
}

void example_handler(int signo, siginfo *info, void *context)
{
    printf("PID: %d receives signal(%d). Increase count (%d -> %d)\n",
    	getpid(), signo, count++, count);
    
    print_siginfo(info);
}

int main()
{
    /* codes */
    struct sigaction new_act, old_act;
    union sigval value;
    
    sigemptyset(&new_act.sa_mask);
    new_act.sa_flags = SA_SIGINFO;
    new_act.sa_sigaction = example_handler;
    sigaction(__SIGNAL__, &new_act, &old_act);
    
    sigemptyset(&new_mask);
    sigaddset(&new_mask, __SIGNAL__);
    
    if(!(pid=fork()){ /* Child */
    	sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
        printf("[Child] Wait 3 seconds with the signal blocked!\n");
        sleep(3);
        sigprocmask(SIG_SETMASK, &old_mask, NULL);
        print("[Child] count: %d\n", count);
    }
    else{ // Parent
    	printf("[Parent] Parent's pid is %d\n", getpid());
        
        for(int i=0; i<3; i++){
        	value.sival_int = (i+1) * 1024;
            sigqueue(pid, __SIGNAL__, value);
            printf("[Parent] Send the signal (%d) to child (%d) with 
            	sival_int: %d\n", __SIGNAL__, pid, value.sival_int);
        }
    	
    	if(waitpid(pid, NULL, 0) == pid)
        	printf("[Parent] Child (%d) is terminated!\n", pid);
        printf("[Parent] count: %d\n", count);
    }
    
    return 0;
}

 

__SIGNAL__을 standard signal로 정의한 경우:

 

__SIGNAL__을 RT signal로 정의한 경우:

 

Assembly Function Calls

 

Machanisms in Procedures

 

  • Passing control
    • To beginning of procedure code
    • Back to return point
  • Passing data
    • Procedure arguments
    • Return value
  • Memory management
    • Allocate during procedure execution
    • Deallocate upon return
  • Mechanisms all implemented with machine instructions

 

x86-64 Stack

 

stack

 

+ 위 그림은 가상 메모리 공간상의 stack을 말한다.

  • Region of memory managed with stack discipline
  • Grows toward lower addresses
  • Register %rsp contains lowest stack address; address of 'top' element

 

x86-64 Stack: Push and Pop

 

  • pushq Src
    • Fetch operand at Src
    • Decrement %rsp by 8; because size of register is 64bits in x86-64
    • Write operand at address given by %rsp
  • popq Dest
    • Read value at address given by %rsp
    • Increment %rsp by 8
    • Store value at Dest (must be register)

 

Procedure Control Flow

 

Use stack to support procedure call and return

  • Procedure call: call label (함수 이름, 함수 포인터, 함수 주소값)
    • push return address on stack
    • Jump to label
  • Return address:
    • Address of the next instruction right after call
    • Example from disassembly
  • Procedure return: ret
    • Pop address from stack
    • Jump to address

 

#1
#2
#3
#4

 

Registers and stack

 

 

+ Stack에는 변수가 너무 많아서 register가 6개 이상 필요할 경우 사용되는데, 이때는 뒤쪽 arguments부터 먼저 집어넣어서 앞쪽 arguments먼저 꺼낼 수 있게 한다.

 

+ 이후의 내용은 다음의 블로그를 참고

https://velog.io/@msung99/%EC%8B%9C%EC%8A%A4%ED%85%9C-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-4-2%EC%A3%BC%EC%B0%A8-4ngkpnns

 

어셈블리어 x86-64 시스템의 Stack 구조

그림을 잘 보면 스택의 bottom 이 위쪽에 있고, top 이 아래쪽에 있다!stack은 메모리를 더 사용할수록 밑으로 자라난다.(cf. heap 은 위로 자라남)rsp 라는 특수한 레지스터가 스택의 맨 아래 주소를 가

velog.io