/*
* linux/kernel/exit.c
*
* (C) 1991 Linus Torvalds
*/

#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <signal.h>		// źͷļźŷųźŽṹԼźŲԭ͡
#include <sys/wait.h>		// ȴͷļϵͳwait()waitpid()سš

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <linux/tty.h>		// tty ͷļйtty_ioͨŷĲ
#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");	// ָ
}

//// ָ(*p)ź(sig)ȨΪpriv
static inline int
send_sig (long sig, struct task_struct *p, int priv)
{
// źŲȷָΪ˳
  if (!p || sig < 1 || sig > 32)
    return -EINVAL;
// ȨЧûʶ(euid)̵ָeuid ǳûڽλͼ
// źţ˳suser()Ϊ(current->euid==0)жǷ񳬼û
  if (priv || (current->euid == p->euid) || suser ())
    p->signal |= (1 << (sig - 1));
  else
    return -EPERM;
  return 0;
}

//// ֹỰ(session)
static void
kill_session (void)
{
  struct task_struct **p = NR_TASKS + task;	// ָ*p ָĩˡ

// е񣨳0 ⣩Ựڵǰ̵ĻỰ͹ҶϽźš
  while (--p > &FIRST_TASK)
    {
      if (*p && (*p)->session == current->session)
	(*p)->signal |= 1 << (SIGHUP - 1);	// ͹ҶϽźš
    }
}

/*
* XXX need to check permissions needed to send signals to process
* groups, etc. etc. kill() permissions semantics are tricky!
*/
/*
* ΪȷźţXXX Ҫɡkill()ɻƷǳ!
*/
//// kill()ϵͳÿκν̻鷢κźš
// pid ֵ>0źű͸pid
// pid=0ôźžͻᱻ͸ǰ̵Ľен̡
// pid=-1źsig ͻᷢ͸һн̡
// pid < -1źsig ͸-pid н̡
// źsig Ϊ0򲻷źţԻд顣ɹ򷵻0
int
sys_kill (int pid, int sig)
{
  struct task_struct **p = NR_TASKS + task;
  int err, retval = 0;

  if (!pid)
    while (--p > &FIRST_TASK)
      {
	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 == 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;
  return retval;
}

//// ֪ͨ -- pid źSIGCHLDӽֹֹ̽ͣ
// ûҵ̣Լͷš
static void
tell_father (int pid)
{
  int i;

  if (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;
      }
/* if we don't find any fathers, we just release ourselves */
/* This is not really OK. Must change it to make father 1 */
  printk ("BAD BAD - no father found\n\r");
  release (current);		// ûҵ̣Լͷš
}

//// ˳ϵͳõжϴбá
int
do_exit (long code)		// code Ǵ롣
{
  int i;

// ͷŵǰ̴κݶռڴҳ(free_page_tables()mm/memory.c,105 )
  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)ӽѾ
// ڽ(ZOMBIE)״̬1 ӽֹźSIGCHLD
  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)
/* assumption task[1] is always init */
	  (void) send_sig (SIGCHLD, task[1], 1);
      }
// رյǰ̴ŵļ
  for (i = 0; i < NR_OPEN; i++)
    if (current->filp[i])
      sys_close (i);
// Եǰ̹Ŀ¼pwdĿ¼root Լгi ڵֱͬÿա
  iput (current->pwd);
  current->pwd = NULL;
  iput (current->root);
  current->root = NULL;
  iput (current->executable);
  current->executable = NULL;
// ǰͷ(leader)̲пƵնˣͷŸնˡ
  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;
// ֪̣ͨҲ򸸽̷źSIGCHLD -- ӽֹֹ̽ͣ
  tell_father (current->father);
  schedule ();			// µȽ̵С
  return (-1);			/* just to suppress warnings */
}

//// ϵͳexit()ֹ̡
int
sys_exit (int error_code)
{
  return do_exit ((error_code & 0xff) << 8);
}

//// ϵͳwaitpid()ǰֱ̣pid ָӽ˳ֹյҪֹ
// ý̵źţҪһźžźŴ򣩡pid ָӽ
// ˳ѳνḶ̌򱾵ý̷ءӽʹõԴͷš
// pid > 0, ʾȴ̺ŵpid ӽ̡
// pid = 0, ʾȴŵڵǰ̵κӽ̡
// pid < -1, ʾȴŵpid ֵκӽ̡
// [ pid = -1, ʾȴκӽ̡]
// options = WUNTRACEDʾӽֹͣģҲϷء
// options = WNOHANGʾûӽ˳ֹϷء
// stat_addr Ϊգͽ״̬Ϣ浽
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;
      if (pid > 0)
	{			// ָpid>0ɨĽpid
	  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;
	}
      switch ((*p)->state)
	{
	case TASK_STOPPED:
	  if (!(options & WUNTRACED))
	    continue;
	  put_fs_long (0x7f, stat_addr);	// ״̬ϢΪ0x7f
	  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;		// ˳ӽ̵pid.
	default:
	  flag = 1;		// ӽֹ̲ͣ״̬flag=1
	  continue;
	}
    }
  if (flag)
    {				// ӽûд˳״̬
      if (options & WNOHANG)	// options = WNOHANG̷ء
	return 0;
      current->state = TASK_INTERRUPTIBLE;	// õǰΪжϵȴ״̬
      schedule ();		// µȡ
      if (!(current->signal &= ~(1 << (SIGCHLD - 1))))	// ֿʼִбʱ
	goto repeat;		// ûյSIGCHLD źţظ
      else
	return -EINTR;		// ˳س롣
    }
  return -ECHILD;
}
