babyos2(18)—— signal,kill,do_signal,sig_return
2017-12-28 21:40
507 查看
signal是一种IPC方式,babyos2准备实现一个比较简单的模型,只探究它的基本原理,不深究各个信号具体意义及相互关系等。
基本思路:
1)每个进程有一个信号队列,存放已收到未处理的信号
2)一个进程要发送一个信号到另一个进程,通过系统调用sys_kill进入内核,通过pid找到接受信号的进程,并给它的信号队列新增加一个信号
3)每个进程中断返回之前,检查有没有未处理的信号,若有则去处理
process_t类增加下面的成员
sys_kill:
do_signal:
中断返回前,检查是否有未处理的信号,若有调用do_signal处理
暂时收到信号只打印一条信息。
init每隔2s发送一个signal给shell:
因为是在中断返回前检测信号,所以处于内核态,怎样能回到用户态并执行用户态注册的的信号处理函数,且执行完成后回到中断前的地方继续执行呢?
1)保存中断的context,将中断返回后的eip改为用户态信号处理函数
2)在用户态栈上构造一个frame,模拟函数调用sighandler的场景,并指定一个返回地址
3)返回地址指到栈上一个位置,这个位置放置:
movl SYSSIGRET,int0x80
4)则信号处理完成返回的时候,会执行一个系统调用sys_sigreturn,这个系统调用恢复上次中断的context,则从sys_sigreturn中断返回后回到信号处理前那次中断前的地方继续执行。
注册信号处理函数:
处理信号:
sig return:
shell中注册信号处理函数:
信号处理函数打印SIG_no,如图所示,能够成功注册、发送、处理信号。
这样一个最最简单的信号模型就建立好了,暂时只搞明白它的基本原理,像sig mask之类的都暂时不处理,SIG_DFL,默认行为是忽略信号。
基本思路:
1)每个进程有一个信号队列,存放已收到未处理的信号
2)一个进程要发送一个信号到另一个进程,通过系统调用sys_kill进入内核,通过pid找到接受信号的进程,并给它的信号队列新增加一个信号
3)每个进程中断返回之前,检查有没有未处理的信号,若有则去处理
/* * 2017-12-28 * guzhoudiaoke@126.com */ #ifndef _SIGNAL_H_ #define _SIGNAL_H_ #include "types.h" #include "spinlock.h" #include "atomic.h" #define NSIG 32 typedef void (*sighandler_t) (int32); typedef uint64 sigset_t; typedef struct sigaction_s { sighandler_t m_handler; uint64 m_flags; sigset_t m_mask; } sigaction_t; typedef struct siginfo_s { uint32 m_sig; uint32 m_pid; } siginfo_t; class signal_t { public: signal_t(); ~signal_t(); void init(); void copy(const signal_t& signal); void lock(); void unlock(); void set_sigaction(uint32 sig, const sigaction_t& sa); static int32 do_sigaction(uint32 sig, sighandler_t sig_handler); static int32 do_send_signal(uint32 pid, uint32 sig); static int32 do_sigreturn(); public: private: atomic_t m_count; sigaction_t m_action[NSIG]; spinlock_t m_lock; }; #endif
process_t类增加下面的成员
uint32 m_sig_pending; signal_t m_signals; list_t<siginfo_t> m_sig_queue; sigset_t m_sig_blocked; spinlock_t m_sig_mask_lock;
sys_kill:
int32 syscall_t::sys_kill(trap_frame_t* frame) { uint32 pid = frame->ebx; uint32 sig = frame->ecx; return signal_t::do_send_signal(pid, sig); } int32 signal_t::do_send_signal(uint32 pid, uint32 sig) { siginfo_t si; si.m_sig = sig; si.m_pid = current->m_pid; return os()->get_arch()->get_cpu()->send_signal_to(si, pid); } int32 cpu_t::send_signal_to(const siginfo_t& si, uint32 pid) { console()->kprintf(WHITE, "send_signal, "); cli(); process_t* p = find_process(pid); if (p != NULL) { p->m_sig_queue.push_back(si); p->calc_sig_pending(); } sti(); return 0; }
do_signal:
reschedule: call schedule jmp restore_all dosignal: pushl %esp call do_signal addl $4, %esp jmp restore_all common_isr: SAVE_ALL GET_CURRENT(%ebx) movl $(SEG_KDATA<<3),%edx movw %dx, %ds movw %dx, %es movw %dx, %fs movw %dx, %gs pushl %esp call do_common_isr addl $4, %esp ret_from_isr: cli cmpl $0, need_resched(%ebx) jne reschedule cmpl $0, sig_pending(%ebx) jne dosignal restore_all: RESTORE_ALL addl $8, %esp iret
中断返回前,检查是否有未处理的信号,若有调用do_signal处理
extern "C" void do_signal(trap_frame_t* frame) { os()->get_arch()->get_cpu()->do_signal(frame); } void cpu_t::do_signal(trap_frame_t* frame) { while (!current->m_sig_queue.empty()) { siginfo_t si = *current->m_sig_queue.begin(); console()->kprintf(PINK, "proc %u get signal SIG_%u from proc %u\n", current->m_pid, si.m_sig, si.m_pid); current->m_sig_queue.pop_front(); } }
暂时收到信号只打印一条信息。
init每隔2s发送一个signal给shell:
int main() { uint32 cs = 0xffffffff; __asm__ volatile("movl %%cs, %%eax" : "=a" (cs)); // print cs to show work in user mode userlib_t::print("This is printed by init, cs = "); userlib_t::print_int(cs, 16, 0); userlib_t::print("\n"); // fork int32 pid = userlib_t::fork(); if (pid == 0) { // child pid = userlib_t::exec(SHELL_LBA, SHELL_SECT_NUM, "shell"); if (pid != 0) { userlib_t::print("BUG exec failed!!!\n"); } } else { //userlib_t::wait(pid); // parent while (1) { userlib_t::sleep(2); userlib_t::print("I,"); userlib_t::kill(pid, 4); } } return 0; }
因为是在中断返回前检测信号,所以处于内核态,怎样能回到用户态并执行用户态注册的的信号处理函数,且执行完成后回到中断前的地方继续执行呢?
1)保存中断的context,将中断返回后的eip改为用户态信号处理函数
2)在用户态栈上构造一个frame,模拟函数调用sighandler的场景,并指定一个返回地址
3)返回地址指到栈上一个位置,这个位置放置:
movl SYSSIGRET,int0x80
4)则信号处理完成返回的时候,会执行一个系统调用sys_sigreturn,这个系统调用恢复上次中断的context,则从sys_sigreturn中断返回后回到信号处理前那次中断前的地方继续执行。
注册信号处理函数:
void userlib_t::signal(uint32 sig, sighandler_t handler) { __asm__ volatile("int $0x80" : : "b" (sig), "c"(handler), "a" (SYS_SIGNAL)); } int32 syscall_t::sys_signal(trap_frame_t* frame) { uint32 sig = frame->ebx; sighandler_t sig_handler = (sighandler_t) frame->ecx; return signal_t::do_sigaction(sig, sig_handler); } void signal_t::lock() { m_lock.lock(); } void signal_t::unlock() { m_lock.unlock(); } void signal_t::set_sigaction(uint32 sig, const sigaction_t& sa) { m_action[sig].m_handler = sa.m_handler; m_action[sig].m_flags = sa.m_flags; m_action[sig].m_mask = sa.m_mask; } int32 signal_t::do_sigaction(uint32 sig, sighandler_t sig_handler) { if (sig < 1 || sig >= NSIG) { return -1; } sigaction_t sa; sa.m_handler = sig_handler; current->m_signals.lock(); current->m_signals.set_sigaction(sig, sa); current->m_signals.unlock(); return 0; }
处理信号:
void cpu_t::do_signal(trap_frame_t* frame) { if (!current->m_sig_queue.empty()) { siginfo_t si = *current->m_sig_queue.begin(); current->m_sig_queue.pop_front(); signal_t::handle_signal(frame, si); } } int32 signal_t::handle_signal(trap_frame_t* frame, const siginfo_t& si) { uint32 sig = si.m_sig; sigaction_t* action = ¤t->m_signals.m_action[sig]; if (action->m_handler == SIG_DFL) { return -1; } /* get sig frame */ sigframe_t* sig_frame = (sigframe_t *) ((frame->esp - sizeof(sigframe_t)) & -8UL); sig_frame->m_sig = si.m_sig; /* save trap frame into sig frame */ memcpy(&sig_frame->m_trap_frame, frame, sizeof(trap_frame_t)); /* return addr */ sig_frame->m_ret_addr = sig_frame->m_ret_code; /* return code: this is movl $,%eax ; int $0x80 */ sig_frame->m_ret_code[0] = 0xb8; *(int *) (sig_frame->m_ret_code+1) = SYS_SIGRET; *(short *) (sig_frame->m_ret_code+5) = 0x80cd; /* set eip to sig handler */ frame->eip = (uint32) action->m_handler; /* set esp as sig frame */ frame->esp = (uint32) sig_frame; return 0; }
sig return:
int32 syscall_t::sys_sigret(trap_frame_t* frame) { return signal_t::do_sigreturn(frame); } int32 signal_t::do_sigreturn(trap_frame_t* frame) { sigframe_t* sig_frame = (sigframe_t *) (frame->esp - 4); memcpy(frame, &sig_frame->m_trap_frame, sizeof(trap_frame_t)); return 0; }
shell中注册信号处理函数:
/* * guzhoudiaoke@126.com * 2017-12-10 */ #include "shell.h" #include "userlib.h" void process_signal(int32 sig) { userlib_t::print("SIG_"); userlib_t::print_int(sig, 10, 1); userlib_t::print(","); } int main() { sighandler_t handler = &process_signal; userlib_t::signal(4, handler); userlib_t::print("This is printed by shell.\n"); while (1) { userlib_t::sleep(2); userlib_t::print("S,"); } userlib_t::exit(0); return 1; }
信号处理函数打印SIG_no,如图所示,能够成功注册、发送、处理信号。
这样一个最最简单的信号模型就建立好了,暂时只搞明白它的基本原理,像sig mask之类的都暂时不处理,SIG_DFL,默认行为是忽略信号。
相关文章推荐
- 信号处理篇alarm ferror kill mkfifo pause pclose perror pipe popen sigaction sigaddset sigdelset sigemptyset signal sleep strerror
- 信号处理篇alarm ferror kill mkfifo pause pclose perror pipe popen sigaction sigaddset sigdelset sigemptyset signal sleep strerror
- 信号相关函数(signal,sigaction,sigprocmask, kill,sigqueue信号发送函数,睡眠函数,计时器函数)
- linux学习---信号(signal,sigaction,kill,sigqueue,sigprocmask,sigpending,sigsuspend)
- sh: line 0: kill: SIGUSR1: invalid signal specification
- sh: line 0: kill: SIGUSR1: invalid signal specification
- Program received signal :"SIGKILL"
- Linux的Signal及Singal处理(SIGQUIT SIGKILL SIGBUS等)
- Program terminated with signal SIGKILL,Killed
- SpringMVC利用return new ModelAndView(new RedirectView("xxx.do"), Map map)重定向传递多参数
- 信号:signal(), sigaction(), sigaddset(), sigemptyset(), sigismember(), sigprocmask()
- 解决:[WARNING] fpm_children_bury(), line 215: child 2736 (pool default) exited on signal 15 SIGTERM after 59.588363 seconds from start
- signal(SIGPIPE, SIG_IGN)
- PYQT TIP : QSpinBox signal waits for return key to be pressed
- Linux中与进程终止相关的信号SIGTERM,SIGKILL,SIGINT
- signal(SIGPIPE, SIG_IGN)详解
- signal(SIGCHLD, SIG_IGN)
- python main函数中变量默认为global variable & SIGKILL SIGSTOP 无法捕获
- signal系列之SIGQUIT
- signal signal函数每次设置具体的信号处理函数(非SIG_IGN)只能生效一次,多次调用需要调用时在加类似监听的方法!!! 最好用sigaction