#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;
更新指定进程的信号位图:
1. 用户是超级用户
2. 当前进程的有效用户id和指定进程的有效用户id相同
3. 如果有特殊特权级,priv != 0
if (priv || (current->euid==p->euid) || suser())
p->signal |= (1<<(sig-1));
else
return -EPERM;
return 0;
}
向与当前进程处于同一会话期的进程递送挂断信号
static void kill_session(void)
{
struct task_struct **p = NR_TASKS + task;
遍历任务数组
while (--p > &FIRST_TASK) {
递送信号
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;
如果进程id为0,从最后一个进程槽开始遍历进程数组,表示把信号发送给与当前进程同组的所有进程中去
if (!pid) while (--p > &FIRST_TASK) {
如果当前进程id和某进程的进程组id相等,则向这个进程发送该信号
if (*p && (*p)->pgrp == current->pid)
if (err=send_sig(sig,*p,1))
retval = err;
}
如果进程id大于0,从最后一个进程槽开始遍历数组
else if (pid>0) while (--p > &FIRST_TASK) {
如果进程id和遍历中的某个进程id相等,则把该信号发送给这个进程
if (*p && (*p)->pid == pid)
if (err=send_sig(sig,*p,0))
retval = err;
}
如果进程id为-1,表示向所有进程发送该信号,除了进程0.
else if (pid == -1) while (--p > &FIRST_TASK)
if (err = send_sig(sig,*p,0))
retval = err;
如果进程id小于0且不等于0,则将信号发送到同pid绝对值相等的进程组的所有进程。
else while (--p > &FIRST_TASK)
if (*p && (*p)->pgrp == -pid)
if (err = send_sig(sig,*p,0))
retval = err;
return retval;
}
发送给父进程信号的函数,SIGCHILD
static void tell_father(int pid)
{
int i;
如果pid有效,即大于0
if (pid)
遍历任务数组,向与pid相同的进程递送SIGCHILD信号
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/n/r");
release(current);
}
被系统调用函数调用
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));
遍历任务数组,查找当前任务的子任务,如果该任务的状态是僵死,那么将向进程1发送SIGCHILD信号。
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);
将当前进程运行目录归还给i节点
iput(current->pwd);
current->pwd=NULL;
将根目录归还给i节点
iput(current->root);
current->root=NULL;
将执行文件归还给i节点
iput(current->executable);
current->executable=NULL;
如果当前进程是会话期的首进程,并且有终端和其相连,释放终端
if (current->leader && current->tty >= 0)
将与终端联系的进程组设置为0,表示回收
tty_table[current->tty].pgrp = 0;
如果该进程上次使用了协处理器,将协处理器指针置空
if (last_task_used_math == current)
last_task_used_math = NULL;
如果当前进程是首进程,需要向该会话期所有进程发送SIGHUP信号
if (current->leader)
kill_session();
将当前进程状态置为僵死,表示等着父进程回收退出状态
current->state = TASK_ZOMBIE;
current->exit_code = code;
向当前进程的父进程发送SIGCHILD信号
tell_father(current->father);
进行任务调度
schedule();
不会到达此处,因为该进程的状态在调度之前已经置为TASK_ZOMBIE。
return (-1);
}
退出进程的系统调用
int sys_exit(int error_code)
{
return do_exit((error_code&0xff)<<8);
}
取回子进程的退出状态
int sys_waitpid(pid_t pid,unsigned long * stat_addr, int options)
{
int flag, code;
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所制定的进程,不处理进程id为pid的进程
if (pid>0) {
if ((*p)->pid != pid)
continue;
}
如果pid为0,不处理和当前进程组同一组的进程
else if (!pid) {
if ((*p)->pgrp != current->pgrp)
continue;
}
如果pid小于0但是不为-1.不处理同pid绝对值同一组的进程
else if (pid != -1) {
if ((*p)->pgrp != -pid)
continue;
}
到了这个地方,要处理的进程为:
1. 当前进程的子进程,该子进程的id等于pid
2. 当前进程的子进程,且该子进程和当前进程不在同一组中
3. 当前进程的子进程,且该子进程同pid的绝对值所指代的进程在不同组中
switch ((*p)->state) {
如果该子进程为停止状态,且有置WUNTRACED位,将0x7f作为状态返回给父进程。
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;
}
}
如果该子进程不是僵死状态,也不是停滞状态,或者是停滞状态但是没有设置WUNTRACED位,需要进一步处理
if (flag) {
如果设置了WNOHANG位,立即返回,不需要阻塞
if (options & WNOHANG)
return 0;
否则,父进程阻塞在这里,并且被处理器重新调度
current->state=TASK_INTERRUPTIBLE;
schedule();
下次被调度的时候,如果除SIGCHILD之外,没有其他信号需要处理,就继续从头开始执行,否则
被信号中断,返回EINTR错误
if (!(current->signal &= ~(1<<(SIGCHLD-1))))
goto repeat;
else
return -EINTR;
}
如果在这里返回,表示没有找到子进程,子进程不存在。
return -ECHILD;
}