View of xos/kernel/proc.c


XOS | Parent Directory | View | Download

/* Liens entre les processus */
/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "idle.h"
#include "proc_struct.h"
 
#include <vm.h>
#include <kmalloc.h>
#include <string_table.h>
#include <slab.h>
#include <cache_struct.h>
#include <errno.h>
#include <enums.h>
#include <stddef.h>
 
#define HASH_TABLE_SIZE 37
 
/* Table des processus */
 
static struct proc_struct *proc_table[HASH_TABLE_SIZE];
static struct proc_group_struct *proc_group_table[HASH_TABLE_SIZE];
 
static inline unsigned int hash(int pid)
{
  return pid % HASH_TABLE_SIZE;
}
 
void proc_table_init()
{
  struct proc_struct **proc_table_entry;
  struct proc_group_struct **proc_group_table_entry;
 
  proc_table_entry = proc_table;
  proc_group_table_entry = proc_group_table;
  while (proc_table_entry < proc_table + HASH_TABLE_SIZE)
    *proc_table_entry++ = NULL, *proc_group_table_entry++ = NULL;
 
  idle->id = 0;
 
  idle->proc_table_next = NULL;
  proc_table[hash(0)] = idle;
}
 
struct proc_struct *get_proc(int pid)
{
  struct proc_struct *p;
 
  p = proc_table[hash(pid)];
  while (p) {
    if (p->id == pid)
      return p;
    p = p->proc_table_next;
  }
  return NULL;
}
 
struct proc_group_struct *get_proc_group(int pgid)
{
  struct proc_group_struct *pg;
 
  pg = proc_group_table[hash(pgid)];
  while (pg) {
    if (pg->id == pgid)
      return pg;
    pg = pg->proc_group_table_next;
  }
  return NULL;
}
 
static inline int exists(int pid)
{
  return get_proc(pid) || get_proc_group(pid);
}
 
/* Cette fonction ne peut pas echouer des lors que PID_MAX est superieur a
 * (2 * NR_TASKS). En effet, il peut y avoir jusqu'a NR_TASKS processus et
 * NR_TASKS groupes de processus. */
static int generate_pid()
{
  static int next_pid = 1;
 
  int pid;
 
  do {
    pid = next_pid;
    next_pid < PID_MAX ? next_pid++ : (next_pid = 1);
  }
  while (exists(pid));
  return pid;
}
 
void proc_table_add(struct proc_struct *proc)
{
  unsigned int hash_code;
 
  proc->id = generate_pid();
 
  hash_code = hash(proc->id);
  proc->proc_table_next = proc_table[hash_code];
  proc_table[hash_code] = proc;
}
 
void proc_table_remove(struct proc_struct *proc)
{
  unsigned int hash_code;
  struct proc_struct *p;
 
  hash_code = hash(proc->id);
  p = proc_table[hash_code];
  if (p == proc)
    proc_table[hash_code] = proc->proc_table_next;
  else {
    while (p->proc_table_next != proc)
      p = p->proc_table_next;
    p->proc_table_next = proc->proc_table_next;
  }
}
 
static void proc_group_table_add(struct proc_group_struct *proc_group, int pgid)
{
  unsigned int hash_code;
 
  proc_group->id = pgid;
 
  hash_code = hash(proc_group->id);
  proc_group->proc_group_table_next = proc_group_table[hash_code];
  proc_group_table[hash_code] = proc_group;
}
 
static void proc_group_table_remove(struct proc_group_struct *proc_group)
{
  unsigned int hash_code;
  struct proc_group_struct *pg;
 
  hash_code = hash(proc_group->id);
  pg = proc_group_table[hash_code];
  if (pg == proc_group)
    proc_group_table[hash_code] = proc_group->proc_group_table_next;
  else {
    while (pg->proc_group_table_next != proc_group)
      pg = pg->proc_group_table_next;
    pg->proc_group_table_next = proc_group->proc_group_table_next;
  }
}
 
/* Liste */
 
struct proc_struct *proc_list_first;
static unsigned short proc_list_count;
 
void proc_list_init()
{
  idle->proc_list_previous = NULL;
  idle->proc_list_next = NULL;
  proc_list_first = idle;
  proc_list_count = 1;
}
 
void proc_list_add(struct proc_struct *proc)
{
  proc->proc_list_previous = NULL;
  proc->proc_list_next = proc_list_first;
  proc_list_first = proc;
  if (proc->proc_list_next)
    proc->proc_list_next->proc_list_previous = proc;
  proc_list_count++;
}
 
void proc_list_remove(struct proc_struct *proc)
{
  if (proc->proc_list_previous)
    proc->proc_list_previous->proc_list_next = proc->proc_list_next;
  else
    proc_list_first = proc->proc_list_next;
  if (proc->proc_list_next)
    proc->proc_list_next->proc_list_previous = proc->proc_list_previous;
  proc_list_count--;
}
 
/* Arborescence */
 
void proc_tree_init()
{
  idle->parent = NULL;
  idle->next_sibling = NULL;
  idle->first_child = NULL;
}
 
void proc_tree_add(struct proc_struct *proc, struct proc_struct *parent)
{
  proc->parent = parent;
  proc->next_sibling = parent->first_child;
  proc->first_child = NULL;
  parent->first_child = proc;
}
 
void proc_tree_dishinerit_children(struct proc_struct *proc)
{
  struct proc_struct *p;
 
  /* connection des fils, orphelins, a idle */
  if (proc->first_child) {
    p = proc->first_child;
    p->parent = idle;
    while (p->next_sibling) {
      p->next_sibling->parent = idle;
      p = p->next_sibling;
    }
    p->next_sibling = idle->first_child;
    idle->first_child = proc->first_child;
  }
}
 
