From 780507f42b2c6ba060174f38636d2362610f3c29 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Fri, 24 Feb 2012 23:43:35 +0100 Subject: [PATCH] Implementation of wait4 syscall The 'noux_bash.run' script has become able to present the user with an interactive bash shell for executing various coreutils programs. It is still pretty limited, i.e., the environment is not correctly passed to child processes and pipes are not supported. But bash and coreutils are operational. --- ports/run/noux_bash.run | 2 - ports/src/noux/child.h | 112 ++++++++++++++++++++-- ports/src/noux/file_descriptor_registry.h | 2 +- ports/src/noux/main.cc | 44 +++++---- 4 files changed, 133 insertions(+), 27 deletions(-) diff --git a/ports/run/noux_bash.run b/ports/run/noux_bash.run index 706e667911..cea4989806 100644 --- a/ports/run/noux_bash.run +++ b/ports/run/noux_bash.run @@ -95,8 +95,6 @@ append config { - - diff --git a/ports/src/noux/child.h b/ports/src/noux/child.h index 34c851594b..fff915994b 100644 --- a/ports/src/noux/child.h +++ b/ports/src/noux/child.h @@ -93,7 +93,6 @@ namespace Noux { /* trigger exit of main event loop */ init_process_exited(); } - /* XXX destroy child */ } }; @@ -119,13 +118,101 @@ namespace Noux { }; - class Child : private Child_policy, - public Rpc_object, - public File_descriptor_registry + class Family_member : public List::Element { private: - int const _pid; + int const _pid; + Lock _lock; + List _list; + Family_member * const _parent; + bool _has_exited; + int _exit_status; + Semaphore _wait4_blocker; + + void _wakeup_wait4() + { + _wait4_blocker.up(); + } + + public: + + Family_member(int pid, Family_member *parent) + : _pid(pid), _parent(parent), _has_exited(false), _exit_status(0) + { } + + virtual ~Family_member() { } + + int pid() const { return _pid; } + + Family_member *parent() { return _parent; } + + int exit_status() const { return _exit_status; } + + /** + * Called by the parent at creation time of the process + */ + void insert(Family_member *member) + { + Lock::Guard guard(_lock); + _list.insert(member); + } + + /** + * Called by the parent from the return path of the wait4 syscall + */ + void remove(Family_member *member) + { + Lock::Guard guard(_lock); + _list.remove(member); + } + + /** + * Tell the parent that we exited + */ + void wakeup_parent(int exit_status) + { + _exit_status = exit_status; + _has_exited = true; + if (_parent) + _parent->_wakeup_wait4(); + } + + Family_member *poll4() + { + Lock::Guard guard(_lock); + + /* check if any of our children has exited */ + Family_member *curr = _list.first(); + for (; curr; curr = curr->next()) { + if (curr->_has_exited) + return curr; + } + return 0; + } + + /** + * Wait for the exit of any of our children + */ + Family_member *wait4() + { + for (;;) { + Family_member *result = poll4(); + if (result) + return result; + + _wait4_blocker.down(); + } + } + }; + + + class Child : private Child_policy, + public Rpc_object, + public File_descriptor_registry, + public Family_member + { + private: Signal_receiver *_sig_rec; @@ -281,6 +368,7 @@ namespace Noux { * true if the child is a fork from another child */ Child(char const *name, + Family_member *parent, int pid, Signal_receiver *sig_rec, Vfs *vfs, @@ -291,7 +379,7 @@ namespace Noux { Rpc_entrypoint &resources_ep, bool forked) : - _pid(pid), + Family_member(pid, parent), _sig_rec(sig_rec), _exit_dispatcher(this), _exit_context_cap(sig_rec->manage(&_exit_dispatcher)), @@ -324,6 +412,11 @@ namespace Noux { ~Child() { + PDBG("Destructing child %p", this); + + _sig_rec->dissolve(&_execve_cleanup_dispatcher); + _sig_rec->dissolve(&_exit_dispatcher); + /* XXX _binary_ds */ _entrypoint.dissolve(this); @@ -387,7 +480,12 @@ namespace Noux { void exit(int exit_value) { PINF("child %s exited with exit value %d", _name, exit_value); - Signal_transmitter(_exit_context_cap).submit(); + + wakeup_parent(exit_value); + + /* handle exit of the init process */ + if (parent() == 0) + Signal_transmitter(_exit_context_cap).submit(); } Ram_session *ref_ram_session() diff --git a/ports/src/noux/file_descriptor_registry.h b/ports/src/noux/file_descriptor_registry.h index caee66eed1..d4b0b31171 100644 --- a/ports/src/noux/file_descriptor_registry.h +++ b/ports/src/noux/file_descriptor_registry.h @@ -48,7 +48,7 @@ namespace Noux { return false; } - void _assign_fd(int fd, Shared_pointer io_channel) + void _assign_fd(int fd, Shared_pointer &io_channel) { _fds[fd].io_channel = io_channel; _fds[fd].allocated = true; diff --git a/ports/src/noux/main.cc b/ports/src/noux/main.cc index 9af6dee967..e498d8d105 100644 --- a/ports/src/noux/main.cc +++ b/ports/src/noux/main.cc @@ -32,7 +32,7 @@ * - pipe * ;- read init binary from vfs * - import env into child (execve and fork) - * - shell + * ;- shell * - debug 'find' * - stacked file system infrastructure * - TMP file system @@ -75,7 +75,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) { if (verbose_syscall) Genode::printf("PID %d -> SYSCALL %s\n", - _pid, Noux::Session::syscall_name(sc)); + pid(), Noux::Session::syscall_name(sc)); try { switch (sc) { @@ -157,7 +157,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) char const *env = "PWD=\"/\""; char const *filename = _sysio->execve_in.filename; Child *child = new Child(filename, - _pid, + parent(), + pid(), _sig_rec, _vfs, Args(_sysio->execve_in.args, @@ -168,6 +169,10 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _resources.ep, false); + /* replace ourself by the new child at the parent */ + parent()->remove(this); + parent()->insert(child); + _assign_io_channels_to(child); /* signal main thread to remove ourself */ @@ -290,6 +295,7 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) * reusing the name of the parent. */ Child *child = new Child(name(), + this, new_pid, _sig_rec, _vfs, @@ -300,6 +306,8 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) _resources.ep, true); + Family_member::insert(child); + _assign_io_channels_to(child); /* copy our address space into the new child */ @@ -319,25 +327,25 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc) case SYSCALL_GETPID: { - _sysio->getpid_out.pid = _pid; + _sysio->getpid_out.pid = pid(); return true; } case SYSCALL_WAIT4: { - PINF("SYSCALL_WAIT4 called"); + Family_member *exited = _sysio->wait4_in.nohang ? poll4() : wait4(); - /* - * XXX check if one of out children exited - */ + if (exited) { + _sysio->wait4_out.pid = exited->pid(); + _sysio->wait4_out.status = exited->exit_status(); + Family_member::remove(exited); - if (!_sysio->wait4_in.nohang) - _blocker.down(); - - _sysio->wait4_out.pid = -1; - _sysio->wait4_out.status = 0; - - PINF("SYSCALL_WAIT4 returning"); + /* destroy 'Noux::Child' */ + destroy(Genode::env()->heap(), exited); + } else { + _sysio->wait4_out.pid = 0; + _sysio->wait4_out.status = 0; + } return true; } @@ -504,6 +512,7 @@ int main(int argc, char **argv) static Genode::Signal_receiver sig_rec; init_child = new Noux::Child(name_of_init_process(), + 0, pid_allocator()->alloc(), &sig_rec, &vfs, @@ -540,8 +549,9 @@ int main(int argc, char **argv) Signal_dispatcher *dispatcher = static_cast(signal.context()); - for (int i = 0; i < signal.num(); i++) - dispatcher->dispatch(); + if (dispatcher) + for (int i = 0; i < signal.num(); i++) + dispatcher->dispatch(); } PINF("-- exiting noux ---");