Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 39
Текст из файла (страница 39)
Thekernel indexes into a DObjectCon much more rarely than it indexes intoa DObjectIx, so the overhead of waiting on the mutex is not significantin this case.Array management for DObjectCon is simpler than for DObjectIx.The first iCount slots are occupied with no gaps – removing an entry172KERNEL SERVICESwill move all the following entries down one. When the kernel adds anew entry, it always goes at the end of the array.Here is the DObjectCon class:class DObjectCon : public DBase{protected:enum {ENotOwnerID};public:∼DObjectCon();static DObjectCon* New(TInt aUniqueID);IMPORT_C void Remove(DObject* aObj);IMPORT_C TInt Add(DObject* aObj);IMPORT_C DObject* operator[](TInt aIndex);IMPORT_C DObject* At(TInt aFindHandle);IMPORT_C TInt CheckUniqueFullName(DObject* aOwner, const TDesC& aName);IMPORT_C TInt CheckUniqueFullName(DObject* aObject);IMPORT_C TInt FindByName(TInt& aFindHandle, const TDesC& aMatch,TKName& aName);IMPORT_C TInt FindByFullName(TInt& aFindHandle, const TDesC& aMatch,TFullName& aFullName);IMPORT_C TInt OpenByFullName(DObject*& aObject, const TDesC& aMatch);inline TInt UniqueID() {return iUniqueID;}inline TInt Count() {return iCount;}inline void Wait() {Kern::MutexWait(*iMutex);}inline void Signal() {Kern::MutexSignal(*iMutex);}inline DMutex* Lock() {return iMutex;}protected:DObjectCon(TInt aUniqueID);TBool NamesMatch(DObject* aObject, DObject* aCurrentObject);TBool NamesMatch(DObject* aObject, const TDesC& aObjectName,DObject* aCurrentObject);public:TInt iUniqueID;private:TInt iAllocated;TInt iCount;DObject** iObjects;DMutex* iMutex;};Key member data of DObjectConiUniqueIDThis is an identity number indicating the type of kernel object heldin this container.
The value used is 1 + the corresponding value inthe TObjectType enumeration – for example the identity number forthreads is 1.iAllocatedThis is the number of slots currently allocated in the iObjects array.SERVICES PROVIDED TO USER THREADS173iCountThis is the number of slots currently occupied in the iObjects array.iObjectsThis is the pointer to the array of pointers to DObjects that are currentlyheld in this container.iMutexThis is the pointer to the DMutex mutex object that the kernel uses toprotect accesses to this container.5.2 Services provided to user threads5.2.1 Executive call mechanismThe kernel provides services to user-mode code using a mechanism thatwe call executive calls, or exec calls for short.Exec calls begin as a standard user-side function, and then use asoftware exception as a gateway to allow them to enter kernel code.
Thesoftware exception instruction switches the CPU into supervisor modeand starts the execution of kernel code at a defined entry point – seeChapter 6, Interrupts and Exceptions, for more on this.The CPU’s instruction set generally limits the number of possible entrypoints from software interrupts or traps – for example, on an ARM CPUthere is only one SWI instruction to enter supervisor mode. Because ofthis, we use a dispatcher in the nanokernel to decode a parameter passedfrom user side, determine the function required and then call it. On ARMCPUs, the parameter is the opcode used with the SWI instruction, andthis determines the function that the dispatcher calls.This calling mechanism results in a very loose coupling between thekernel and user processes, and this means that we can make designchanges within the kernel more easily.5.2.1.1 Flow of execution in an executive callNow I’ll show the flow of execution from a user-mode application tosupervisor-mode kernel code and back again.
Let’s choose an exampleto trace:TUint8* Exec::ChunkBase(ChunkHandle)This executive call returns a pointer to the start of a chunk belongingto the calling thread. The parameter passed is the handle of the chunkwithin the thread.174KERNEL SERVICESUserThread1.EUSER2.userkernelNanokernel3.4.Symbian OS Kernel5.Figure 5.2 Kernel executive callYou can follow my explanation in Figure 5.2.1.User thread: Let’s assume that a user-side thread is executing thefollowing section of code:RChunk newChunk=0;newChunk=OpenGlobal(_L(‘‘SharedChunk’’),ETrue);TUint* base=0;base=newChunk.Base();This code segment opens a shared chunk and stores the handle returnedin newChunk. Next it wants to find out the base address of this chunk,which it does by calling RChunk::Base().
I will trace this operationfrom the user side into the kernel, via an executive call.The code for the RChunk::Base() method is found in the file\e32\euser\us\_exec.cpp and looks like this:EXPORT_C TUint8 *RChunk::Base() const{return(Exec::ChunkBase(iHandle));}SERVICES PROVIDED TO USER THREADS175So RChunk::Base() calls Exec::ChunkBase(), which is in the userlibrary, EUSER.DLL.2. User library: Exec::ChunkBase() is in the file \epoc32\include\exec\_user.h, and is generated by entering ‘‘ABLD MAKEFILE GENEXEC’’ in the E32 directory. The ABLD tool takes the fileexecs.txt, and uses it to generate the source code for the user-sideexecutive calls.
The portion of execs.txt we are interested inis this:slow{name = ChunkBasereturn = TUint8*handle = chunk}You can see that it tells the tools to generate a function named ChunkBasewhich returns a pointer to TUint, and which is passed a handle toa chunk.The generated Exec::ChunkBase() function looks like this:__EXECDECL__ TUint8* Exec::ChunkBase(TInt){SLOW_EXEC1(EExecChunkBase);}In \e32\include\u32exec.h we have:#elif defined(__CPU_ARM)// Executive call macros for AR#define EXECUTIVE_FAST 0x00800000#define EXECUTIVE_SLOW 0x00000000#define __DISPATCH(n)asm("mov ip, lr ");asm("swi %a0" : : "i" (n));#define#define#define#define#define#define#defineFAST_EXEC0(n)FAST_EXEC1(n)SLOW_EXEC0(n)SLOW_EXEC1(n)SLOW_EXEC2(n)SLOW_EXEC3(n)SLOW_EXEC4(n)\\__DISPATCH((n)|EXECUTIVE_FAST)__DISPATCH((n)|EXECUTIVE_FAST)__DISPATCH((n)|EXECUTIVE_SLOW)__DISPATCH((n)|EXECUTIVE_SLOW)__DISPATCH((n)|EXECUTIVE_SLOW)__DISPATCH((n)|EXECUTIVE_SLOW)__DISPATCH((n)|EXECUTIVE_SLOW)When you disentangle the macros, you can see thatExec::ChunkBase() makes this SWI call to enter supervisor mode:SWI EExecChunkBaseEExecChunkBase is an enumeration that gives the opcode for theSWI call.1763.KERNEL SERVICESNanokernel dispatcher: We enter the nanokernel at the function__ArmVectorSwi, in vectors.cia.
This function makes muchuse of the executive tables, which are defined like this:GLREF_D const TUint32 EpocFastExecTable[];GLREF_D const TUint32 EpocSlowExecTable[];Essentially, the fast executive table consists of a number of 32-bit entries,the nth of which is the address of the handler for the nth fast exec call.The slow executive table consists of pairs of 32-bit entries, the first ofwhich is a set of attribute flags, and the second of which is the addressof the slow exec call handler. I will cover this subject in more detail inSection 5.2.1.6.The function __ArmVectorSwi first checks bit 23 of the ARM opcodeto find out whether this is a slow exec call or a fast one. If bit 23 is 1, thenthis is a fast exec call, and the dispatcher will switch interrupts off beforeindexing into the fast exec table, and calling the relevant kernel function.In our case, bit 23 is 0, so ours is a slow exec call.
Next thedispatcher checks bit 31 in the attribute word of the slow exec table.EExecChunkBase has this bit set, so the dispatcher locks the system bytaking the system lock fast mutex.The dispatcher goes on to check another bit in the attribute word tosee if it should call the Symbian OS preprocessing handler, PreprocessHandler, the address of which it discovers from the second wordof the slow exec table. The dispatcher always claims the system lockbefore calling PreprocessHandler.Again, in our case this bit is set, so the dispatcher calls PreprocessHandler. I’ll discuss this in the next section.On returning from PreprocessHandler, the dispatcher finally callsthe relevant OS function: in our case this is ExecHandler::ChunkBase().Finally the dispatcher checks a bit to see whether it should release thesystem lock fast mutex, and after doing so if required, it returns to theuser library.4.Preprocessing handler (optional): The preprocessing handler is partof the Symbian OS kernel (rather than the nanokernel) and is foundin cexec.cia.
It looks up handles to kernel objects.The preprocessing handler has access to the following information:• The arguments passed to the executive function, which includethe handle to look up. The preprocessing handler may modifythese arguments as part of its execution• The attribute flags of the executive call, the bottom five bits ofwhich specify the type of kernel object that the handle refers to.SERVICES PROVIDED TO USER THREADS177On return, the preprocessing handler will have replaced the handle witha pointer to the kernel object to which it refers.There are various special handles that the preprocessing handlermust pay attention to.
Firstly, there are the two handles defined ine32const.h://A flag used by the kernel to indicate the current process.const TInt KCurrentProcessHandle=0xffff0000|KHandleNoClose;//A flag used by the kernel to indicate the current thread.const TInt KCurrentThreadHandle=0xffff0001|KHandleNoClose;Then there are three special handle types:// lookup IPC message handle, allow disconnectEIpcMessageD=0x20,// lookup IPC message handle, don’t allow disconnectEIpcMessage=0x21,// lookup IPC message client, don’t allow disconnectEIpcClient=0x22,Handles like of this type are ‘‘magic’’ values that refer to a client/serverIPC message. In the case of EIpcClient type, this means ‘‘the threadthat sent the message’’. The magic value is in fact the address of the RMessageK object stored within the kernel! Don’t worry – the kernel performsstrict validation checks on this object to prevent security breaches.Returning to our simpler example, the preprocessing handler merelylooks up the handle in the owning thread or process, and returns with apointer to the corresponding DChunk.5.
OS function: The exec handling function that the dispatcher callsmay be almost anywhere in kernel – in the nanokernel, the SymbianOS kernel, the memory model or even the variant.In our example, ExecHandler::ChunkBase() is in the filesexec. cpp, which is part of the Symbian OS kernel. This functionsimply retrieves the base of the chunk from the DChunk, like this:TUint8 *ExecHandler::ChunkBase(DChunk* aChunk)// Return the address of the base of the Chunk.{return (TUint8 *)aChunk->Base();}5.2.1.2 Context of executive callAn exec call executes in the context of the calling user-mode thread, notthat of any kernel thread.
The only changes that happen on entry to thekernel are:178KERNEL SERVICES• The processor switches into supervisor mode• The active stack changes from the current thread’s user stack to thecurrent thread’s supervisor stack.Because of this, you can’t make an exec call from an interrupt serviceroutine or an IDFC, because in these situations there is no thread context.5.2.1.3 Changes from EKA1The exec call mechanism has changed considerably from EKA1 to EKA2.On EKA1, exec calls borrow the kernel server or the null thread stack,rather than running on the calling thread’s own supervisor stack as they doon EKA2. For this, and other reasons, EKA1 exec calls have the followingrestrictions:1.They are not preemptible2.They can’t block in the kernel3.They can’t allocate and free kernel memory.On EKA1, if a user-mode thread needed to call a service that allocatedor freed kernel memory (for example, a service that created or destroyedobjects derived from CObject), then that user-mode thread had to makea special kind of kernel call, known as a kernel server call.
This is nolonger the case in EKA2.As we’ve seen, on EKA2 exec calls run on the supervisor stack of thecalling thread. This means that exec calls can be preempted and they canblock in the kernel. Furthermore, because EKA2 does not link to EUSER,exec calls may allocate and free kernel memory.5.2.1.4 Accessing user-mode memoryEarlier in this chapter, I said that an exec call runs in the context of thecalling thread. This means that on systems with an MMU and multipleprocesses running in separate address spaces, the active address spaceis still that of the process to which the calling thread belongs. It istherefore theoretically possible for the kernel-side exec call to directlyaccess the memory of the user process that called it, by dereferencing apointer or using memcpy().