/* Enleve un processus de l'arborescence des processus.
 * Le processus ne doit pas avoir de fils. */
void proc_tree_remove(struct proc_struct *proc)
{
  struct proc_struct *p;
 
  /* deconnection du pere */
  if (proc == proc->parent->first_child)
    proc->parent->first_child = proc->next_sibling;
  else {
    p = proc->parent->first_child;
    while (p->next_sibling != proc)
      p = p->next_sibling;
    p->next_sibling = proc->next_sibling;
  }
}
 
/* Groupes de processus */
 
/* Un processus doit toujours avoir un groupe. Un groupe ne doit jamais etre
 * vide. Les fonctions ci-dessous sont atomiques : elles garantissent ces
 * proprietes. */
 
static struct proc_group_struct idle_group;
 
static struct cache_struct prog_group_cache;
 
void proc_group_init()
{
  proc_group_table_add(&idle_group, 0);
  idle->group = &idle_group;
  idle->group_next = NULL;
  idle_group.first_proc = idle;
 
  kmem_cache_init(&prog_group_cache, sizeof (struct proc_group_struct));
}
 
/* Alloue un groupe vide. */
static struct proc_group_struct *alloc_proc_group(int pgid)
{
  struct proc_group_struct *proc_group;
 
  if (!(proc_group = kmem_cache_alloc(&prog_group_cache)))
    return NULL;
  proc_group_table_add(proc_group, pgid);
  proc_group->first_proc = NULL;
  return proc_group;
}
 
/* Libere un groupe vide. */
static void free_proc_group(struct proc_group_struct *proc_group)
{
  proc_group_table_remove(proc_group);
  kmem_cache_free(&prog_group_cache, proc_group);
}
 
/* Ajoute un processus dans un groupe. */
static void add_proc(struct proc_group_struct *proc_group, struct proc_struct *proc)
{
  proc->group = proc_group;
  proc->group_next = proc_group->first_proc;
  proc_group->first_proc = proc;
}
 
/* Enleve un processus d'un groupe et libere le groupe s'il devient vide. */
static void remove_proc(struct proc_struct *proc)
{
  struct proc_group_struct *proc_group;
  struct proc_struct *p;
 
  proc_group = proc->group;
  if (proc_group->first_proc == proc) {
    proc_group->first_proc = proc->group_next;
    if (!proc_group->first_proc)
      free_proc_group(proc_group);
  }
  else
    for (p = proc_group->first_proc; p->group_next; p = p->group_next)
      if (p->group_next == proc) {
        p->group_next = proc->group_next;
        return;
      }
}
 
int proc_create_proc_group(struct proc_struct *proc)
{
  struct proc_group_struct *proc_group;
 
  if (!(proc_group = alloc_proc_group(proc->id)))
    return -ENOMEM;
  add_proc(proc_group, proc);
  return 0;
}
 
void proc_group_add(struct proc_struct *proc, struct proc_group_struct *proc_group)
{
  add_proc(proc_group, proc);
}
 
void proc_group_remove(struct proc_struct *proc)
{
  remove_proc(proc);
}
 
void proc_change_set_proc_group(struct proc_struct *proc, struct proc_group_struct *proc_group)
{
  remove_proc(proc);
  add_proc(proc_group, proc);
}
 
int proc_change_create_proc_group(struct proc_struct *proc)
{
  struct proc_group_struct *proc_group;
 
  if (!(proc_group = alloc_proc_group(proc->id)))
    return -ENOMEM;
  remove_proc(proc);
  add_proc(proc_group, proc);
  return 0;
}
 
int is_orphan(const struct proc_struct *proc)
{
  return proc->parent == idle;
}
 
/* Informations sur les processus */
 
/* pid_table est alloue avec kmalloc() et devra-t-etre libere par l'appelant.
 * size est le nombre d'elements dans le tableau (et non la taille de l'espace
 * memoire alloue au tableau lui-meme. */
int proc_get_pid_table(int **pid_table, unsigned int *size)
{
  int *pid;
  const struct proc_struct *p;
 
  if (!(*pid_table = kmalloc(proc_list_count * sizeof (int))))
    return -ENOMEM;
  pid = *pid_table;
  for (p = proc_list_first; p; p = p->proc_list_next)
    *pid++ = p->id;
  *size = proc_list_count;
  return 0;
}
 
int proc_get_ppid_pid(int pid, int *ppid)
{
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  *ppid = proc == idle ? 0 : proc->parent->id;
  return 0;
}
 
int proc_get_pgid_pid(int pid, int *pgid)
{
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  *pgid = proc->group->id;
  return 0;
}
 
int proc_get_state_pid(int pid, int *state)
{
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  *state = proc->state;
  return 0;
}
 
int proc_get_cmdline_pid(int pid, struct string_table_struct *cmdline)
{
  static const struct string_table_struct zombie_cmdline = {0, NULL, 0};
 
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  return string_table_clone(proc->state != PS_ZOMBIE ? &proc->cmdline : &zombie_cmdline, cmdline);
}
 
int proc_get_wd_pid(int pid, struct file_struct **wd)
{
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  *wd = proc == idle || proc->state != PS_ZOMBIE ? proc->wd : NULL;
  return 0;
}
 
int proc_get_maps(int pid, struct map_info_struct **map_info_array, unsigned int *count)
{
  const struct proc_struct *proc;
 
  if (!(proc = get_proc(pid)))
    return -EINVAL;
  return vm_get_maps(&proc->vm, map_info_array, count);
}