diff --git a/main.cpp b/main.cpp index 257b9034f21b87738e3f77de12227271981ce15a..b2cc9a2761491d5b55b15c3c7652abdb97f68110 100644 --- a/main.cpp +++ b/main.cpp @@ -1,5 +1,7 @@ #include <cstdio> +#include <sys/wait.h> +#include <signal.h> #include "miosix.h" #include "kernel/process.h" #include "app_template/prog3.h" @@ -7,12 +9,8 @@ using namespace std; using namespace miosix; -int main() -{ - getchar(); - - ElfProgram prog(reinterpret_cast<const unsigned int*>(main_elf),main_elf_len); - Process::create(prog); +void ledThread(void *) +{ for(;;) { ledOn(); @@ -21,3 +19,24 @@ int main() Thread::sleep(200); } } + +int main() +{ + Thread::create(ledThread,STACK_MIN); + + ElfProgram prog(reinterpret_cast<const unsigned int*>(main_elf),main_elf_len); + for(;;) + { + getchar(); + Process::create(prog); + int ec; + pid_t pid=Process::wait(&ec); + iprintf("Process %d terminated\n",pid); + if(WIFEXITED(ec)) + { + iprintf("Exit code is %d\n",WEXITSTATUS(ec)); + } else if(WIFSIGNALED(ec)) { + if(WTERMSIG(ec)==SIGSEGV) iprintf("Process segfaulted\n"); + } + } +} diff --git a/miosix/kernel/process.cpp b/miosix/kernel/process.cpp index a91c76992568470e4a8cef3656294bfbb7d54b6e..240b1a21022bda7fbbdbb6f7bbcaab5f8541c035 100644 --- a/miosix/kernel/process.cpp +++ b/miosix/kernel/process.cpp @@ -29,6 +29,8 @@ #include <memory> #include <cstdio> #include <cstring> +#include <sys/wait.h> +#include <signal.h> #include "sync.h" #include "process_pool.h" #include "process.h" @@ -70,7 +72,7 @@ namespace miosix { // class Process // -Process *Process::create(const ElfProgram& program) +pid_t Process::create(const ElfProgram& program) { auto_ptr<Process> proc(new Process(program)); { @@ -95,10 +97,87 @@ Process *Process::create(const ElfProgram& program) //among the threads of a process proc->threads.push_back(thr); thr->wakeup(); //Actually start the thread, now that everything is set up - return proc.release(); //Do not delete the pointer + pid_t result=proc->pid; + proc.release(); //Do not delete the pointer + return result; } -Process::Process(const ElfProgram& program) : program(program) +pid_t Process::getppid(pid_t proc) +{ + Lock<Mutex> l(procMutex); + map<pid_t,Process *>::iterator it=processes.find(proc); + if(it==processes.end()) return -1; + return it->second->ppid; +} + +pid_t Process::waitpid(pid_t pid, int* exit, int options) +{ + Lock<Mutex> l(procMutex); + pid_t result=-1; + if(pid<=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()) + { + genericWaiting.wait(l); + it=zombies.find(ppid); + } + 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()) + { + Process *joined=it->second; + if(joined->exitCode==-1) + { + //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) + errorHandler(UNEXPECTED); + } + result=joined->pid; + if(exit!=0) *exit=joined->exitCode; + 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); + delete joined; + } + } + } + return result; +} + +Process::~Process() +{ + #ifdef __CODE_IN_XRAM + ProcessPool::instance().deallocate(loadedProgram); + #endif //__CODE_IN_XRAM +} + +Process::Process(const ElfProgram& program) : program(program), waitCount(0), + exitCode(-1) { //This is required so that bad_alloc can never be thrown when the first //thread of the process will be stored in this vector @@ -112,8 +191,18 @@ Process::Process(const ElfProgram& program) : program(program) if(elfSize<ProcessPool::blockSize) roundedSize=ProcessPool::blockSize; else if(elfSize & (elfSize-1)) roundedSize=1<<ffs(elfSize); #ifndef __CODE_IN_XRAM - mpu=miosix_private::MPUConfiguration(program.getElfBase(),roundedSize, + //FIXME -- begin + //Till a flash file system that ensures proper alignment of the programs + //loaded in flash is implemented, make the whole flash visible as a big MPU + //region + extern unsigned char _end asm("_end"); + unsigned int flashEnd=reinterpret_cast<unsigned int>(&_end); + if(flashEnd & (flashEnd-1)) flashEnd=1<<ffs(flashEnd); + mpu=miosix_private::MPUConfiguration(0,flashEnd, image.getProcessBasePointer(),image.getProcessImageSize()); +// mpu=miosix_private::MPUConfiguration(program.getElfBase(),roundedSize, +// image.getProcessBasePointer(),image.getProcessImageSize()); + //FIXME -- end #else //__CODE_IN_XRAM loadedProgram=ProcessPool::instance().allocate(roundedSize); memcpy(loadedProgram,reinterpret_cast<char*>(program.getElfBase()),elfSize); @@ -133,12 +222,14 @@ 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 #ifdef WITH_ERRLOG iprintf("Process %d terminated due to a fault\n" "* Code base address was 0x%x\n" @@ -157,9 +248,7 @@ void *Process::start(void *argv) { case 2: running=false; - #ifdef WITH_ERRLOG - iprintf("Exit %d\n",sp.getFirstParameter()); //FIXME: remove - #endif //WITH_ERRLOG + returnValue=(sp.getFirstParameter() & 0xff)<<8; break; case 3: //FIXME: check that the pointer belongs to the process @@ -178,6 +267,7 @@ void *Process::start(void *argv) break; default: running=false; + returnValue=SIGSYS; //Bad syscall #ifdef WITH_ERRLOG iprintf("Unexpected syscall number %d\n",sp.getSyscallId()); #endif //WITH_ERRLOG @@ -186,7 +276,15 @@ void *Process::start(void *argv) } if(Thread::testTerminate()) running=false; } while(running); - //TODO: handle process termination + { + Lock<Mutex> l(procMutex); + proc->exitCode=returnValue; + if(proc->waitCount>0) proc->waiting.broadcast(); + else { + zombies.insert(make_pair(proc->ppid,proc)); + genericWaiting.broadcast(); + } + } return 0; } @@ -203,8 +301,10 @@ pid_t Process::getNewPid() } map<pid_t,Process*> Process::processes; +multimap<pid_t,Process *> Process::zombies; pid_t Process::pidCounter=1; Mutex Process::procMutex; +ConditionVariable Process::genericWaiting; } //namespace miosix diff --git a/miosix/kernel/process.h b/miosix/kernel/process.h index 83e33bd96f048c577db5b9cf676a2729cac00f20..98fa09198b89b9577e932305160b5a0fddaab0af 100644 --- a/miosix/kernel/process.h +++ b/miosix/kernel/process.h @@ -32,6 +32,7 @@ #include <map> #include <sys/types.h> #include "kernel.h" +#include "sync.h" #include "elf_program.h" #include "config/miosix_settings.h" @@ -48,22 +49,19 @@ public: /** * Create a new process * \param program Program that the process will execute - * \return a pointer to the newly created process + * \return the pid of the newly created process * \throws std::exception or a subclass in case of errors, including * not emough memory to spawn the process */ - static Process *create(const ElfProgram& program); - - /** - * \return the pid of the created process - */ - pid_t getpid() const { return pid; } + static pid_t create(const ElfProgram& program); /** + * Given a process, returns the pid of its parent. + * \param proc the pid of a process * \return the pid of the parent process, or zero if the process was created - * by the kernel directly + * by the kernel directly, or -1 if proc is not a valid process */ - pid_t getppid() const { return ppid; } + static pid_t getppid(pid_t proc); /** * Wait for child process termination @@ -71,7 +69,7 @@ public: * is not null * \return the pid of the terminated process, or -1 in case of errors */ - pid_t wait(int *exit); + static pid_t wait(int *exit) { return waitpid(-1,exit,0); } /** * Wait for a specific child process to terminate @@ -83,7 +81,12 @@ public: * case WNOHANG is specified and the specified process has not terminated, * 0 is returned */ - pid_t waitpid(pid_t pid, int *exit, int options); + static pid_t waitpid(pid_t pid, int *exit, int options); + + /** + * Destructor + */ + ~Process(); private: Process(const Process&); @@ -116,29 +119,29 @@ private: #endif //__CODE_IN_XRAM ProcessImage image; ///<The RAM image of a process miosix_private::FaultData fault; ///< Contains information about faults - std::vector<Thread *> threads; ///<Threads that belong to the process miosix_private::MPUConfiguration mpu; ///<Memory protection data + std::vector<Thread *> threads; ///<Threads that belong to the process pid_t pid; ///<The pid of this process pid_t ppid; ///<The parent pid of this process - ///This union is used to join threads. When the thread to join has not yet - ///terminated and no other thread called join it contains (Thread *)NULL, - ///when a thread calls join on this thread it contains the thread waiting - ///for the join, and when the thread terminated it contains (void *)result - union - { - Thread *waitingForJoin;///<Thread waiting to join this - int exitCode; ///<Process exit code - } joinData; + ///Contains the count of active wait calls which specifically requested + ///to wait on this process + int waitCount; + ///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 - ///Maps the pid to the Process instance + ///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; ///Used to assign a new pid to a process static pid_t pidCounter; ///Uset to guard access to processes and pidCounter static Mutex procMutex; ///Used to wait on process termination - static ConditionVariable waiting; + static ConditionVariable genericWaiting; //Needs access to fault,mpu friend class Thread;