Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 37
Текст из файла (страница 37)
Inthe next chapter, I will describe how EKA2 provides services to usermode threads.5Kernel Servicesby Jane SalesOn two occasions I have been asked (by members of Parliament!):‘‘Pray, Mr. Babbage, if you put into the machine wrong figures, will theright answers come out?’’ I am not able rightly to apprehend the kind ofconfusion of ideas that could provoke such a question.Charles BabbageEKA2 provides a variety of services to user-mode threads.
In this chapter Iwill explain the mechanism it uses to do so, which we call an ‘‘executivecall’’, and then I will describe a few example services to give you a feelfor them.Of course, the kernel does not just provide services for user-modethreads – each part of the kernel provides services to the other parts ofthe kernel too. I will consider interfaces between modules such as thenanokernel and the memory model, and interfaces between the differentabstraction levels of the kernel, such as the independent layer and theCPU layer.But first of all I will look inside the basic object and handle mechanismused by Symbian OS. This is at the heart of the communication betweenthe user side and the kernel.5.1 Objects and handles5.1.1 Handles – the RHandleBase classUser-side code always references a kernel-side object through an objectknown as a handle.
Handles are objects derived from the base classRHandleBase:class RHandleBase{public:enum162KERNEL SERVICES{EReadAccess=0x1,EWriteAccess=0x2,EDirectReadAccess=0x4,EDirectWriteAccess=0x8,};public:inline RHandleBase();inline TInt Handle() const;inline void SetHandle(TInt aHandle);inline TInt SetReturnedHandle(TInt aHandleOrError);static void DoExtendedClose();IMPORT_C void Close();IMPORT_C TName Name() const;IMPORT_C TFullName FullName() const;IMPORT_C void SetHandleNC(TInt aHandle);IMPORT_C TInt Duplicate(const RThread& aSrc,TOwnerType aType=EOwnerProcess);IMPORT_C void HandleInfo(THandleInfo* anInfo);IMPORT_C TUint Attributes() const;protected:inline RHandleBase(TInt aHandle);IMPORT_C TInt Open(const TFindHandleBase& aHandle,TOwnerType aType);static TInt SetReturnedHandle(TInt aHandleOrError,RHandleBase& aHandle);TInt OpenByName(const TDesC &aName,TOwnerType aOwnerType,TInt aObjectType);private:static void DoExtendedCloseL();protected:TInt iHandle;};Here you can see some of the fundamental methods that we canperform on handles: we can open and close them, retrieve their shortname and their full name, and we can duplicate them.
You can alsosee that RHandleBase’s only member data is a single 32-bit integer,iHandle. To show you how the kernel forms this integer, I will firstneed to explain a container class, DObjectIx, which is known as theobject index. This class is a container for kernel-side objects derived fromDObject, which I will discuss first.5.1.2 Reference-counted kernel objectsA large part of the kernel interface presented to user-side code is concerned with creation and manipulation of kernel objects representedby user-side RHandleBase-derived classes. These kernel objects havesome basic properties in common.5.1.2.1 Reference countedKernel objects are reference counted: multiple references can exist toeach object and the kernel only destroys the object when all referenceshave been removed.OBJECTS AND HANDLES1635.1.2.2 Accessed using handlesUser-side code accesses kernel objects indirectly using handles, ratherthan directly using pointers.
The kernel translates a handle into a pointerby looking it up in a thread or process handle array. The use of handlesallows the kernel to check the validity of kernel object references madeby user code.5.1.2.3 NamedKernel objects may have names that you can use to find the object.Moreover, the name can be scoped relative to another kernel object (theowner). I will expand more on this later.5.1.2.4 The DObject classAs I mentioned earlier, kernel objects are represented using classesderived from the DObject class. This base class provides the necessaryreference counts, object names and name scoping relative to the ownerobject.
DObject is in turn derived from DBase – this class provideskernel-side behavior equivalent to that provided by the user-side classCBase; that is, it zero-fills memory before object construction and provides a virtual destructor. It also offers the ability to trigger asynchronousdeletion of the object, which is important in time-critical code.Here is a slightly cut-down version of the DObject class:class DObject : public DBase{public:enum TCloseReturn{EObjectDeleted=1,EObjectUnmapped=2,};enum TObjectProtection{ELocal=0,EProtected,EGlobal,};public:inline TInt Inc() {return NKern::SafeInc(iAccessCount);}inline TInt Dec() {return NKern::SafeDec(iAccessCount);}IMPORT_C DObject();IMPORT_C ∼DObject();inline TInt Open() { return(Inc()?KErrNone:KErrGeneral); }IMPORT_C void CheckedOpen();IMPORT_C virtual TInt Close(TAny* aPtr);IMPORT_C virtual TInt RequestUserHandle(DThread* aThread,TOwnerType aType);IMPORT_C virtual TInt AddToProcess(DProcess* aProcess);IMPORT_C TInt AsyncClose();164KERNEL SERVICESIMPORT_C virtual void DoAppendName(TDes& aName);IMPORT_C void DoAppendFullName(TDes& aFullName);IMPORT_C void Name(TDes& aName);IMPORT_C void AppendName(TDes& aName);IMPORT_C void FullName(TDes& aFullName);IMPORT_C void AppendFullName(TDes& aFullName);IMPORT_C TInt SetName(const TDesC* aName);IMPORT_C TInt SetOwner(DObject* aOwner);IMPORT_C void TraceAppendName(TDes8& aName, TBool aLock);IMPORT_C void TraceAppendFullName(TDes8& aFullName, TBool aLock);inline DObject* Owner();inline TInt AccessCount();inline TInt UniqueID();inline HBuf* NameBuf();inline void SetProtection(TObjectProtection aProtection);inline TUint Protection();public:TInt iAccessCount;DObject* iOwner;TUint8 iContainerID;TUint8 iProtection;TUint8 iSpare[2];HBuf* iName;public:static NFastMutex Lock;};Key member data of DObjectiAccessCountThis counts how many references exist to the object – it is always nonnegative.iOwnerThis is a reference-counted pointer to the DObject (thread or process)that is the owner of this object.iContainerIDThis is the ID of the DObjectCon that contains this object.
I will discussthis later in this chapter.iNameThis is a pointer to a kernel-heap-allocated descriptor that holds thisobject’s name. It is NULL if the object is unnamed.iProtectionThis is a TObjectProtection value, which notes if the object is privateto the owning thread or process.5.1.2.5 DObjects explainedThe DObject class is new to EKA2. In EKA1 we derived our kernelclasses from the user library’s object class, CObject. In EKA2, we choseOBJECTS AND HANDLES165to create a new, kernel-only, DObject class to break the dependencybetween the kernel and the user library.
In the same way, we createdDObjectIx for the kernel to use instead of CObjectIx.When a user thread requests the creation of an object representedby a handle, the kernel creates a DObject with an access count of 1,representing the pointer returned to the creating thread. If another threadthen wishes to open this object, the kernel calls DObject::Open()on its behalf, incrementing the DObject’s access count. We wantedit to be possible to call this method from anywhere, even in an ISRor DFC, so we prevented it from being over-ridden in a derived class.The result is that DObject::Open() always atomically executes thefollowing operation:if (iAccessCount==0)return KErrGeneral;else{++iAccessCount;return KErrNone;}The access count is incremented, unless it was zero – this is an error,because, as we’ve seen, every DObject is created with an access countof 1.The DObject::Dec() method does the opposite – it atomically executes the following operation:if (iAccessCount==0)return 0;elsereturn iAccessCount--;The Open() and Dec() methods are not protected by fast mutexes;they simply use atomic instructions or disable interrupts for a short time.When a user thread closes a handle, the kernel invokes the DObject::Close(TAny*) method to remove a reference from the object.It calls Dec(), then proceeds to delete the object if the returned value is1, indicating that the last reference has been closed:EXPORT_C TInt DObject::Close(TAny* aPtr){if (Dec()==1){NKern::LockSystem(); // in case it is still in useNKern::UnlockSystem();DBase::Delete(this);return EObjectDeleted;}return 0;}166KERNEL SERVICESSince Close() may cause the freeing of memory on the kernel heap,the rules about when kernel heap operations may be performed apply;this means that we can’t call it from an ISR or a DFC, for example.This contrasts with Open(), which as we’ve seen can be called fromanywhere.
We therefore allow the Close() method to be over-riddenby making it virtual.The kernel deletes a DObject only when its access count becomeszero – in fact, this always happens via the Close() method. It is possiblethat a DObject with a zero access count is in the process of beingdestroyed. This is why Open() must fail if the object’s access countis zero.The parameter aPtr passed to Close() is either NULL or a pointerto the process that is closing a handle on the object. The kernel uses thepointer when the object being closed is a chunk, to remove the chunkfrom the process address space.DObject also provides an AsyncClose() method. This is the sameas Close() except that the parameter is always NULL and the kerneldoes the delete (if one is needed) asynchronously in the supervisor thread.Of course, AsyncClose() will only work if the derived class does notover-ride Close().There are two names associated with a DObject – the name (alsoknown as the short name) and the full name.The short name is either:1.The string pointed to by iName2.If iName=NULL, it is ‘‘Local-XXXXXXXX’’ where XXXXXXXX is thehexadecimal address of the DObject.Object short names can be up to 80 characters in length.
This makes themshorter than in EKA1, where the maximum was 128 characters. There’sanother difference too: EKA1 supported Unicode names, whereas inEKA2, names must be in ASCII. We made this decision for several reasons:• If the kernel were to support Unicode internally, then we would haveto duplicate many Unicode functions and large folding tables insidethe kernel• An ASCII compare is much simpler than a Unicode folded compare,so searching for objects by name is faster• The naming of objects is a programmer convenience, and programmers generally write code in ASCII source files.The object’s full name is longer; it can be anything up to 256 charactersin length.