Arm Linux Kernel Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

81112
549
416221


[ARM64]signal - sa_handler signal handler 실행 (code walk-through) 4. 프로세스(Process) 관리


signal에 대해서는 리눅스 시스템 프로그램에서 많이 들어본 적이 있을 꺼에요.
쓰레드에 특정 시그날이 전달되면 해당 시그널 핸들러 함수가 호출된다는 내용이죠.

아래 시스템 프로그램의 경우에는 SIGINT 시그널을 전달 받으면 sig_handler 함수가 호출되겠지오.
#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
  if (signo == SIGINT)
    printf("received SIGINT\n");
}

int main(void)
{
  if (signal(SIGINT, sig_handler) == SIG_ERR)
  printf("\ncan't catch SIGINT\n");
  // A long long wait so that we can easily issue a signal to this process
  while(1) 
    sleep(1);
  return 0;
}

그럼 이 코드가 리눅스 커널에서는 어떤 흐름으로 처리되는지 순서대로 살펴볼께요.

시스템에서 1초 안에 200번 정도의 인터럽트가 뜨는데요. 유저 공간에서 프로그램이 실행되다가 인터럽트가 뜨면,
el0_irq_compat 벡터로 프로그램 카운터가 이동을 하고 아래 흐름으로 코드가 수행이 되요.
el0_irq_naked -> ret_to_user -> work_pending -> do_notify_resume

el0_irq_compat:
kernel_entry 0, 32
b el0_irq_naked
el0_irq_naked:
enable_dbg
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_off
#endif

ct_user_exit
irq_handler

#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
#endif
b ret_to_user
ENDPROC(el0_irq)
ret_to_user:
disable_irq // disable interrupts
ldr x1, [tsk, #TI_FLAGS]
and x2, x1, #_TIF_WORK_MASK
cbnz x2, work_pending
enable_step_tsk x1, x2
no_work_pending:
kernel_exit 0
ENDPROC(ret_to_user)
work_pending:
tbnz x1, #TIF_NEED_RESCHED, work_resched
/* TIF_SIGPENDING, TIF_NOTIFY_RESUME or TIF_FOREIGN_FPSTATE case */
ldr x2, [sp, #S_PSTATE]
mov x0, sp // 'regs'
tst x2, #PSR_MODE_MASK // user mode regs?
b.ne no_work_pending // returning to kernel
enable_irq // enable interrupts for do_notify_resume()
bl do_notify_resume
b ret_to_user

asmlinkage void do_notify_resume(struct pt_regs *regs,
unsigned int thread_flags)
{
if (thread_flags & _TIF_SIGPENDING)
do_signal(regs);


이후 do_signal -> handle_signal -> setup_rt_frame 코드를 타게 되죠.
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
{
struct task_struct *tsk = current;
sigset_t *oldset = sigmask_to_save();
int usig = ksig->sig;
int ret;
// .. 생략 ..
if (is_compat_task()) {
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
ret = compat_setup_rt_frame(usig, ksig, oldset, regs);
else
ret = compat_setup_frame(usig, ksig, oldset, regs);
} else {
ret = setup_rt_frame(usig, ksig, oldset, regs);
}

static int setup_rt_frame(int usig, struct ksignal *ksig, sigset_t *set,
  struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
int err = 0;

frame = get_sigframe(ksig, regs);
if (!frame)
return 1;

__put_user_error(0, &frame->uc.uc_flags, err);
__put_user_error(NULL, &frame->uc.uc_link, err);

err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
err |= setup_sigframe(frame, regs, set);
if (err == 0) {
setup_return(regs, &ksig->ka, frame, usig);
if (ksig->ka.sa.sa_flags & SA_SIGINFO) {

결국 아래 "regs->pc = (unsigned long)ka->sa.sa_handler;" 코드에서 유저 스페이스에서
등록한 sig_handler() 함수가 실행되요.
static void setup_return(struct pt_regs *regs, struct k_sigaction *ka,
void __user *frame, int usig)
{
__sigrestore_t sigtramp;

regs->regs[0] = usig;
regs->sp = (unsigned long)frame;
regs->regs[29] = regs->sp + offsetof(struct rt_sigframe, fp);
regs->pc = (unsigned long)ka->sa.sa_handler;

if (ka->sa.sa_flags & SA_RESTORER)
sigtramp = ka->sa.sa_restorer;
else
sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp);

regs->regs[30] = (unsigned long)sigtramp;
}


핑백

덧글

댓글 입력 영역