598 lines
14 KiB
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;
|
|
}
|