linux0.11信号处理之 exit.c
exit.c 程序解析
1 功能描述
该程序主要的作用是终止和退出的有关事宜。主要包括进程释放、会话终止和程序退出处理函数以及杀死进程,终止进程,挂起进程调用函数。还包括进程信号发送函数,以及通知父进程子进程终止的函数tell_father()。
释放进程的函数release() 主要根据制定的任务数据结构指针,在任务数组中删除指定的进程指针、释放内存页,并立刻让内核重新调度任务的运行。
kill_session() 函数通过向会话号与当前进程相同的进程发送挂断进程的信号。
sys_kill()用于向进程发送任何指定的信号。根据pid的不同参数值,该系统会向不同的进程发送任何指定的信号。
do_exit() 函数是在exit系统调用的中断处理程序中被调用。它首先会释放当前进程的内存页面。如果当前进程有子进程,就将子进程的father置为1 ,即把子进程的父进程变为进程1(init进程)。如果该子进程已经处于僵死的状态,那么向进程1发送子进程终止信号SIGCHLD 。接着关闭当前进程打开的所有文件,释放试验的中断设备。
协处理器设备,若当前进程是进程组的领头进程,则还需要终止所有相关进程。 随后把当前进程置位僵死状态,设置退出码,并向父进程发送子进程终止信号SIGCHLD。最后让内核重新调度任务运行。
2 代码示例
#include
<
errno.h
>
#include
<
signal.h
>
#include
<
sys
/
wait.h
>
#include
<
linux
/
sched.h
>
#include
<
linux
/
kernel.h
>
#include
<
linux
/
tty.h
>
#include
<
asm
/
segment.h
>
int
sys_pause(
void
) ;
//
把进程置为睡眠状态
int
sys_close(
int
fd) ;
//
关闭指定文件的系统调用
//
释放指定进程所占用的任务槽,及其任务数据结构占用的内存页面
void
release(
struct
task_struct
*
p)
{
int i ;
if(!p)
return ;
for(i = 1 ; i < NR_TASKS ; i++)
{
if(task[i] == p)
{
task[i] = NULL;
free_page((long)p) ;
schedule() ; //重新调度进程
return ;
}
}
panic("trying to release non - existent task");
}
//
向指定的任务发送信号
static
inline
int
send_sig(
long
sig ,
struct
task_struct
*
p ,
int
priv)
{
if(!p || sig < 1 || sig > 32)
return -EINVAL ;
if(priv || (current->euid == p->euid)|| suser()) //euid表示当前进程的权限
p->signal |= (1<<(sig - 1)) ;
else
return -EPERM ;
return 0 ;
}
//
终止会话session
static
void
kill_session(
void
)
{
struct task_struct **p = NR_TASKS + task ; //*p首先指向最后一个任务
//扫描任务指针数组,如果所有的任务的会话号等于当前进程的会话号,那么就向它发送终止信号
for(;p >= &FIRST_TASK ; p--)
{
if(*p && (*p)->session == current->session)
(*p)->signal |= 1 <<(SIGHUP - 1) ;
}
}
//
向进程组发送信号
//
这个函数用来向任何进程发送信号
int
sys_kill(
int
pid ,
int
sig)
{
struct task_struct **p = NR_TASKS + task ;
int err , retval = 0 ;
if(!pid) while(--p > &FIRST_TASK){ //如果当前进程pid==0。那么就会把信号发送给与当前进程在同一组中的进程
if(*p && (*p)->pgrp == current->pid)
if(err = send_sig(sig , *p , 1)) //强制发送
retval = err ;
} else if(pid > 0) while(--p > &FIRST_TASK){
if(*p && (*p)->pid == current->pid)
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
else if(pid == -1) while(--p > &FIRST_TASK){
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
else while(--p > &FIRST_TASK)
if(*p && (*p)->pgrp == -pid)
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
//
通知父进程--向进程pid发送信号SIGCHLD ;默认情况下子进程将停止或者终止
//
如果没有找到父进程,则自己释放。
static
void
tell_father(
int
pid)
{
int i ;
if(pid)
//扫描进程数组表寻找指定进程pid,并向其发送子进程将停止或者终止信号
for(i = 0 ; i < NR_TASKS ; i++)
{
if(!task[i])
continue ;
if(task[i]->pid != pid)
continue ;
task[i]->signal |= (1<<(SIGCHLD - 1)) ;
return ;
}
//如果没有找到父进程,则进程就自己释放。
printk("BAD BAD - no father found\b\r") ;
release(current) ;
}
//
程序退出处理函数。在下面的sys_exit()函数中被调用
int
do_exit(
long
code)
{
int i ;
free_page_tables(get_base(current->ldt[1]) , get_limit(0x0f)) ;
free_page_tables(get_base(current->ldt[2]) , get_limit(0x17)) ;
//如果当前进程有子进程,将子进程的father置为1。
//若该子进程已经处于僵死状态,则向进程1发送子进程终止信号SIGCHLD
//如果该子进程已经处于僵死状态,则向进程1发送子进程终止信号
for(i = 0 ; i < NR_TASKS ; i++)
if(task[i] && task[i]->father == current->pid)
{
task[i]->father = 1 ;
if(task[i]->state == TASK_ZOMBIE)
(void)send_sig(SIGCHLD , task[1] , 1) ;
}
//关闭当前进程打开着的全部文件
for(i = 0 ; i < NR_OPEN ;i++)
if(current->filp[i])
sys_close(i) ;
//对当前进程的工作目录pwd,跟目录root以及执行文件的i节点进行同步操作,放回各个i节点并分别置空
iput(current->pwd) ;
current->pwd = NULL ;
iput(current->root) ;
current->root = NULL ;
iput(current->executable) ;
current->executable = NULL ;
//如果当前进程是会话头领进程并且具有控制终端,则释放该终端
if(current->leader && current->tty >= 0)
tty_table[current->tty].pgrp = 0 ;
//如果当前进程上次使用过协处理器,则将last_task_used_math 置空
if(last_task_used_math == current)
last_task_used_math = NULL ;
//如果当前进程是leader进程,则终止该会话的所有相关进程
if(current->leader)
kill_session() ;
//把当前进程的状态变为僵死状态,表明当前进程已经释放了资源。并保存由父进程读取的退出码
current->state = TASK_ZOMBIE ;
current->exit_code = code ;
tell_father(current->father) ;//通知父进程,子进程将结束
schedule() ; //重新调度进程运行
return (-1) ;
}
//
系统调用exit()。终止进程
int
sys_exit(
int
error_code)
{
return do_exit((error_code & 0xff) << 8) ;
}
//
挂起当前进程,等待pid指定的子进程退出或者收到终止该进程的信号,或者是需要一个信号句柄
int
sys_waitpid(pid_t pid , unsigned
long
*
stat_addr ,
int
options)
{
int flag , code ; //flag标志作用于后面表示所选出的子进程处于就绪或者睡眠状态
struct task_struct ** p;
verify_area(stat_addr , 4) ;
repeat:
flag = 0 ;
//从任务数组末端开始扫描所有的任务,跳过空项,本进程项以及非当前进程的子进程项
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
{
if(!*p || *p == current) //跳过空项以及当前进程项
continue ;
if((*p)->father != current->pid) //跳过非当前进程的子进程项
continue ;
//此时选择到的进程一定是当前进程的子进程
//如果当前的pid>0,但是不等于参数pid
//就说明是当前进程其他的子进程
if(pid > 0)
{
if((*p)->pid != pid)
{
continue ;
}
} else if(!pid){ //如果pid==0,则表示正在等待组号等于当前进程的所有进程
if((*p)->pgrp != current->pgrp)
continue ;
}else if(pid != -1){ //如果pid<-1
if((*p)->pgrp != -pid)
continue ;
}
//如果前3个对pid的判断不符合标准,则表示当前进程正在等待其他任何子进程,即pid=-1的情况
//接下来根据子进程的状态来处理
switch((*p)->state){
case TASK_STOPPED :
if(!(options & WUNTRACED))
continue ;
put_fs_long(0x7f , stat_addr) ;
return (*p)->pid ;
case TASK_ZOMBIE : //如果子进程是僵死状态,那么首先把子进程的用户态时间和内核态时间加到当前进程中
current->cutime += (*p)->utime ;
current->cstime += (*p)->stime ;
flag = (*p)->pid ; //临时保存当前子进程的退出码
code = (*p)->exit_code ; //取当前进程的退出码
release(*p) ;
put_fs_long(code , stat_addr) ; //置状态信息为退出码
return flag ;
default :
flag = 1 ;
continue ;
}
}
if(flag){
if(options & WNOHANG)
{
return 0 ;
}
current->state = TASK_INTERRUPTIBLE ; //置当前进程为可中断状态
schedule() ; //重新运行
if(!(current->signal &= ~(1<<(SIGCHLD-1)) ))
goto repeat ;
else
return -EINTR ;
}
return -ECHILD ;
}
#include < signal.h >
#include < sys / wait.h >
#include < linux / sched.h >
#include < linux / kernel.h >
#include < linux / tty.h >
#include < asm / segment.h >
int sys_pause( void ) ; // 把进程置为睡眠状态
int sys_close( int fd) ; // 关闭指定文件的系统调用
// 释放指定进程所占用的任务槽,及其任务数据结构占用的内存页面
void release( struct task_struct * p)
{
int i ;
if(!p)
return ;
for(i = 1 ; i < NR_TASKS ; i++)
{
if(task[i] == p)
{
task[i] = NULL;
free_page((long)p) ;
schedule() ; //重新调度进程
return ;
}
}
panic("trying to release non - existent task");
}
// 向指定的任务发送信号
static inline int send_sig( long sig , struct task_struct * p , int priv)
{
if(!p || sig < 1 || sig > 32)
return -EINVAL ;
if(priv || (current->euid == p->euid)|| suser()) //euid表示当前进程的权限
p->signal |= (1<<(sig - 1)) ;
else
return -EPERM ;
return 0 ;
}
// 终止会话session
static void kill_session( void )
{
struct task_struct **p = NR_TASKS + task ; //*p首先指向最后一个任务
//扫描任务指针数组,如果所有的任务的会话号等于当前进程的会话号,那么就向它发送终止信号
for(;p >= &FIRST_TASK ; p--)
{
if(*p && (*p)->session == current->session)
(*p)->signal |= 1 <<(SIGHUP - 1) ;
}
}
// 向进程组发送信号
// 这个函数用来向任何进程发送信号
int sys_kill( int pid , int sig)
{
struct task_struct **p = NR_TASKS + task ;
int err , retval = 0 ;
if(!pid) while(--p > &FIRST_TASK){ //如果当前进程pid==0。那么就会把信号发送给与当前进程在同一组中的进程
if(*p && (*p)->pgrp == current->pid)
if(err = send_sig(sig , *p , 1)) //强制发送
retval = err ;
} else if(pid > 0) while(--p > &FIRST_TASK){
if(*p && (*p)->pid == current->pid)
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
else if(pid == -1) while(--p > &FIRST_TASK){
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
else while(--p > &FIRST_TASK)
if(*p && (*p)->pgrp == -pid)
if(err = send_sig(sig , *p , 0)) //强制发送
retval = err ;
}
// 通知父进程--向进程pid发送信号SIGCHLD ;默认情况下子进程将停止或者终止
// 如果没有找到父进程,则自己释放。
static void tell_father( int pid)
{
int i ;
if(pid)
//扫描进程数组表寻找指定进程pid,并向其发送子进程将停止或者终止信号
for(i = 0 ; i < NR_TASKS ; i++)
{
if(!task[i])
continue ;
if(task[i]->pid != pid)
continue ;
task[i]->signal |= (1<<(SIGCHLD - 1)) ;
return ;
}
//如果没有找到父进程,则进程就自己释放。
printk("BAD BAD - no father found\b\r") ;
release(current) ;
}
// 程序退出处理函数。在下面的sys_exit()函数中被调用
int do_exit( long code)
{
int i ;
free_page_tables(get_base(current->ldt[1]) , get_limit(0x0f)) ;
free_page_tables(get_base(current->ldt[2]) , get_limit(0x17)) ;
//如果当前进程有子进程,将子进程的father置为1。
//若该子进程已经处于僵死状态,则向进程1发送子进程终止信号SIGCHLD
//如果该子进程已经处于僵死状态,则向进程1发送子进程终止信号
for(i = 0 ; i < NR_TASKS ; i++)
if(task[i] && task[i]->father == current->pid)
{
task[i]->father = 1 ;
if(task[i]->state == TASK_ZOMBIE)
(void)send_sig(SIGCHLD , task[1] , 1) ;
}
//关闭当前进程打开着的全部文件
for(i = 0 ; i < NR_OPEN ;i++)
if(current->filp[i])
sys_close(i) ;
//对当前进程的工作目录pwd,跟目录root以及执行文件的i节点进行同步操作,放回各个i节点并分别置空
iput(current->pwd) ;
current->pwd = NULL ;
iput(current->root) ;
current->root = NULL ;
iput(current->executable) ;
current->executable = NULL ;
//如果当前进程是会话头领进程并且具有控制终端,则释放该终端
if(current->leader && current->tty >= 0)
tty_table[current->tty].pgrp = 0 ;
//如果当前进程上次使用过协处理器,则将last_task_used_math 置空
if(last_task_used_math == current)
last_task_used_math = NULL ;
//如果当前进程是leader进程,则终止该会话的所有相关进程
if(current->leader)
kill_session() ;
//把当前进程的状态变为僵死状态,表明当前进程已经释放了资源。并保存由父进程读取的退出码
current->state = TASK_ZOMBIE ;
current->exit_code = code ;
tell_father(current->father) ;//通知父进程,子进程将结束
schedule() ; //重新调度进程运行
return (-1) ;
}
// 系统调用exit()。终止进程
int sys_exit( int error_code)
{
return do_exit((error_code & 0xff) << 8) ;
}
// 挂起当前进程,等待pid指定的子进程退出或者收到终止该进程的信号,或者是需要一个信号句柄
int sys_waitpid(pid_t pid , unsigned long * stat_addr , int options)
{
int flag , code ; //flag标志作用于后面表示所选出的子进程处于就绪或者睡眠状态
struct task_struct ** p;
verify_area(stat_addr , 4) ;
repeat:
flag = 0 ;
//从任务数组末端开始扫描所有的任务,跳过空项,本进程项以及非当前进程的子进程项
for(p = &LAST_TASK ; p > &FIRST_TASK ; --p)
{
if(!*p || *p == current) //跳过空项以及当前进程项
continue ;
if((*p)->father != current->pid) //跳过非当前进程的子进程项
continue ;
//此时选择到的进程一定是当前进程的子进程
//如果当前的pid>0,但是不等于参数pid
//就说明是当前进程其他的子进程
if(pid > 0)
{
if((*p)->pid != pid)
{
continue ;
}
} else if(!pid){ //如果pid==0,则表示正在等待组号等于当前进程的所有进程
if((*p)->pgrp != current->pgrp)
continue ;
}else if(pid != -1){ //如果pid<-1
if((*p)->pgrp != -pid)
continue ;
}
//如果前3个对pid的判断不符合标准,则表示当前进程正在等待其他任何子进程,即pid=-1的情况
//接下来根据子进程的状态来处理
switch((*p)->state){
case TASK_STOPPED :
if(!(options & WUNTRACED))
continue ;
put_fs_long(0x7f , stat_addr) ;
return (*p)->pid ;
case TASK_ZOMBIE : //如果子进程是僵死状态,那么首先把子进程的用户态时间和内核态时间加到当前进程中
current->cutime += (*p)->utime ;
current->cstime += (*p)->stime ;
flag = (*p)->pid ; //临时保存当前子进程的退出码
code = (*p)->exit_code ; //取当前进程的退出码
release(*p) ;
put_fs_long(code , stat_addr) ; //置状态信息为退出码
return flag ;
default :
flag = 1 ;
continue ;
}
}
if(flag){
if(options & WNOHANG)
{
return 0 ;
}
current->state = TASK_INTERRUPTIBLE ; //置当前进程为可中断状态
schedule() ; //重新运行
if(!(current->signal &= ~(1<<(SIGCHLD-1)) ))
goto repeat ;
else
return -EINTR ;
}
return -ECHILD ;
}