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
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을 말한다.
- 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
Registers and stack
+ Stack에는 변수가 너무 많아서 register가 6개 이상 필요할 경우 사용되는데, 이때는 뒤쪽 arguments부터 먼저 집어넣어서 앞쪽 arguments먼저 꺼낼 수 있게 한다.
+ 이후의 내용은 다음의 블로그를 참고
'[학교 수업] > [학교 수업] 시스템 프로그래밍' 카테고리의 다른 글
[시스템 프로그래밍] Pipes (3) | 2024.10.15 |
---|---|
[시스템 프로그래밍] Inter-Process Communication (3) | 2024.10.11 |
[시스템 프로그래밍] Assembly Language (1) | 2024.10.06 |
[시스템 프로그래밍] Signal Examples (1) | 2024.10.06 |
[시스템 프로그래밍] Race Condition (0) | 2024.09.28 |