From c857021bca7f49c3f478c8d3d5f824298dc74f32 Mon Sep 17 00:00:00 2001 From: Terraneo Federico <fede.tft@hotmail.it> Date: Thu, 3 May 2012 13:31:08 +0200 Subject: [PATCH] Finished implementing waitpid, needs testing --- main.cpp | 8 +- miosix/kernel/process.cpp | 161 +++++++++++++++++++++++++++----------- miosix/kernel/process.h | 10 ++- 3 files changed, 127 insertions(+), 52 deletions(-) diff --git a/main.cpp b/main.cpp index b2cc9a27..75f70ce4 100644 --- a/main.cpp +++ b/main.cpp @@ -25,12 +25,14 @@ int main() Thread::create(ledThread,STACK_MIN); ElfProgram prog(reinterpret_cast<const unsigned int*>(main_elf),main_elf_len); - for(;;) + for(int i=0;;i++) { getchar(); - Process::create(prog); + pid_t child=Process::create(prog); int ec; - pid_t pid=Process::wait(&ec); + pid_t pid; + if(i%2==0) pid=Process::wait(&ec); + else pid=Process::waitpid(child,&ec,0); iprintf("Process %d terminated\n",pid); if(WIFEXITED(ec)) { diff --git a/miosix/kernel/process.cpp b/miosix/kernel/process.cpp index 240b1a21..9ac56c20 100644 --- a/miosix/kernel/process.cpp +++ b/miosix/kernel/process.cpp @@ -79,8 +79,13 @@ pid_t Process::create(const ElfProgram& program) Lock<Mutex> l(procMutex); proc->pid=getNewPid(); if(Thread::getCurrentThread()->proc!=0) + { proc->ppid=Thread::getCurrentThread()->proc->pid; - else proc->ppid=0; + Thread::getCurrentThread()->proc->childs.push_back(proc.get()); + } else { + proc->ppid=0; + kernelChilds.push_back(proc.get()); + } processes[proc->pid]=proc.get(); } Thread *thr=Thread::createUserspace(Process::start,0,Thread::DEFAULT, @@ -89,6 +94,10 @@ pid_t Process::create(const ElfProgram& program) { Lock<Mutex> l(procMutex); processes.erase(proc->pid); + if(Thread::getCurrentThread()->proc!=0) + { + Thread::getCurrentThread()->proc->childs.remove(proc.get()); + } else kernelChilds.remove(proc.get()); throw runtime_error("Thread creation failed"); } //Cannot throw bad_alloc due to the reserve in Process's constructor. @@ -113,60 +122,102 @@ pid_t Process::getppid(pid_t proc) pid_t Process::waitpid(pid_t pid, int* exit, int options) { Lock<Mutex> l(procMutex); - pid_t result=-1; - if(pid<=0) + Process *self=Thread::getCurrentThread()->proc; + if(self==0) { - //Wait for a generic child process - Process *self=Thread::getCurrentThread()->proc; - pid_t ppid=self==0 ? 0 : self->pid; - multimap<pid_t,Process *>::iterator it=zombies.find(ppid); - if((options & WNOHANG) && it==zombies.end()) return 0; - while(it==zombies.end()) + //The wait is performed by the kernel + if(pid<=0) { - genericWaiting.wait(l); - it=zombies.find(ppid); + //Wait for a generic child process + if(kernelZombies.empty() && (options & WNOHANG)) return 0; + while(kernelZombies.empty()) + { + if(kernelChilds.empty()) return -1; + genericWaiting.wait(l); + } + Process *joined=kernelZombies.front(); + kernelZombies.pop_front(); + processes.erase(joined->pid); + if(joined->waitCount!=0) errorHandler(UNEXPECTED); + if(exit!=0) *exit=joined->exitCode; + pid_t result=joined->pid; + delete joined; + return result; + } else { + //Wait on a specific child process + map<pid_t,Process *>::iterator it=processes.find(pid); + if(it==processes.end() || it->second->ppid!=0) return -1; + Process *joined=it->second; + if(joined->zombie==false) + { + //Process hasn't terminated yet + if(options & WNOHANG) return 0; + joined->waitCount++; + joined->waiting.wait(l); + joined->waitCount--; + if(joined->waitCount<0 || joined->zombie==false) + errorHandler(UNEXPECTED); + } + //If multiple threads call waitpid on the same child, the last + //gets the return value, the other -1 + pid_t result=-1; + if(joined->waitCount==0) + { + if(exit!=0) *exit=joined->exitCode; + result=joined->pid; + kernelZombies.remove(joined); + processes.erase(joined->pid); + delete joined; + } + return result; } - Process *joined=it->second; - if(joined->waitCount!=0) errorHandler(UNEXPECTED); - result=joined->pid; - if(exit!=0) *exit=joined->exitCode; - zombies.erase(it); - processes.erase(result); - delete joined; } else { - //Wait on a specific process - map<pid_t,Process *>::iterator it=processes.find(pid); - if(it!=processes.end()) + //The wait is performed by a process + if(pid<=0) { + //Wait for a generic child process + if(self->zombies.empty() && (options & WNOHANG)) return 0; + while(self->zombies.empty()) + { + if(self->childs.empty()) return -1; + genericWaiting.wait(l); + } + Process *joined=self->zombies.front(); + self->zombies.pop_front(); + processes.erase(joined->pid); + if(joined->waitCount!=0) errorHandler(UNEXPECTED); + if(exit!=0) *exit=joined->exitCode; + pid_t result=joined->pid; + delete joined; + return result; + } else { + //Wait on a specific child process + map<pid_t,Process *>::iterator it=processes.find(pid); + if(it==processes.end() || it->second->ppid!=self->pid + || pid==self->pid) return -1; Process *joined=it->second; - if(joined->exitCode==-1) + if(joined->zombie==false) { //Process hasn't terminated yet if(options & WNOHANG) return 0; joined->waitCount++; joined->waiting.wait(l); joined->waitCount--; - if(joined->waitCount<0 || joined->exitCode==-1) + if(joined->waitCount<0 || joined->zombie==false) errorHandler(UNEXPECTED); } - result=joined->pid; - if(exit!=0) *exit=joined->exitCode; + pid_t result=-1; if(joined->waitCount==0) { - typedef typename multimap<pid_t,Process *>::iterator iterator; - pair<iterator,iterator> p=zombies.equal_range(joined->ppid); - for(iterator it=p.first;it!=p.second;++it) - { - if(it->second!=joined) continue; - zombies.erase(it); - break; - } - processes.erase(result); + result=joined->pid; + if(exit!=0) *exit=joined->exitCode; + self->zombies.remove(joined); + processes.erase(joined->pid); delete joined; } + return result; } } - return result; } Process::~Process() @@ -177,7 +228,7 @@ Process::~Process() } Process::Process(const ElfProgram& program) : program(program), waitCount(0), - exitCode(-1) + zombie(false) { //This is required so that bad_alloc can never be thrown when the first //thread of the process will be stored in this vector @@ -222,14 +273,13 @@ void *Process::start(void *argv) #endif //__CODE_IN_XRAM Thread::setupUserspaceContext(entry,proc->image.getProcessBasePointer(), proc->image.getProcessImageSize()); - int returnValue=0; bool running=true; do { miosix_private::SyscallParameters sp=Thread::switchToUserspace(); if(proc->fault.faultHappened()) { running=false; - returnValue=SIGSEGV; //Segfault + proc->exitCode=SIGSEGV; //Segfault #ifdef WITH_ERRLOG iprintf("Process %d terminated due to a fault\n" "* Code base address was 0x%x\n" @@ -248,7 +298,7 @@ void *Process::start(void *argv) { case 2: running=false; - returnValue=(sp.getFirstParameter() & 0xff)<<8; + proc->exitCode=(sp.getFirstParameter() & 0xff)<<8; break; case 3: //FIXME: check that the pointer belongs to the process @@ -267,7 +317,7 @@ void *Process::start(void *argv) break; default: running=false; - returnValue=SIGSYS; //Bad syscall + proc->exitCode=SIGSYS; //Bad syscall #ifdef WITH_ERRLOG iprintf("Unexpected syscall number %d\n",sp.getSyscallId()); #endif //WITH_ERRLOG @@ -278,11 +328,29 @@ void *Process::start(void *argv) } while(running); { Lock<Mutex> l(procMutex); - proc->exitCode=returnValue; - if(proc->waitCount>0) proc->waiting.broadcast(); - else { - zombies.insert(make_pair(proc->ppid,proc)); - genericWaiting.broadcast(); + proc->zombie=true; + list<Process*>::iterator it; + for(it=proc->childs.begin();it!=proc->childs.end();++it) (*it)->ppid=0; + for(it=proc->zombies.begin();it!=proc->zombies.end();++it) (*it)->ppid=0; + kernelChilds.splice(kernelChilds.begin(),proc->childs); + kernelZombies.splice(kernelZombies.begin(),proc->zombies); + if(proc->ppid!=0) + { + map<pid_t,Process *>::iterator it=processes.find(proc->ppid); + if(it==processes.end()) errorHandler(UNEXPECTED); + it->second->childs.remove(proc); + if(proc->waitCount>0) proc->waiting.broadcast(); + else { + it->second->zombies.push_back(proc); + genericWaiting.broadcast(); + } + } else { + kernelChilds.remove(proc); + if(proc->waitCount>0) proc->waiting.broadcast(); + else { + kernelZombies.push_back(proc); + genericWaiting.broadcast(); + } } } return 0; @@ -301,7 +369,8 @@ pid_t Process::getNewPid() } map<pid_t,Process*> Process::processes; -multimap<pid_t,Process *> Process::zombies; +std::list<Process *> Process::kernelChilds; +std::list<Process *> Process::kernelZombies; pid_t Process::pidCounter=1; Mutex Process::procMutex; ConditionVariable Process::genericWaiting; diff --git a/miosix/kernel/process.h b/miosix/kernel/process.h index 98fa0919..dc54e1af 100644 --- a/miosix/kernel/process.h +++ b/miosix/kernel/process.h @@ -30,6 +30,7 @@ #include <vector> #include <map> +#include <list> #include <sys/types.h> #include "kernel.h" #include "sync.h" @@ -122,6 +123,8 @@ private: miosix_private::MPUConfiguration mpu; ///<Memory protection data std::vector<Thread *> threads; ///<Threads that belong to the process + std::list<Process *> childs; ///<Living child processes are stored here + std::list<Process *> zombies; ///<Dead child processes are stored here pid_t pid; ///<The pid of this process pid_t ppid; ///<The parent pid of this process ///Contains the count of active wait calls which specifically requested @@ -130,12 +133,13 @@ private: ///Active wait calls which specifically requested to wait on this process ///wait on this condition variable ConditionVariable waiting; - int exitCode; ///< Contains -1 if process is running, or the exit code + bool zombie; ///< True for terminated not yet joined processes + short int exitCode; ///< Contains the exit code ///Maps the pid to the Process instance. Includes zombie processes static std::map<pid_t,Process *> processes; - ///Terminated but not joined processes, the key is the parent's pid - static std::multimap<pid_t,Process *> zombies; + static std::list<Process *> kernelChilds; + static std::list<Process *> kernelZombies; ///Used to assign a new pid to a process static pid_t pidCounter; ///Uset to guard access to processes and pidCounter -- GitLab