Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


[Linux][Kernel] signal - signal handler data structure 4. Process Management

리눅스 커널 코드 리뷰를 많이 했는데요.
리눅스 시스템 프로그램이 커널과 어떻게 연동되는지 한 가지 점검해볼께요.

유저 공간에서 signal을 설정하는 코드를 많이 볼 수 있습니다.

아래는 SIGINT란 시그널이 전달되었을 때 linux_sig()란 함수가 호출되는 간단한 코드입니다.
그럼 아래 시그널 콜백 함수가 어떻게 실행이 될까요?
#include <stdio.h>
#define SIGINT  (2)

typedef void (*handler_t)(int);

handler_t signal( int, handler_t );

handler_t old;
void linux_sig( int signo )
{
printf("linux_sig(%d)\n", signo );
signal( SIGINT, old );
}

int main()
{
old = signal( SIGINT, linux_sig);
while(1)
;
return 0;
}

- 설정 코드
signal 관련해서 중요한 task descriptor의 멤버 변수가 있는데요.
각각 멤버에 대해서 살펴볼께요.

struct task_struct {
    volatile long state;
    void *stack;
// ..생략...
   struct signal_struct *signal;
   struct sighand_struct *sighand;
   sigset_t blocked;
   sigset_t saved_sigmask;
   struct sigpending pending;
   

sighand
struct task_struct.sighand.action[0--64].sa_handler에 sig handler 주소를 설정합니다.
위 예제의 경우 task_struct.sighand.action[SIGINT].sa_handler =  my_sig 이렇게 되는거죠.

타입은 struct sighand_struct* 이구요. 여기서 중요한 멤버가 action인데요. 64만큼의 배열로 되어 있어요.
조금 더 자세히 짚어보면 struct task_struct.sighand.action[0--64]에 위치하고 있어요.

struct task_struct.sighand.action의 각각 배열들은 각 signal에 대한 sig handle 정보를 담고 있어요. 
예를 들면, 아래와 같죠.

struct task_struct.sighand.action[SIGHUP]  // #define SIGHUP 1
struct task_struct.sighand.action[SIGINT]   // #define SIGINT 2
struct task_struct.sighand.action[SIGQUIT] // #define SIGQUIT 3
.. 생략..
struct task_struct.sighand.action[SIGTRAP] // #define SIGTRAP 5
struct task_struct.sighand.action[SIGABRT] // #define SIGABRT 6
.. 생략..
struct task_struct.sighand.action[ SIGKILL] // #define  SIGKILL 9


위에서 만든 my_sig 프로세스의 task_descriptor가 d7e09840이면,
crash tool로 아래와 같이 각각 멤버 변수를 확인할 수 있어요.

crash> struct task_struct.sighand d7e09840
  sighand = 0xdab7bec0

crash> struct sighand_struct 0xdab7bec0 -px
struct sighand_struct {
  count = {
    counter = 0x86
  },
  action = {{
      sa = {
        sa_handler = 0x0,
        sa_flags = 0x0,
        sa_restorer = 0x0,
        sa_mask = {
          sig = {0x0, 0x0}
        }
      }
    }, {
      sa = {  //<<-- SIGHUP
        sa_handler = 0x0,
        sa_flags = 0x0,
        sa_restorer = 0x0,
        sa_mask = {
          sig = {0x0, 0x0}
        }
      }
    }, {
      sa = {  //<<--SIGINT
        sa_handler = 0xa71eacad //<<-- linux_sig의 가상 메모리(유저 공간) 주소
        sa_flags = 0x1c000004,
        sa_restorer = 0xa723ea08,
        sa_mask = {
          sig = {0xfffbfeff, 0x0}
        }
      }
    }, {
      sa = {  //<<-- SIGQUIT
        sa_handler = 0x0,
        sa_flags = 0x0,
        sa_restorer = 0x0,
        sa_mask = {
          sig = {0x0, 0x0}
        }
      }

signal 관련해서 struct sighand_struct 구조체는 아래와 같아요.
crash> struct sighand_struct
struct sighand_struct {
    atomic_t count;
    struct k_sigaction action[64];
    spinlock_t siglock;
    wait_queue_head_t signalfd_wqh;
}

crash> struct k_sigaction
struct k_sigaction {
    struct sigaction sa;


pending
해당 프로세스에 대해 시그널을 설명하면, struct task_struct.pending.signal.sig란 Vector Hash Table에 해당 시그널 필드를 0x1로 설정하죠.
위 예제의 경우 SIGIN 필드(0x2)가 0x1로 설정 되죠.
                 
sig |0|0|0|0|1|0|0|

crash> struct task_struct.pending d7e09840
  pending = {
    list = {
      next = 0xd7e09dd8,
      prev = 0xd7e09dd8
    },
    signal = {
      sig = {2, 0}
    }
  }
    
get_signal-> dequeue_signal-> __dequeue_signal 함수 흐름으로 아래 함수가 호출되는데요.
struct task_struct.pending 필드를 확인한 다음에 해당 프로세스에 signal을 전달합니다.
 
int next_signal(struct sigpending *pending, sigset_t *mask)
{
unsigned long i, *s, *m, x;
int sig = 0;

s = pending->signal.sig; //<<--
m = mask->sig; 
... 생략 ...
return sig;
}
 
static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
siginfo_t *info, bool *resched_timer)
{
int sig = next_signal(pending, mask);

if (sig) {
if (current->notifier) {
if (sigismember(current->notifier_mask, sig)) {
if (!(current->notifier)(current->notifier_data)) {
clear_thread_flag(TIF_SIGPENDING);
return 0;
}
}
}

collect_signal(sig, pending, info, resched_timer);
}

pending 시그널 정보를 가져오는 매크로는 아래와 같네요.
#define PENDING(p,b) has_pending_signals(&(p)->signal, (b))

Reference(프로세스 관리)


핑백

덧글

  • linu 2019/03/29 11:15 # 삭제 답글

    안녕하세요 핑백에 올려주신 링크는 내용과 관련이 있는 것인가요?? 순서상으로는 너무 뒤에 있는 내용이라 뭐부터 봐야할지 햇갈려서요...
  • AustinKim 2019/03/29 12:35 #

    포스팅한 내용은 시그널을 전달하거나 설정할 때 태스크 디스크립터 자료구조에 대한 내용입니다.
    관련 코드 리뷰는 프로세스 자료구조에 더 가까운 내용인 것 같네요.
댓글 입력 영역