Implement proper wait/waitpid.

This is a pretty big commit, so let's run through it in parts:

- All of the userspace changes are to switch away from syscall_wait
  Mostly, this is to waitpid; some things were tweaked to do things
  "properly" instead of waiting for particular processes. Init has
  been fixed to do a proper spin wait.
- syscall_wait is gone - as are its uses. newlib bindings have been
  using just waitpid for a while now.
- waitpid now performs like a Unix waitpid
- process reaping is no longer a "do this on next change thing":
  it happens when a process is waited on, like it should
  (That means we can have real zombies: terminated processes that
  have not yet been waited on)
- Reparenting of children to init has been implemented, so you
  can fork-daemonize!

Overall, this is pretty big... So I hope it doesn't break everything.
This commit is contained in:
Kevin Lange 2014-04-27 01:37:33 -07:00
parent cd1880ebb5
commit 4d8335ad75
19 changed files with 215 additions and 216 deletions

View File

@ -152,6 +152,20 @@ void tree_remove(tree_t * tree, tree_node_t * node) {
free(node);
}
void tree_remove_reparent_root(tree_t * tree, tree_node_t * node) {
/* Remove this node and move its children into the root children */
tree_node_t * parent = node->parent;
if (!parent) return;
tree->nodes--;
list_delete(parent->children, list_find(parent->children, node));
foreach(child, node->children) {
/* Reassign the parents */
((tree_node_t *)child->value)->parent = tree->root;
}
list_merge(tree->root->children, node->children);
free(node);
}
void tree_break_off(tree_t * tree, tree_node_t * node) {
tree_node_t * parent = node->parent;
if (!parent) return;

View File

@ -102,6 +102,7 @@ typedef struct process {
node_t sleep_node;
node_t * timed_sleep_node;
uint8_t is_tasklet;
volatile uint8_t sleep_interrupted;
} process_t;
typedef struct {
@ -117,18 +118,14 @@ extern process_t * spawn_init(void);
extern process_t * spawn_kidle(void);
extern void set_process_environment(process_t * proc, page_directory_t * directory);
extern void make_process_ready(process_t * proc);
extern void make_process_reapable(process_t * proc);
extern uint8_t process_available(void);
extern process_t * next_ready_process(void);
extern uint8_t should_reap(void);
extern process_t * next_reapable_process(void);
extern uint32_t process_append_fd(process_t * proc, fs_node_t * node);
extern process_t * process_from_pid(pid_t pid);
extern process_t * process_get_first_child(process_t * process);
extern void delete_process(process_t * proc);
process_t * process_get_parent(process_t * process);
extern uint32_t process_move_fd(process_t * proc, int src, int dest);
extern int process_is_ready(process_t * proc);
extern void set_reaped(process_t * proc);
extern void wakeup_sleepers(unsigned long seconds, unsigned long subseconds);
extern void sleep_until(process_t * process, unsigned long seconds, unsigned long subseconds);
@ -140,6 +137,10 @@ extern list_t * process_list;
typedef void (*tasklet_t) (void *, char *);
extern int create_kernel_tasklet(tasklet_t tasklet, char * name, void * argp);
extern void release_directory(page_directory_t * dir);
extern void release_directory_for_exec(page_directory_t * dir);
extern void reap_process(process_t * proc);
extern int waitpid(int pid, int * status, int options);
#endif

View File

@ -25,5 +25,4 @@ typedef struct page_directory {
int32_t ref_count;
} page_directory_t;
#endif

View File

@ -16,13 +16,10 @@
tree_t * process_tree; /* Parent->Children tree */
list_t * process_list; /* Flat storage */
list_t * process_queue; /* Ready queue */
list_t * reap_queue; /* Processes to reap */
list_t * sleep_queue;
list_t * recently_reaped;
volatile process_t * current_process = NULL;
process_t * kernel_idle_task = NULL;
static uint8_t volatile reap_lock = 0;
static uint8_t volatile tree_lock = 0;
static uint8_t volatile process_queue_lock = 0;
static uint8_t volatile wait_lock_tmp = 0;
@ -38,9 +35,7 @@ void initialize_process_tree(void) {
process_tree = tree_create();
process_list = list_create();
process_queue = list_create();
reap_queue = list_create();
sleep_queue = list_create();
recently_reaped = list_create();
}
/*
@ -102,16 +97,6 @@ process_t * next_ready_process(void) {
return next;
}
process_t * next_reapable_process(void) {
spin_lock(&reap_lock);
node_t * np = list_dequeue(reap_queue);
spin_unlock(&reap_lock);
if (!np) { return NULL; }
process_t * next = np->value;
free(np);
return next;
}
/*
* Reinsert a process into the ready queue.
*
@ -130,6 +115,7 @@ void make_process_ready(process_t * proc) {
}
/* Else: I have no idea what happened. */
} else {
proc->sleep_interrupted = 1;
spin_lock(&wait_lock_tmp);
list_delete((list_t*)proc->sleep_node.owner, &proc->sleep_node);
spin_unlock(&wait_lock_tmp);
@ -140,18 +126,8 @@ void make_process_ready(process_t * proc) {
spin_unlock(&process_queue_lock);
}
void make_process_reapable(process_t * proc) {
delete_process(proc);
spin_lock(&reap_lock);
list_insert(reap_queue, (void *)proc);
spin_unlock(&reap_lock);
}
void set_reaped(process_t * proc) {
spin_lock(&reap_lock);
list_insert(recently_reaped, (void *)proc);
spin_unlock(&reap_lock);
}
extern void tree_remove_reparent_root(tree_t * tree, tree_node_t * node);
/*
* Delete a process from the process tree
@ -167,11 +143,20 @@ void delete_process(process_t * proc) {
/* We can not remove the root, which is an error anyway */
assert((entry != process_tree->root) && "Attempted to kill init.");
if (process_tree->root == entry) {
/* We are init, don't even bother. */
return;
}
/* Remove the entry. */
spin_lock(&tree_lock);
tree_remove(process_tree, entry);
/* Reparent everyone below me to init */
tree_remove_reparent_root(process_tree, entry);
list_delete(process_list, list_find(process_list, proc));
spin_unlock(&tree_lock);
/* Uh... */
free(proc);
}
static void _kidle(void) {
@ -408,14 +393,6 @@ process_t * spawn_process(volatile process_t * parent) {
return proc;
}
process_t * find_reaped_process(pid_t pid) {
foreach(node, recently_reaped) {
process_t * proc = node->value;
if (proc && proc->id == pid) return proc;
}
return NULL;
}
uint8_t process_compare(void * proc_v, void * pid_v) {
pid_t pid = (*(pid_t *)pid_v);
process_t * proc = (process_t *)proc_v;
@ -431,32 +408,20 @@ process_t * process_from_pid(pid_t pid) {
spin_unlock(&tree_lock);
if (entry) {
return (process_t *)entry->value;
} else {
return find_reaped_process(pid);
}
}
process_t * process_get_first_child_rec(tree_node_t * node, process_t * target) {
if (!node) return NULL;
process_t * proc = (process_t *)node->value;
if (proc == target) {
foreach(child, node->children) {
process_t * cproc = (process_t *)((tree_node_t *)child->value)->value;
return cproc;
}
return NULL;
}
foreach(child, node->children) {
/* Recursively print the children */
process_t * out = process_get_first_child_rec(child->value, target);
if (out) return out;
}
return NULL;
}
process_t * process_get_first_child(process_t * process) {
process_t * process_get_parent(process_t * process) {
process_t * result = NULL;
spin_lock(&tree_lock);
process_t * result = process_get_first_child_rec(process_tree->root, process);
tree_node_t * entry = process->tree_entry;
if (entry->parent) {
result = entry->parent->value;
}
spin_unlock(&tree_lock);
return result;
}
@ -519,10 +484,6 @@ uint8_t process_available(void) {
return (process_queue->head != NULL);
}
uint8_t should_reap(void) {
return (reap_queue->head != NULL);
}
/*
* Append a file descriptor to a process.
*
@ -583,11 +544,12 @@ int sleep_on(list_t * queue) {
switch_task(0);
return 0;
}
current_process->sleep_interrupted = 0;
spin_lock(&wait_lock_tmp);
list_append(queue, (node_t *)&current_process->sleep_node);
spin_unlock(&wait_lock_tmp);
switch_task(0);
return 0;
return current_process->sleep_interrupted;
}
int process_is_ready(process_t * proc) {
@ -640,3 +602,119 @@ void sleep_until(process_t * process, unsigned long seconds, unsigned long subse
process->timed_sleep_node = list_insert_after(sleep_queue, before, proc);
spin_unlock(&sleep_lock);
}
void reap_process(process_t * proc) {
debug_print(INFO, "Reaping process %d; mem before = %d", proc->id, memory_use());
list_free(proc->wait_queue);
free(proc->wait_queue);
list_free(proc->signal_queue);
free(proc->signal_queue);
free(proc->wd_name);
debug_print(INFO, "Releasing shared memory for %d", proc->id);
shm_release_all(proc);
free(proc->shm_mappings);
debug_print(INFO, "Freeing more mems %d", proc->id);
free(proc->name);
if (proc->signal_kstack) {
free(proc->signal_kstack);
}
debug_print(INFO, "Dec'ing fds for %d", proc->id);
proc->fds->refs--;
if (proc->fds->refs == 0) {
debug_print(INFO, "Reached 0, all dependencies are closed for %d's file descriptors and page directories", proc->id);
release_directory(proc->thread.page_directory);
debug_print(INFO, "Going to clear out the file descriptors %d", proc->id);
for (uint32_t i = 0; i < proc->fds->length; ++i) {
if (proc->fds->entries[i]) {
//close_fs(proc->fds->entries[i]);
//free(proc->fds->entries[i]);
}
//close_fs(proc->fds->entries[i]);
}
debug_print(INFO, "... and their storage %d", proc->id);
free(proc->fds->entries);
free(proc->fds);
debug_print(INFO, "... and the kernel stack (hope this ain't us) %d", proc->id);
free((void *)(proc->image.stack - KERNEL_STACK_SIZE));
}
debug_print(INFO, "Reaped process %d; mem after = %d", proc->id, memory_use());
delete_process(proc);
debug_print_process_tree();
}
static int wait_candidate(process_t * parent, int pid, int options, process_t * proc) {
(void)options; /* there is only one option that affects candidacy, and we don't support it yet */
if (!proc) return 0;
if (pid < -1) {
if (proc->group == -pid || proc->id == -pid) return 1;
} else if (pid == 0) {
/* Matches our group ID */
if (proc->group == parent->id) return 1;
} else if (pid > 0) {
/* Specific pid */
if (proc->id == pid) return 1;
} else {
return 1;
}
return 0;
}
int waitpid(int pid, int * status, int options) {
process_t * proc = (process_t *)current_process;
if (proc->group) {
proc = process_from_pid(proc->group);
}
debug_print(NOTICE, "waitpid(%s%d, ..., %d) (from pid=%d.%d)", (pid >= 0) ? "" : "-", (pid >= 0) ? pid : -pid, options, current_process->id, current_process->group);
do {
process_t * candidate = NULL;
int has_children = 0;
/* First, find out if there is anyone to reap */
foreach(node, proc->tree_entry->children) {
if (!node->value) {
continue;
}
process_t * child = ((tree_node_t *)node->value)->value;
if (wait_candidate(proc, pid, options, child)) {
has_children = 1;
if (child->finished) {
candidate = child;
break;
}
}
}
if (!has_children) {
/* No valid children matching this description */
debug_print(NOTICE, "No children matching description.");
return -ECHILD;
}
if (candidate) {
debug_print(NOTICE, "Candidate found (%x:%d), bailing early.", candidate, candidate->id);
if (status) {
*status = candidate->status;
}
int pid = candidate->id;
reap_process(candidate);
return pid;
} else {
if (options & 1) {
return 0;
}
debug_print(NOTICE, "Sleeping until queue is done.");
/* Wait */
if (sleep_on(proc->wait_queue) != 0) {
debug_print(NOTICE, "wait() was interrupted");
return -EINTR;
}
}
} while (1);
}

View File

@ -109,73 +109,10 @@ static int write(int fd, char * ptr, int len) {
return out;
}
static int advanced_wait(int child, int * status, int options) {
process_t * volatile child_task;
if (child == 0) {
debug_print_process_tree();
child_task = process_get_first_child((process_t *)current_process);
} else if (child == -1) {
debug_print(WARNING, "wait(-1) from %d", getpid());
child_task = process_get_first_child((process_t *)current_process);
} else if (child < 1) {
debug_print(WARNING, "Process %d requested group wait, which we can not do! Pretending they asked for a single process (%d)", getpid(), -child);
child_task = process_from_pid(-child);
} else {
child_task = process_from_pid(child);
}
/* If the child task doesn't exist, bail */
if (!child_task) {
if (status && !validate_safe(status)) {
*status = 0;
}
debug_print(WARNING, "Tried to wait for non-existent process by pid %d", getpid());
return -ECHILD;
}
debug_print(NOTICE, "pid=%d waiting on pid=%d", current_process->id, child_task->id);
while (child_task->finished == 0) {
/* Add us to the wait queue for this child */
sleep_on(child_task->wait_queue);
}
/* Grab the child's return value */
int ret = child_task->status;
if (status && !validate_safe(status)) {
*status = ret;
}
return child_task->id;
}
static int sys_waitpid(int pid, int * status, int options) {
if (status && validate_safe(status)) return -EINVAL;
static int wait(int child) {
process_t * volatile child_task;
if (child == 0) {
debug_print_process_tree();
child_task = process_get_first_child((process_t *)current_process);
} else if (child == -1) {
debug_print(WARNING, "wait(-1) from %d", getpid());
child_task = process_get_first_child((process_t *)current_process);
if (child_task) {
child = child_task->id;
} else {
return -1;
}
} else if (child < 1) {
debug_print(WARNING, "Process %d requested group wait, which we can not do! Pretending they asked for a single process (%d)", getpid(), -child);
child_task = process_from_pid(-child);
} else {
child_task = process_from_pid(child);
}
/* If the child task doesn't exist, bail */
if (!child_task) {
debug_print(WARNING, "Tried to wait for non-existent process %d by pid %d", child, getpid());
return 0;
}
debug_print(NOTICE, "pid=%d waiting on pid=%d", current_process->id, child_task->id);
while (child_task->finished == 0) {
/* Add us to the wait queue for this child */
sleep_on(child_task->wait_queue);
}
/* Grab the child's return value */
int ret = child_task->status;
return ret;
return waitpid(pid, status, options);
}
static int open(const char * file, int flags, int mode) {
@ -714,7 +651,7 @@ static uintptr_t syscalls[] = {
(uintptr_t)&seek,
(uintptr_t)&stat,
(uintptr_t)&RESERVED, /* 16 */
(uintptr_t)&wait,
(uintptr_t)&RESERVED,
(uintptr_t)&RESERVED,
(uintptr_t)&RESERVED,
(uintptr_t)&RESERVED, /* 20 */
@ -750,7 +687,7 @@ static uintptr_t syscalls[] = {
(uintptr_t)&sys_chmod,
(uintptr_t)&sys_umask,
(uintptr_t)&sys_unlink, /* 52 */
(uintptr_t)&advanced_wait,
(uintptr_t)&sys_waitpid,
};
uint32_t num_syscalls;

View File

@ -112,45 +112,6 @@ void release_directory_for_exec(page_directory_t * dir) {
extern char * default_name;
void reap_process(process_t * proc) {
debug_print(INFO, "Reaping process %d; mem before = %d", proc->id, memory_use());
list_free(proc->wait_queue);
free(proc->wait_queue);
list_free(proc->signal_queue);
free(proc->signal_queue);
free(proc->wd_name);
debug_print(INFO, "Releasing shared memory for %d", proc->id);
shm_release_all(proc);
free(proc->shm_mappings);
debug_print(INFO, "Freeing more mems %d", proc->id);
free(proc->name);
if (proc->signal_kstack) {
free(proc->signal_kstack);
}
debug_print(INFO, "Dec'ing fds for %d", proc->id);
proc->fds->refs--;
if (proc->fds->refs == 0) {
debug_print(INFO, "Reached 0, all dependencies are closed for %d's file descriptors and page directories", proc->id);
release_directory(proc->thread.page_directory);
debug_print(INFO, "Going to clear out the file descriptors %d", proc->id);
for (uint32_t i = 0; i < proc->fds->length; ++i) {
if (proc->fds->entries[i]) {
//close_fs(proc->fds->entries[i]);
//free(proc->fds->entries[i]);
}
//close_fs(proc->fds->entries[i]);
}
debug_print(INFO, "... and their storage %d", proc->id);
free(proc->fds->entries);
free(proc->fds);
debug_print(INFO, "... and the kernel stack (hope this ain't us) %d", proc->id);
free((void *)(proc->image.stack - KERNEL_STACK_SIZE));
}
debug_print(INFO, "Reaped process %d; mem after = %d", proc->id, memory_use());
debug_print_process_tree();
set_reaped(proc);
}
/*
* Clone a page table
*
@ -449,12 +410,6 @@ void switch_task(uint8_t reschedule) {
if (eip == 0x10000) {
/* Returned from EIP after task switch, we have
* finished switching. */
while (should_reap()) {
process_t * proc = next_reapable_process();
if (proc) {
reap_process(proc);
}
}
fix_signal_stacks();
/* XXX: Signals */
@ -599,9 +554,12 @@ void task_exit(int retval) {
}
current_process->status = retval;
current_process->finished = 1;
debug_print(INFO, "[%d] Waking up %d processes...", getpid(), current_process->wait_queue->length);
wakeup_queue(current_process->wait_queue);
make_process_reapable((process_t *)current_process);
process_t * parent = process_get_parent((process_t *)current_process);
if (parent) {
wakeup_queue(parent->wait_queue);
}
switch_next();
}

View File

@ -108,9 +108,9 @@ static hashmap_t * shell_commands_map = NULL;
static int shell_create_userspace_shell(fs_node_t * tty, int argc, char * argv[]) {
int pid = create_kernel_tasklet(debug_shell_run_sh, "[[k-sh]]", NULL);
fprintf(tty, "Shell started with pid = %d\n", pid);
process_t * child_task = process_from_pid(pid);
sleep_on(child_task->wait_queue);
return child_task->status;
int status;
waitpid(pid,&status,0);
return status;
}
static int shell_echo(fs_node_t * tty, int argc, char * argv[]) {

View File

@ -12,6 +12,7 @@
#include <string.h>
#include <fcntl.h>
#include <syscall.h>
#include <sys/wait.h>
#define DEFAULT_HOSTNAME "toaru-test"
@ -50,7 +51,10 @@ int start_options(char * args[]) {
int i = execvp(args[0], args);
exit(0);
} else {
return syscall_wait(pid);
while (1) {
int i = wait(NULL);
if (i == -1) return 0;
}
}
}

View File

@ -15,6 +15,7 @@
#include <time.h>
#include <signal.h>
#include <termios.h>
#include <sys/wait.h>
#include <sys/utsname.h>
@ -178,7 +179,7 @@ int main(int argc, char ** argv) {
int i = execvp(args[0], args);
} else {
child = f;
syscall_wait(f);
waitpid(f, NULL, 0);
}
child = 0;
free(username);

View File

@ -1,6 +1,9 @@
#include <syscall.h>
#include <sys/wait.h>
int main(int argc, char * argv[]) {
int pid = syscall_system_function(7, NULL);
return syscall_wait(pid);
int status;
wait(&status);
return status;
}

View File

@ -10,9 +10,11 @@
#include <stdlib.h>
#include <unistd.h>
#include <syscall.h>
#include "lib/pthread.h"
#include <signal.h>
#include <termios.h>
#include <sys/wait.h>
#include "lib/pthread.h"
int fd = 0;
@ -77,8 +79,7 @@ int main(int argc, char ** argv) {
if (!strcmp(line, "quit")) {
kill(child_pid, SIGKILL);
printf("Waiting for threads to shut down...\n");
syscall_wait(child_pid);
while (wait(NULL) != -1);
printf("Exiting.\n");
return 0;
} else if (!strcmp(line, "continue")) {

View File

@ -1393,13 +1393,9 @@ int main(int argc, char * argv[]) {
break;
case YUTANI_MSG_SESSION_END:
{
for (unsigned int i = 0; i <= YUTANI_ZORDER_MAX; ++i) {
if (yg->zlist[i]) {
yutani_msg_t * response = yutani_msg_build_session_end();
pex_send(server, yg->zlist[i]->owner, response->size, (char *)response);
free(response);
}
}
yutani_msg_t * response = yutani_msg_build_session_end();
pex_broadcast(server, response->size, (char *)response);
free(response);
}
break;
case YUTANI_MSG_WINDOW_FOCUS:

View File

@ -13,6 +13,7 @@
#include <cairo.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include "lib/sha2.h"
#include "lib/graphics.h"
@ -418,7 +419,7 @@ int main (int argc, char ** argv) {
free(foo);
free(buf);
syscall_wait(_session_pid);
waitpid(_session_pid, NULL, 0);
}
yutani_close(y, wina);

View File

@ -3,6 +3,7 @@
#include <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <sys/wait.h>
#define LINE_LEN 1024
@ -70,13 +71,12 @@ int main(int argc, char * argv[]) {
execvp(args[0], args);
}
syscall_wait(_panel_pid);
wait(NULL);
printf("Session leader has exited. Sending INT signals to %d.\n", _wallpaper_pid);
printf("Waiting on wallpaper.\n");
syscall_wait(_wallpaper_pid);
printf("Session has ended.\n");
while (1) {
int pid = waitpid(-1, NULL, 0);
if (pid == -1) break;
}
return 0;
}

View File

@ -122,12 +122,13 @@ static void * toastd_handler(void * garbage) {
while (!exit_app) {
pex_packet_t * p = calloc(PACKET_SIZE, 1);
pex_listen(toastd_server, p);
if (pex_listen(toastd_server, p) > 0) {
notification_t * toast = (void *)p->data;
add_toast(toast);
notification_t * toast = (void *)p->data;
add_toast(toast);
free(p);
free(p);
}
}
}
@ -197,7 +198,7 @@ int main (int argc, char ** argv) {
}
}
pthread_kill(toastd_thread, SIGWINCH);
pthread_kill(toastd_thread, SIGINT);
pthread_kill(closer_thread, SIGWINCH);
return 0;

View File

@ -14,6 +14,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <getopt.h>
#include <wchar.h>
@ -477,7 +478,7 @@ void key_event(int ret, key_event_t * event) {
}
void * wait_for_exit(void * garbage) {
syscall_wait(child_pid);
waitpid(child_pid, NULL, 0);
/* Clean up */
exit_application = 1;
/* Exit */

View File

@ -22,6 +22,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <getopt.h>
#include <errno.h>
#include <ft2build.h>
@ -887,7 +888,7 @@ void key_event(int ret, key_event_t * event) {
}
void * wait_for_exit(void * garbage) {
syscall_wait(child_pid);
waitpid(child_pid, NULL, 0);
/* Clean up */
exit_application = 1;
/* Exit */
@ -1069,6 +1070,7 @@ void * handle_incoming(void * garbage) {
case YUTANI_MSG_SESSION_END:
{
kill(child_pid, SIGKILL);
exit_application = 1;
}
break;
case YUTANI_MSG_RESIZE_OFFER:

View File

@ -10,6 +10,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/wait.h>
#include "lib/pthread.h"
#define NUM_THREADS 5
@ -69,7 +70,7 @@ int main(int argc, char * argv[]) {
}
for (int i = 0; i < NUM_THREADS; ++i) {
syscall_wait(thread[i].id);
waitpid(thread[i].id, NULL, 0);
}
printf("Done. Result of %scomputation was %d %s!!\n",

View File

@ -8,6 +8,7 @@
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char ** argv) {
int quiet = 0;
@ -28,7 +29,7 @@ int main(int argc, char ** argv) {
} else {
if (!quiet)
printf("Waiting on %d\n", k);
syscall_wait(k);
waitpid(k, NULL, 0);
}
}
return 0;