Wiley.Symbian.OS.Internals.Real.time.Kernel.Programming.Dec.2005.eBook-DDU (779891), страница 33
Текст из файла (страница 33)
Thisallows you to prevent a handle to a spoof server being passed by thesemechanisms, as you may verify the identity of the server whose sessionyou are accepting a handle to. The initial session type and security policyparameters are then marshalled into the call to DSession:: Add(),where they are used to fail session creation if the server does not meetthe required policy.The final responsibility of the session is to find – and allocate ifnecessary – kernel memory for messages to be stored in.
I will discussthese message pools in the next section. At session creation time, you canspecify whether the session uses a pool specific to the session or a globalkernel pool. The session stores the type of pool it is using in iPool. If it isusing a per-session pool, then it maintains a pointer to the next availablefree message in iNextFreeMessage.140INTER-THREAD COMMUNICATIONDuring a send, the session will then use one of the session’s disconnectmessage, the thread’s synchronous message or the next free message fromthe selected pool.
If the session is using the global pool and there are nomore free messages the system lock is relinquished (to avoid holding itfor an unbounded period of time whilst allocating), the message pool isexpanded, then the lock is reclaimed and sending proceeds as before.4.1.5.3 Messages – minimal states and message pool designNext, I’ll consider the design of message pools, that is, the memory usedto store messages within the kernel.
There is one important constrainton the design of the message pools, namely that there must always bea free message available for a session to send a disconnect message tothe server, so that resources may be correctly freed in OOM situations.This disconnect message is naturally associated with the session whosedisconnection it is notifying and will always be available if the message isembedded within the session object itself. To minimize the memory usedby this disconnect message object, we have designed the message objectto have a base class, RMessageKBase, which contains only the datarequired for the disconnect message, and then derive from it the (larger)message class, RMessageK, which is used for normal messages:class RMessageKBase : public SDblQueLink{public:TBool IsFree() const { return !iNext; }TBool IsDelivered() const{ return iNext!=0 && (TLinAddr(iNext) & 3)==0; }TBool IsAccepted() const{ return ((TLinAddr)iNext & 3)==3; }public:TInt iFunction;};class RMessageK : public RMessageKBase{public:enum TMsgType {EDisc=0, ESync=1, ESession=2, EGlobal=3};inline TInt ArgType(TInt aParam) const;inline TInt Arg(TInt aParam) const;void Free();static RMessageK* NewMsgBlock(TInt aCount, TInt aType);IMPORT_C DThread* Thread() const;static RMessageK* MessageK(TInt aHandle, DThread* aThread);IMPORT_C static RMessageK* MessageK(TInt aHandle);public:TInt iArgs[4];TUint16 iArgFlags; // describes which arguments are descriptors/handlesTUint8 iPool; // 0=disconnect msg, 1=thread sync message,// 2=from session pool, 3=from global poolCLIENT-SERVER ITC141TUint8 iPad;DSession* iSession; // pointer to sessionSDblQueLink iSessionLink; // attaches message to sessionDThread* iClient; // pointer to client thread (not reference counted)TRequestStatus* iStatus; // pointer to user side TRequestStatus};After we have ensured that session cleanup works properly, the next mostimportant concern in designing the message allocation strategy is to minimize the memory that the kernel uses for sending messages.
In the originalclient-server implementation of Symbian OS v5, a fixed number of message objects were allocated for each session, resulting in poor messageobject utilization, considering that most IPC calls were synchronous andhence only one of the message objects was in use at any one time!Analysis of the client-server system, by instrumenting EUSER andEKERN, has shown that as many as 99% of the calls to RSessionBase::SendReceive() are to the synchronous overload.
Obviously,a thread cannot send a synchronous message to more than one server ata time, because it waits for the synchronous message’s completion insideEUSER immediately after dispatching it. This means that we can use aper-thread message object to avoid having to allocate message objects forall the synchronous messages that are sent. This message object (RMessageK) is embedded within the DThread object, and therefore avoidsallocation issues by being allocated as part of the thread object itself.Thus all that remains to be determined is the allocation strategy formessage objects used by asynchronous IPC (such messages are typicallyused for remote I/O such as sockets or for event notification). Thesemessage objects are allocated on a per-session basis, or dynamically froma global pool. As we only need a small number of them, the overhead fornon-utilization of these message objects is not large.You may wonder why we do not insist on a global pool, and cut memory requirements further.
This is because for real-time code a guaranteedresponse time is important, and a global, dynamic pool does not providethose guarantees (as it may require memory allocation in the kernel). Thismeans that we must provide the option of creating a per-session pool,which allows the real-time code to manage the time it takes to processan asynchronous request precisely. That said, it is more common forservers to use asynchronous message completion for event notification,in which case using the dynamic global pool becomes more attractivedue to its smaller memory footprint.
This is therefore the recommendedoption where real-time guarantees are not required for any asynchronousIPC calls to the server.This allocation scheme allows any number of threads to invoke synchronous IPC on a server using the same session without having toincrease the session’s message pool and it also provides guaranteedmessage sending for synchronous IPC.142INTER-THREAD COMMUNICATIONWe have achieved further memory savings by minimizing the statethat is required within the message objects themselves. There are onlythree states that a message can have, as shown in Figure 4.5.FREEDELIVEREDACCEPTEDThe message is not currently in useThe message has been sent but the server hasn’tseen it yetThe server has received the message but not yetcompleted it.To assure a message’s cleanup when a session closes, we attach itto a session queue whilst it is not free.
We also have to ensure nomessage can persist beyond the lifetime of the thread that sent it, assuch a message can no longer be completed. By assuming a strategy thatmessages should never be discarded prematurely (for example, when thethread Exit() s), but only at the last possible moment (just before thethread object is destroyed), we can avoid the complication of maintainingan open reference on the thread and the associated state required forthis.
When a thread exits, therefore, we need not iterate through a queueto discard DELIVERED messages – they are simply allowed to propagatethrough the server as usual. Instead of an open reference on the threadin each message, we need only maintain a count of the outstandingmessages for each thread (in DThread::iIpcCount).When a thread exits it checks this count and if it is non-zero itincrements its own reference count so it is not destroyed and sets aflag (the top bit of the message count) to indicate this has been done.Completing a message decrements the outstanding message count forthe client thread and if its value reaches 0x80000000, this means theFREEDServer::Deliver()DSession::Detach()ExecHandler::MessageComplete()DSession::Detach()DELIVEREDDServer::Accept()Figure 4.5ACCEPTEDMessage object state machineCLIENT-SERVER ITC143message count for the thread has reached zero and the flag has beenset.
The extra reference on the thread is then closed, allowing it to besafely destroyed.Closing a session does not unilaterally discard DELIVERED messageseither – again they are simply allowed to propagate through the server.This means that session close needs only to send the disconnect messageto the server and has no need to iterate its queue of messages.So, we only need to perform message queue iteration either whenthe server itself terminates or when the server completes a disconnectmessage.
In the latter case, the iteration is needed to free any remainingACCEPTED messages (no DELIVERED messages can remain since thedisconnect message is guaranteed to be the last message received fromthat session). The messages concerned are not on any other queue sothere is no contention when we iterate over the session queue.
Whenthe server itself terminates, the complete system of server and sessionsis frozen, because clients are not allowed to send a message to theserver whilst it is terminating, so again there is no contention on thesession queue when a message is being completed and hence no needfor an intermediate COMPLETED state to deal with delayed removal ofmessages from the session queue.The three states are then encoded in a minimal way into the doublylinked list fields:iLink.iNextiLink.iPrevFREENULLN/ADELIVEREDValid address (multiple of 4)[bottom bits == 00b]N/AACCEPTED∼(this)[bottom bits == 11b]∼&(serverDProcess)4.1.5.4 Message handlesAnother key design decision we took for message objects was not toderive them from DObject.
This means that they do not have standardobject containers and user handles. Rather, the user-side handle for anRMessageK is in fact its address on the kernel heap. To verify the handlesthat user-mode operations give, the kernel uses the fact that a user-sidecomponent only ever has a valid handle to a message when it is in theACCEPTED state and does the following:• Checks the address and size of the object are within the kernel heap144INTER-THREAD COMMUNICATION• If so, reads the memory under an exception trap (the machine codedversions of these functions use the magic exception immunity mechanism, see Section 5.4.3.1)• Check that msg.iNext == ∼(& msg)• Check that msg.iPrev == ∼(requestingThread->iOwning Process).If these tests pass, then the message object is taken to be valid and therequested operation can proceed.4.1.6 Changes for IPCv2The main change from the IPCv1 implementation of client-server hasbeen the removal of the insecure APIs.
These were:classclassclassclassclassclassCSharableSessionCSessionCServerRMessagePtrRMessageRServerRSessionBase::Share(TAttachMode aAttachMode=EExplicitAttach)RSessionBase::Attach()RSessionBase::Send(TInt aFunction,TAny* aPtr)RSessionBase::SendReceive(TInt aFunction,TAny* aPtr,TRequestStatus& aStatus)RSessionBase::SendReceive(TInt aFunction,TAny* aPtr)RSubSessionBase::CreateSubSession(RSessionBase&,TInt aFunction,const TAny* aPtr)RSubSessionBase::Send(TInt aFunction,const TAny* aPtr)RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr,TRequestStatus&)RSubSessionBase::SendReceive(TInt aFunction,const TAny* aPtr)RThread::GetDesLength(const TAny* aPtr)RThread::GetDesMaxLength(const TAny* aPtr)RThread::ReadL(const TAny* aPtr,TDes8& aDes,TInt anOffset)RThread::ReadL(const TAny* aPtr,TDes16 &aDes,TInt anOffset)RThread::WriteL(const TAny* aPtr,const TDesC8& aDes,TInt anOffset)RThread::WriteL(const TAny* aPtr,const TDesC16& aDes,TInt anOffset)RThread::RequestComplete(TRequestStatus*& aStatus,TInt aReason)RThread::Kill(TInt aReason)RThread::Terminate(TInt aReason)RThread::Panic(const TDesC& aCategory,TInt aReason)We have replaced these APIs with the framework I have describedabove.