diff --git a/miosix/_doc/textdoc/Changelog.txt b/miosix/_doc/textdoc/Changelog.txt index 08f5b3eb8cc89c164d6d7fa6a4d35a143788cced..a9ef80f87cede9bea40d788d23aeec5d2912dfb1 100644 --- a/miosix/_doc/textdoc/Changelog.txt +++ b/miosix/_doc/textdoc/Changelog.txt @@ -1,5 +1,9 @@ Changelog for Miosix np embedded OS +- Added ProcessBase class. The kernel used to have a null pointer as process, + now it has a ProcessBase instance, and is the only entity that has an + instance of PRocessBase that is not also an instance of Process. +- Merged processes branch - breaking change: updated newlib patch to make off_t a 64bit type. This allows accessing files larger than 4GByte for filesystems that support it (currently, only DevFs). This change requires recompiling GCC! diff --git a/miosix/filesystem/file_access.h b/miosix/filesystem/file_access.h index a46f5923f0af60f40e00cb33c29519f6b60bee9e..36a82883cf6288f58b67f1d9bbb6768ae7ff96ad 100644 --- a/miosix/filesystem/file_access.h +++ b/miosix/filesystem/file_access.h @@ -417,8 +417,15 @@ public: void addFileDescriptorTable(FileDescriptorTable *fdt) { #ifdef WITH_PROCESSES - Lock<FastMutex> l(mutex); - fileTables.push_back(fdt); + if(isKernelRunning()) + { + Lock<FastMutex> l(mutex); + fileTables.push_back(fdt); + } else { + //This function is also called before the kernel is started, + //and in this case it is forbidden to lock mutexes + fileTables.push_back(fdt); + } #endif //WITH_PROCESSES } diff --git a/miosix/kernel/kernel.cpp b/miosix/kernel/kernel.cpp index 9ef0323a9c8f160a2d321ff0c44bcd03a42c6c87..3d45f973b4f5219e38157f3832d12ec02e650714 100644 --- a/miosix/kernel/kernel.cpp +++ b/miosix/kernel/kernel.cpp @@ -76,6 +76,13 @@ static bool kernel_started=false;///<\internal becomes true after startKernel. /// calls to these functions. static unsigned char interruptDisableNesting=0; +#ifdef WITH_PROCESSES + +/// The proc field of the Thread class for kernel threads points to this object +static ProcessBase *kernel=0; + +#endif //WITH_PROCESSES + /** * \internal * Idle thread. Created when the kernel is started, it phisically deallocates @@ -153,6 +160,15 @@ bool areInterruptsEnabled() void startKernel() { + #ifdef WITH_PROCESSES + try { + kernel=new ProcessBase; + } catch(...) { + errorHandler(OUT_OF_MEMORY); + return; + } + #endif //WITH_PROCESSES + // Create the idle and main thread Thread *idle, *main; idle=Thread::doCreate(idleThread,STACK_IDLE,NULL,Thread::DEFAULT,true); @@ -263,7 +279,7 @@ bool IRQwakeThreads() Memory layout for a thread |------------------------| | class Thread | - |------------------------|<-- proc, this + |------------------------|<-- this | stack | | | | | V | @@ -599,12 +615,13 @@ Thread *Thread::doCreate(void*(*startfunc)(void*) , unsigned int stacksize, void Thread::IRQhandleSvc(unsigned int svcNumber) { - if(cur->proc==0) errorHandler(UNEXPECTED); + if(cur->proc==kernel) errorHandler(UNEXPECTED); if(svcNumber==1) { const_cast<Thread*>(cur)->flags.IRQsetUserspace(true); ::ctxsave=cur->userCtxsave; - cur->proc->mpu.IRQenable(); + //We know it's not the kernel, so the cast is safe + static_cast<Process*>(cur->proc)->mpu.IRQenable(); } else { const_cast<Thread*>(cur)->flags.IRQsetUserspace(false); ::ctxsave=cur->ctxsave; @@ -614,9 +631,10 @@ void Thread::IRQhandleSvc(unsigned int svcNumber) bool Thread::IRQreportFault(const miosix_private::FaultData& fault) { - if(cur->proc==0 || const_cast<Thread*>(cur)->flags.isInUserspace()==false) - return false; - cur->proc->fault=fault; + if(const_cast<Thread*>(cur)->flags.isInUserspace()==false + || cur->proc==kernel) return false; + //We know it's not the kernel, so the cast is safe + static_cast<Process*>(cur->proc)->fault=fault; const_cast<Thread*>(cur)->flags.IRQsetUserspace(false); ::ctxsave=cur->ctxsave; miosix_private::MPUConfiguration::IRQdisable(); @@ -747,6 +765,18 @@ void Thread::setupUserspaceContext(unsigned int entry, unsigned int *gotBase, #endif //WITH_PROCESSES +Thread::Thread(unsigned int *watermark, unsigned int stacksize, + bool defaultReent) : schedData(), flags(), savedPriority(0), + mutexLocked(0), mutexWaiting(0), watermark(watermark), + ctxsave(), stacksize(stacksize), cReent(defaultReent), cppReent() +{ + joinData.waitingForJoin=NULL; + #ifdef WITH_PROCESSES + proc=kernel; + userCtxsave=0; + #endif //WITH_PROCESSES +} + Thread::~Thread() { #ifdef WITH_PROCESSES diff --git a/miosix/kernel/kernel.h b/miosix/kernel/kernel.h index 561cc7d4064f54457edd1a6a5e956409d7b27ba9..ddec3c5ffbc593a97dd10414d48b9d466e2a2701 100644 --- a/miosix/kernel/kernel.h +++ b/miosix/kernel/kernel.h @@ -386,15 +386,13 @@ bool isKernelRunning(); */ long long getTick(); -//Declaration of the struct, definition follows below +//Forwrd declaration struct SleepData; - -//Forwrd declaration of classes class MemoryProfiling; class Mutex; class ConditionVariable; #ifdef WITH_PROCESSES -class Process; +class ProcessBase; #endif //WITH_PROCESSES /** @@ -692,6 +690,11 @@ public: static const int getStackSize(); #ifdef WITH_PROCESSES + + /** + * \return the process associated with the thread + */ + ProcessBase *getProcess() { return proc; } /** * \internal @@ -907,21 +910,11 @@ private: /** * Constructor, initializes thread data. - * \param priority thread's priority * \param watermark pointer to watermark area * \param stacksize thread's stack size + * \param defaultReent true if the global reentrancy structure is to be used */ - Thread(unsigned int *watermark, unsigned int stacksize, bool defaultReent): - schedData(), flags(), savedPriority(0), mutexLocked(0), mutexWaiting(0), - watermark(watermark), ctxsave(), stacksize(stacksize), - cReent(defaultReent), cppReent() - { - joinData.waitingForJoin=NULL; - #ifdef WITH_PROCESSES - proc=0; - userCtxsave=0; - #endif //WITH_PROCESSES - } + Thread(unsigned int *watermark, unsigned int stacksize, bool defaultReent); /** * Destructor @@ -981,7 +974,7 @@ private: CppReentrancyData cppReent; #ifdef WITH_PROCESSES ///Process to which this thread belongs. Null if it is a kernel thread. - Process *proc; + ProcessBase *proc; ///Pointer to the set of saved registers for when the thread is running in ///user mode. For kernel threads (i.e, threads where proc==null) this ///pointer is null diff --git a/miosix/kernel/process.cpp b/miosix/kernel/process.cpp index 18a6ba0bf9f1c1f1d801b7ce7dfbb7c4d21c1357..cdd68c1eef10a3540c2fa40ca1bd588ff8f8112f 100644 --- a/miosix/kernel/process.cpp +++ b/miosix/kernel/process.cpp @@ -29,6 +29,7 @@ #include <memory> #include <cstdio> #include <cstring> +#include <cassert> #include <algorithm> #include <sys/wait.h> #include <sys/types.h> @@ -86,19 +87,22 @@ public: */ static Processes& instance(); - ///Maps the pid to the Process instance. Includes zombie processes - map<pid_t,Process *> processes; - list<Process *> kernelChilds; - list<Process *> kernelZombies; ///Used to assign a new pid to a process pid_t pidCounter; + ///Maps the pid to the Process instance. Includes zombie processes + map<pid_t,ProcessBase *> processes; ///Uset to guard access to processes and pidCounter Mutex procMutex; ///Used to wait on process termination ConditionVariable genericWaiting; private: - Processes() {} + Processes() + { + ProcessBase *kernel=Thread::getCurrentThread()->getProcess(); + assert(kernel->getPid()==0); + processes[0]=kernel; + } Processes(const Processes&); Processes& operator=(const Processes&); }; @@ -116,30 +120,22 @@ Processes& Processes::instance() pid_t Process::create(const ElfProgram& program) { Processes& p=Processes::instance(); + ProcessBase *parent=Thread::getCurrentThread()->proc; auto_ptr<Process> proc(new Process(program)); { Lock<Mutex> l(p.procMutex); proc->pid=getNewPid(); - if(Thread::getCurrentThread()->proc!=0) - { - proc->ppid=Thread::getCurrentThread()->proc->pid; - Thread::getCurrentThread()->proc->childs.push_back(proc.get()); - } else { - proc->ppid=0; - p.kernelChilds.push_back(proc.get()); - } + proc->ppid=parent->pid; + parent->childs.push_back(proc.get()); p.processes[proc->pid]=proc.get(); } - Thread *thr=Thread::createUserspace(Process::start,0,Thread::DEFAULT, - proc.get()); + Thread *thr; + thr=Thread::createUserspace(Process::start,0,Thread::DEFAULT,proc.get()); if(thr==0) { Lock<Mutex> l(p.procMutex); p.processes.erase(proc->pid); - if(Thread::getCurrentThread()->proc!=0) - { - Thread::getCurrentThread()->proc->childs.remove(proc.get()); - } else p.kernelChilds.remove(proc.get()); + parent->childs.remove(proc.get()); throw runtime_error("Thread creation failed"); } //Cannot throw bad_alloc due to the reserve in Process's constructor. @@ -157,7 +153,7 @@ pid_t Process::getppid(pid_t proc) { Processes& p=Processes::instance(); Lock<Mutex> l(p.procMutex); - map<pid_t,Process *>::iterator it=p.processes.find(proc); + map<pid_t,ProcessBase *>::iterator it=p.processes.find(proc); if(it==p.processes.end()) return -1; return it->second->ppid; } @@ -166,101 +162,51 @@ pid_t Process::waitpid(pid_t pid, int* exit, int options) { Processes& p=Processes::instance(); Lock<Mutex> l(p.procMutex); - Process *self=Thread::getCurrentThread()->proc; - if(self==0) + ProcessBase *self=Thread::getCurrentThread()->proc; + if(pid<=0) { - //The wait is performed by the kernel - if(pid<=0) + //Wait for a generic child process + if(self->zombies.empty() && (options & WNOHANG)) return 0; + while(self->zombies.empty()) { - //Wait for a generic child process - if(p.kernelZombies.empty() && (options & WNOHANG)) return 0; - while(p.kernelZombies.empty()) - { - if(p.kernelChilds.empty()) return -1; - p.genericWaiting.wait(l); - } - Process *joined=p.kernelZombies.front(); - p.kernelZombies.pop_front(); - p.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=p.processes.find(pid); - if(it==p.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; - p.kernelZombies.remove(joined); - p.processes.erase(joined->pid); - delete joined; - } - return result; + if(self->childs.empty()) return -1; + p.genericWaiting.wait(l); } + Process *joined=self->zombies.front(); + self->zombies.pop_front(); + p.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 { - //The wait is performed by a process - if(pid<=0) + //Wait on a specific child process + map<pid_t,ProcessBase *>::iterator it=p.processes.find(pid); + if(it==p.processes.end() || it->second->ppid!=self->pid + || pid==self->pid) return -1; + //Since the case when pid==0 has been singled out, this cast is safe + Process *joined=static_cast<Process*>(it->second); + if(joined->zombie==false) { - //Wait for a generic child process - if(self->zombies.empty() && (options & WNOHANG)) return 0; - while(self->zombies.empty()) - { - if(self->childs.empty()) return -1; - p.genericWaiting.wait(l); - } - Process *joined=self->zombies.front(); - self->zombies.pop_front(); - p.processes.erase(joined->pid); - if(joined->waitCount!=0) errorHandler(UNEXPECTED); + //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); + } + pid_t result=-1; + if(joined->waitCount==0) + { + result=joined->pid; if(exit!=0) *exit=joined->exitCode; - pid_t result=joined->pid; + self->zombies.remove(joined); + p.processes.erase(joined->pid); delete joined; - return result; - } else { - //Wait on a specific child process - map<pid_t,Process *>::iterator it=p.processes.find(pid); - if(it==p.processes.end() || it->second->ppid!=self->pid - || pid==self->pid) 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); - } - pid_t result=-1; - if(joined->waitCount==0) - { - result=joined->pid; - if(exit!=0) *exit=joined->exitCode; - self->zombies.remove(joined); - p.processes.erase(joined->pid); - delete joined; - } - return result; } + return result; } } @@ -312,7 +258,8 @@ Process::Process(const ElfProgram& program) : program(program), waitCount(0), void *Process::start(void *argv) { - Process *proc=Thread::getCurrentThread()->proc; + //This function is never called with a kernel thread, so the cast is safe + Process *proc=static_cast<Process*>(Thread::getCurrentThread()->proc); if(proc==0) errorHandler(UNEXPECTED); unsigned int entry=proc->program.getEntryPoint(); #ifdef __CODE_IN_XRAM @@ -481,25 +428,17 @@ void *Process::start(void *argv) 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; - p.kernelChilds.splice(p.kernelChilds.begin(),proc->childs); - p.kernelZombies.splice(p.kernelZombies.begin(),proc->zombies); - if(proc->ppid!=0) - { - map<pid_t,Process *>::iterator it=p.processes.find(proc->ppid); - if(it==p.processes.end()) errorHandler(UNEXPECTED); - it->second->childs.remove(proc); - if(proc->waitCount>0) proc->waiting.broadcast(); - else { - it->second->zombies.push_back(proc); - p.genericWaiting.broadcast(); - } - } else { - p.kernelChilds.remove(proc); - if(proc->waitCount>0) proc->waiting.broadcast(); - else { - p.kernelZombies.push_back(proc); - p.genericWaiting.broadcast(); - } + ProcessBase *kernel=p.processes[0]; + kernel->childs.splice(kernel->childs.begin(),proc->childs); + kernel->zombies.splice(kernel->zombies.begin(),proc->zombies); + + map<pid_t,ProcessBase *>::iterator it2=p.processes.find(proc->ppid); + if(it2==p.processes.end()) errorHandler(UNEXPECTED); + it2->second->childs.remove(proc); + if(proc->waitCount>0) proc->waiting.broadcast(); + else { + it2->second->zombies.push_back(proc); + p.genericWaiting.broadcast(); } } return 0; @@ -512,12 +451,12 @@ pid_t Process::getNewPid() { if(p.pidCounter<0) p.pidCounter=1; if(p.pidCounter==0) continue; //Zero is not a valid pid - map<pid_t,Process*>::iterator it=p.processes.find(p.pidCounter); + map<pid_t,ProcessBase*>::iterator it=p.processes.find(p.pidCounter); if(it!=p.processes.end()) continue; //Pid number already used return p.pidCounter++; } } - + } //namespace miosix #endif //WITH_PROCESSES diff --git a/miosix/kernel/process.h b/miosix/kernel/process.h index 74000c49a4c36fe1a3c093ae61f41a366944c8af..c8e7a8e9604a64fbd9d131eb638942af502c1cd2 100644 --- a/miosix/kernel/process.h +++ b/miosix/kernel/process.h @@ -42,10 +42,42 @@ namespace miosix { +class Process; + +/** + * This class contains the fields that are in common between the kernel and + * processes + */ +class ProcessBase +{ +public: + /** + * Constructor + */ + ProcessBase() : pid(0), ppid(0) {} + + /** + * \return the process' pid + */ + pid_t getPid() const { return pid; } + +protected: + pid_t pid; ///<The pid of this process + pid_t ppid; ///<The parent pid of this process + std::list<Process *> childs; ///<Living child processes are stored here + std::list<Process *> zombies; ///<Dead child processes are stored here + +private: + ProcessBase(const ProcessBase&); + ProcessBase& operator= (const ProcessBase&); + + friend class Process; +}; + /** * Process class, allows to create and handle processes */ -class Process +class Process : public ProcessBase { public: /** @@ -91,8 +123,6 @@ public: ~Process(); private: - Process(const Process&); - Process& operator= (const Process&); /** * Constructor @@ -124,11 +154,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 + std::set<int> mFiles; ///<Files openend by this process - 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 ///to wait on this process int waitCount; diff --git a/miosix/kernel/scheduler/control/control_scheduler.cpp b/miosix/kernel/scheduler/control/control_scheduler.cpp index 3be4cd723afbe27ce96d7b2fafb1a57688aaf092..32fba7b71057f012c64470436113c973e61cdc48 100644 --- a/miosix/kernel/scheduler/control/control_scheduler.cpp +++ b/miosix/kernel/scheduler/control/control_scheduler.cpp @@ -224,7 +224,8 @@ void ControlScheduler::IRQfindNextThread() miosix_private::MPUConfiguration::IRQdisable(); } else { ctxsave=cur->userCtxsave; - cur->proc->mpu.IRQenable(); + //A kernel thread is never in userspace, so the cast is safe + static_cast<Process*>(cur->proc)->mpu.IRQenable(); } #else //WITH_PROCESSES ctxsave=cur->ctxsave; diff --git a/miosix/kernel/scheduler/edf/edf_scheduler.cpp b/miosix/kernel/scheduler/edf/edf_scheduler.cpp index 5eac0a0b159ab7527435b46ed421a7092979d833..1d51253d71f99cf03d1f8ad44c4f847691b92ca0 100644 --- a/miosix/kernel/scheduler/edf/edf_scheduler.cpp +++ b/miosix/kernel/scheduler/edf/edf_scheduler.cpp @@ -123,7 +123,8 @@ void EDFScheduler::IRQfindNextThread() miosix_private::MPUConfiguration::IRQdisable(); } else { ctxsave=cur->userCtxsave; - cur->proc->mpu.IRQenable(); + //A kernel thread is never in userspace, so the cast is safe + static_cast<Process*>(cur->proc)->mpu.IRQenable(); } #else //WITH_PROCESSES ctxsave=cur->ctxsave; diff --git a/miosix/kernel/scheduler/priority/priority_scheduler.cpp b/miosix/kernel/scheduler/priority/priority_scheduler.cpp index 90c96295d107b68dcb16ad7283a459d66bc6b9e8..fb3cb32a16ad102a64c76757f4c8194e41e615b3 100644 --- a/miosix/kernel/scheduler/priority/priority_scheduler.cpp +++ b/miosix/kernel/scheduler/priority/priority_scheduler.cpp @@ -217,7 +217,8 @@ void PriorityScheduler::IRQfindNextThread() miosix_private::MPUConfiguration::IRQdisable(); } else { ctxsave=cur->userCtxsave; - cur->proc->mpu.IRQenable(); + //A kernel thread is never in userspace, so the cast is safe + static_cast<Process*>(cur->proc)->mpu.IRQenable(); } #else //WITH_PROCESSES ctxsave=temp->ctxsave; diff --git a/miosix_np_2/nbproject/private/private.xml b/miosix_np_2/nbproject/private/private.xml index 9e5653dedb352ed62af9dc5a5c6c3c7896814e32..fc1d89ef527a8c1d35a73972b2c992c0e32ddb50 100644 --- a/miosix_np_2/nbproject/private/private.xml +++ b/miosix_np_2/nbproject/private/private.xml @@ -11,6 +11,10 @@ <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/> <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2"> <group> + <file>file:/media/truecrypt1/Projects/ARM/miosix/miosix-kernel/miosix/kernel/process.h</file> + <file>file:/media/truecrypt1/Projects/ARM/miosix/miosix-kernel/miosix/kernel/kernel.h</file> + <file>file:/media/truecrypt1/Projects/ARM/miosix/miosix-kernel/miosix/kernel/process.cpp</file> + <file>file:/media/truecrypt1/Projects/ARM/miosix/miosix-kernel/miosix/kernel/kernel.cpp</file> <file>file:/media/truecrypt1/Projects/ARM/miosix/miosix-kernel/main.cpp</file> </group> </open-files>