sos-code-article10/sos/process.c

598 lines
14 KiB
C

/* Copyright (C) 2005 David Decotigny
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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include <sos/assert.h>
#include <sos/list.h>
#include <sos/kmem_slab.h>
#include <hwcore/irq.h>
#include <drivers/bochs.h>
#include <hwcore/bitmap.h>
#include <sos/physmem.h> /* for SOS_PAGE_SIZE */
#include <sos/umem_vmm.h>
#include "process.h"
#define SOS_PROCESS_MAX_OPENED_FILES 64
#define SOS_PROCESS_MAX_NAMELEN 32
/**
* Definition of a process in SOS. A process is basically the
* collection of all the resources requested by a user program. The
* threads are one of these resources, the mm_context is one other
* example of such resources.
*/
struct sos_process
{
char name[SOS_PROCESS_MAX_NAMELEN];
/** Process identifier */
sos_pid_t pid;
/** First important resource: the address space */
struct sos_umem_vmm_as *address_space;
/** Second important resource: the CPU execution contexts, aka the
threads executing in the context of this process. */
struct sos_thread *thread_list;
sos_count_t nb_threads;
/** Reference counter, including threads */
sos_count_t ref_cnt;
/** The array of opened file descriptors */
struct sos_fs_opened_file * fds[SOS_PROCESS_MAX_OPENED_FILES];
/** Where the root of the process is (for chroot support). May be NULL */
struct sos_fs_opened_file * root;
/** Where the current working dir of the process is */
struct sos_fs_opened_file * cwd;
/** To chain the processes in the global list of active processes
and PIDs */
struct sos_hash_linkage hlink_pidtable;
};
/** The list of processes in the system */
static struct sos_hash_table * process_hash;
/** The bitmap for PID allocation */
static void * bitmap_pid;
/** Number of bits in the PID bitmap */
#define SOS_PROCESS_PID_BITMAP_NBITS \
(8 * SOS_PROCESS_PID_BITMAP_NPAGES * SOS_PAGE_SIZE)
/** The PID where we start looking for a free slot */
static sos_pid_t next_base_pid;
/** The cache for the sos_process structures */
struct sos_kslab_cache *cache_struct_process;
/**
* Helper function for debugging purposes
*/
void sos_process_dumplist(void)
{
struct sos_thread * cur_thr = sos_thread_get_current();
int nb_procs = 0;
/* Internal function used to dump infos about a single process */
sos_bool_t process_iterator(void* custom_data,
struct sos_process * proc)
{
struct sos_thread * thr;
int nb_thrs;
nb_procs ++;
sos_bochs_printf(" proc %d @%p: '%s', %d threads, %d refs\n",
proc->pid, proc, proc->name?proc->name:"(none)",
proc->nb_threads, proc->ref_cnt);
list_foreach_forward_named(proc->thread_list, thr, nb_thrs,
prev_in_process, next_in_process)
{
if (thr == cur_thr)
sos_bochs_printf(" thr@%p: [CURRENT]\n", thr);
else
sos_bochs_printf(" thr@%p: %cstate=%d eip=%p\n",
thr,
sos_cpu_context_is_in_user_mode(thr->cpu_state)?'u':'k',
thr->state,
(void*)sos_cpu_context_get_PC(thr->cpu_state));
}
return TRUE;
}
sos_bochs_printf("<ps>\n");
sos_hash_walk(process_hash, (sos_hash_map_func_t*)process_iterator, NULL);
sos_bochs_printf(" ======= %d processes =======\n", nb_procs);
sos_bochs_printf("</ps>\n");
}
sos_ret_t sos_process_subsystem_setup()
{
/* Create the cache for the process structures */
cache_struct_process = sos_kmem_cache_create("struct_process",
sizeof(struct sos_process),
3,
0,
SOS_KSLAB_CREATE_MAP
| SOS_KSLAB_CREATE_ZERO);
if (NULL == cache_struct_process)
return -SOS_ENOMEM;
/* Allocate pages for the PID bitmap */
bitmap_pid = (void*) sos_kmem_vmm_alloc(SOS_PROCESS_PID_BITMAP_NPAGES,
SOS_KMEM_VMM_MAP);
if (NULL == bitmap_pid)
{
sos_kmem_cache_destroy(cache_struct_process);
return -SOS_ENOMEM;
}
/* The next process might (and actually will !) have PID 1 */
next_base_pid = 1;
/* Create the hash for the process list */
process_hash = sos_hash_create("pidtable", struct sos_process,
NULL, NULL, 37,
pid, hlink_pidtable);
if (NULL == process_hash)
{
sos_kmem_cache_destroy(cache_struct_process);
sos_kmem_vmm_free((sos_vaddr_t)bitmap_pid);
return -SOS_ENOMEM;
}
return SOS_OK;
}
/** Helper function to allocate a PID entry and bind it to the given process */
static sos_ret_t register_process(struct sos_process * proc)
{
sos_ret_t retval;
sos_pid_t pid;
/* First of all: look for a free PID */
while (TRUE)
{
pid = sos_bitmap_ffc(bitmap_pid,
SOS_PROCESS_PID_BITMAP_NBITS,
next_base_pid);
if (pid <= 0)
{
if (next_base_pid > 1)
{
/* try again from the beginning */
next_base_pid = 1;
continue;
}
else
/* Bad news: no PID left at all ! */
return -SOS_EAGAIN;
}
if (! sos_bitmap_test_and_set(bitmap_pid, pid))
/* Could allocate it ! */
break;
}
/* Set the base PID for next process */
if (pid < SOS_PROCESS_PID_BITMAP_NBITS)
next_base_pid = pid + 1;
else
next_base_pid = 1;
/* Now bind the process into the process hash */
proc->pid = pid;
retval = sos_hash_insert(process_hash, proc);
if (SOS_OK != retval)
{
/* Bad news: cannot register in hash */
SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, pid));
return retval;
}
return SOS_OK;
}
/** Helper function to deallocate a PID entry and unbind it to the given process */
static sos_ret_t unregister_process(struct sos_process * proc)
{
SOS_ASSERT_FATAL(SOS_OK == sos_hash_remove(process_hash, proc));
SOS_ASSERT_FATAL(sos_bitmap_test_and_clear(bitmap_pid, proc->pid));
proc->pid = 0;
return SOS_OK;
}
struct sos_process *sos_process_create(const char *name,
sos_bool_t do_copy_current_process)
{
sos_ret_t retval = SOS_OK;
sos_ui32_t flags;
struct sos_process *proc;
proc = (struct sos_process*) sos_kmem_cache_alloc(cache_struct_process, 0);
if (! proc)
return NULL;
/* proc is already filled with 0 (cache has SOS_KSLAB_CREATE_ZERO
flag) */
/* Copy the file descriptors when needed */
if (do_copy_current_process)
{
struct sos_process * myself = sos_thread_get_current()->process;
int fd;
for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
if (NULL != myself->fds[fd])
{
retval = sos_fs_duplicate_opened_file(myself->fds[fd],
proc,
& proc->fds[fd]);
if (SOS_OK != retval)
goto end_create_proc;
}
retval = sos_fs_duplicate_opened_file(myself->root,
proc,
& proc->root);
if (SOS_OK != retval)
goto end_create_proc;
retval = sos_fs_duplicate_opened_file(myself->cwd,
proc,
& proc->cwd);
if (SOS_OK != retval)
goto end_create_proc;
}
if (do_copy_current_process)
{
struct sos_process * myself = sos_thread_get_current()->process;
proc->address_space
= sos_umem_vmm_duplicate_as(sos_process_get_address_space(myself),
proc);
}
else
proc->address_space = sos_umem_vmm_create_empty_as(proc);
if (NULL == proc->address_space)
{
/* Error */
retval = -SOS_ENOMEM;
goto end_create_proc;
}
if (!name)
{
struct sos_thread * cur_thr = sos_thread_get_current();
if (do_copy_current_process)
name = cur_thr->process->name;
else
name = "[UNNAMED]";
}
strzcpy(proc->name, name, SOS_PROCESS_MAX_NAMELEN);
/* Add it to the global list of processes */
sos_disable_IRQs(flags);
retval = register_process(proc);
if (SOS_OK == retval)
proc->ref_cnt = 1; /* Mark the process as referenced */
sos_restore_IRQs(flags);
end_create_proc:
if (SOS_OK != retval)
{
int fd;
/* Close the file descriptors */
for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
if (NULL != proc->fds[fd])
{
sos_fs_close(proc->fds[fd]);
proc->fds[fd] = NULL;
}
if (proc->root)
sos_fs_close(proc->root);
if (proc->cwd)
sos_fs_close(proc->cwd);
proc->root = proc->cwd = NULL;
sos_kmem_cache_free((sos_vaddr_t) proc);
proc = NULL;
}
return proc;
}
inline
sos_ret_t sos_process_ref(struct sos_process *proc)
{
sos_ui32_t flags;
sos_disable_IRQs(flags);
proc->ref_cnt ++;
sos_restore_IRQs(flags);
return SOS_OK;
}
sos_pid_t sos_process_get_pid(const struct sos_process *proc)
{
return proc->pid;
}
sos_count_t sos_process_get_nb_threads(const struct sos_process *proc)
{
sos_count_t retval;
sos_ui32_t flags;
sos_disable_IRQs(flags);
retval = proc->nb_threads;
sos_restore_IRQs(flags);
return retval;
}
struct sos_mm_context *
sos_process_get_mm_context(const struct sos_process *proc)
{
return sos_umem_vmm_get_mm_context(proc->address_space);
}
struct sos_umem_vmm_as *
sos_process_get_address_space(const struct sos_process *proc)
{
return proc->address_space;
}
sos_ret_t sos_process_set_address_space(struct sos_process *proc,
struct sos_umem_vmm_as *new_as)
{
int fd;
/* Close the FD that are not allowed to be duplicated */
for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
if ( (NULL != proc->fds[fd])
&& (proc->fds[fd]->open_flags & SOS_FS_OPEN_CLOSEONEXEC) )
{
sos_fs_close(proc->fds[fd]);
proc->fds[fd] = NULL;
}
if (proc->address_space)
{
sos_ret_t retval = sos_umem_vmm_delete_as(proc->address_space);
if (SOS_OK != retval)
return retval;
}
proc->address_space = new_as;
return SOS_OK;
}
struct sos_fs_opened_file *
sos_process_get_root(const struct sos_process *proc)
{
return proc->root;
}
struct sos_fs_opened_file *
sos_process_get_cwd(const struct sos_process *proc)
{
return proc->cwd;
}
struct sos_fs_opened_file *
sos_process_get_opened_file(const struct sos_process *proc,
int fd)
{
if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES))
return NULL;
return proc->fds[fd];
}
sos_ret_t
sos_process_chroot(struct sos_process *proc,
struct sos_fs_opened_file * new_root,
struct sos_fs_opened_file ** old_root)
{
*old_root = proc->root;
proc->root = new_root;
return SOS_OK;
}
sos_ret_t
sos_process_chdir(struct sos_process *proc,
struct sos_fs_opened_file * new_cwd,
struct sos_fs_opened_file ** old_cwd)
{
*old_cwd = proc->cwd;
proc->cwd = new_cwd;
return SOS_OK;
}
sos_ret_t
sos_process_register_opened_file(struct sos_process *proc,
struct sos_fs_opened_file * of,
int start_search_at_fd)
{
int i;
for (i = start_search_at_fd ;
i < SOS_PROCESS_MAX_OPENED_FILES ;
i++)
if (NULL == proc->fds[i])
{
proc->fds[i] = of;
return i;
}
return -SOS_EMFILE;
}
sos_ret_t
sos_process_unregister_opened_file(struct sos_process *proc,
int fd)
{
if ((fd < 0) || (fd >= SOS_PROCESS_MAX_OPENED_FILES))
return -SOS_EBADF;
proc->fds[fd] = NULL;
return SOS_OK;
}
/* ***************************************************
* Restricted functions
*/
sos_ret_t sos_process_register_thread(struct sos_process *in_proc,
struct sos_thread *thr)
{
sos_ui32_t flags;
/* The process is assumed to be referenced by somebody */
SOS_ASSERT_FATAL(in_proc->ref_cnt > 0);
/* Only threads that are being created are allowed to be attached to
a process */
SOS_ASSERT_FATAL(thr->state == SOS_THR_CREATED);
/* Update the list of the threads in the process */
thr->process = in_proc;
/* Increment the reference count for the process */
sos_process_ref(in_proc);
/* Add the thread to the process thread's list */
sos_disable_IRQs(flags);
list_add_tail_named(in_proc->thread_list, thr,
prev_in_process, next_in_process);
in_proc->nb_threads ++;
sos_restore_IRQs(flags);
return SOS_OK;
}
/** The function responsible for releasing the resources held by the
process. */
sos_ret_t sos_process_unref(struct sos_process *proc)
{
sos_ui32_t flags;
sos_ret_t retval;
int fd;
SOS_ASSERT_FATAL(proc->ref_cnt > 0);
sos_disable_IRQs(flags);
proc->ref_cnt --;
if (proc->ref_cnt > 0)
{
sos_restore_IRQs(flags);
return -SOS_EBUSY;
}
unregister_process(proc);
sos_restore_IRQs(flags);
/* Close the file descriptors */
for (fd = 0 ; fd < SOS_PROCESS_MAX_OPENED_FILES ; fd++)
if (NULL != proc->fds[fd])
{
sos_fs_close(proc->fds[fd]);
proc->fds[fd] = NULL;
}
sos_fs_close(proc->root);
sos_fs_close(proc->cwd);
proc->root = proc->cwd = NULL;
/* First: free the user address space */
retval = sos_umem_vmm_delete_as(proc->address_space);
SOS_ASSERT_FATAL(SOS_OK == retval);
/* Free the process structure */
sos_kmem_cache_free((sos_vaddr_t)proc);
return SOS_OK;
}
sos_ret_t sos_process_unregister_thread(struct sos_thread *thr)
{
sos_ui32_t flags;
struct sos_process * in_proc = thr->process;
SOS_ASSERT_FATAL(thr->state == SOS_THR_ZOMBIE);
/* Update the list of the threads in the process */
thr->process = NULL;
sos_disable_IRQs(flags);
list_delete_named(in_proc->thread_list, thr,
prev_in_process, next_in_process);
SOS_ASSERT_FATAL(in_proc->nb_threads > 0);
in_proc->nb_threads --;
sos_restore_IRQs(flags);
/* Unreference the process */
sos_process_unref(in_proc);
return SOS_OK;
}
sos_ret_t sos_process_set_name(struct sos_process * proc,
const char * new_name)
{
strzcpy(proc->name, new_name, SOS_PROCESS_MAX_NAMELEN);
return SOS_OK;
}