Programmers Reference - Kernel execution structure
Kernel execution structure
This document describes the internal execution sequence of
the ConSys System Kernel.
The
ConSys System kernel loading sequence:
The first thing to happen when the CConSysKernel class is constructed, is that a instance of the CConSysEnviroment class is constructed. This class loads all the necessary information from the registry. The next thing to happen is to access the first entry in the device list. This entry specifies the CKernelDevice, or a sub class of this class. This class is the created dynamically (se CLibrary). If this succeeds, the loading sequence is now passed on to the Create method of the CKernelDevice class. The Create method now initializes a array of devices. The first entry in this list is the kernel device. The rest of the entries is loaded dynamically using the information in the registry. If all devices loads correct, the kernel device now starts a worker thread (see CMainThread). The Create method now returns. The main thread loads a instance of the transport layer (see CTransport). The information on what class to load is again obtained in the registry. When the transport layer is loaded the main thread wait for a client to connect, and the kernel is fully loaded.
The
ConSys System kernel connection sequence:
When a client connects to the ConSys System kernel the main thread (see CMainThread) returns from the its call to WaitForClient. The main thread now starts a new thread (CConnectThread) that handles the client connection. The main thread now loops back, and starts a new instance of the transport layer class (see CTransport), and wait for a new client to connect.
The CConnectThread reads the data request (see CDataRequest) from the transport layer. When the request is obtained from the transport layer, the connection thread impersonates the client (see ImpersonateClient) to obtain SID (Security ID). Then the thread returns to the security context of the ConSys System kernel (see RevertToSelf). At this point the thread find the priority group of the client by using the group information in the SID, and correlating this with the group names in the registry. The thread now changes priority (se Data server priorities) to the write priority of the client. Depending on the nature of the address (see CAddress) of the request two thing may happen. If the address is for a remote machine the connect thread starts up a instance CRemoteThread for handling the request. If the address of the request is for the local machine, the connect thread starts up a instance of the CLocalThread. Then the connect thread terminates.
The CRemoteThread routes the request to the remote machine.
The CLocalThread gets the library name for the data server from the request.Then the thread loads the library (see CLibrary). If the library is loaded successfully the thread calls GetDataServer to obtain an id for the data server class. The local thread now creates the data server dynamically. If the load succeeds the thread calls the Create function to initialize the data server. When this is done, the thread creates a helper thread at the read priority of the client. The thread now enters a loop, where it calls the transport layer (see operator >>), when the clients writes data the thread then writes the data to the NewData method of the data server.
This thread is responsible for reading data to the client. The thread waits for the forceDataRead event to be signaled. When this happens, or when the wait times out, the thread calls the Data method for data. It then send the data to the client.
The
thread execution structure:
It is important that the system is very responsive, and yields the correct priorities. To obtain this, the system has a large number of threads servicing different parts of the system. When a device signals a data change, a special thread is released. This thread is responsible for signaling the data servers forceDataRead event. Because the data servers has a dead time (the minPollTime), the 'signal data servers' thread may take some time to signal all the data servers. This is why this is done in a separate thread, as it potentially may take as long time as the maximum minPollTime.
The 'signal data servers' thread has a list of all the data server on the parameter/parameters. Each data server has a last update time associated. When the newData event is signaled, the thread runs through the list. For each data server the thread compares the time difference between the last update and the current time. If the time is larger than the minPollTime for the data server the forceDataRead event is marked for signaling, and a 'polling' flag is set to false. The thread has a internal variable where the remaining time to a update of a data server is stored. If the time difference between last update and the current time is less than minPollTime the difference is compared with the internal variable, and the smallest of the two values is stored in the internal variable, and the polling flag is set to true. This procedure is repeated for every data server in the list. When the end of the list is reached, the 'signal data servers' thread waits for the newData event with a time-out value set by the internal variable. If there is a time out of the wait, the 'signal data servers' thread has to release at least one more forceDataRead. It runs through the list and marks the ones for release, and the polling flag is set to false. If the newData is signaled, the procedure is repeated. Below is a listing of the central code in the 'signal data servers' threads Run method:
ASSERT( m_timeToNextUpdate == INFINITE ); while( !(BOOL)m_pParent->m_deviceClosing ){ // The device is still running // Wait for the data to change, time to do a update m_pParent->m_newData.WaitForTrue(m_pParent->m_deviceClosing, m_timeToNextUpdate); if( !(BOOL)m_pParent->m_deviceClosing ){ // Update the data servers m_timeToNextUpdate = m_pParent-> m_dataServers.SignalDataServers((BOOL)(m_pParent->m_newData)); m_pParent->m_newData = FALSE; }; };
and the SignalDataServers method is implemented as shown blow.
inline UINT CDataServerList::SignalDataServers(BOOLEAN newData /*= FALSE*/) { DWORD time = GetTickCount(); m_elapsed = time - m_lastUpdate; m_lastUpdate = time; m_minTime = INFINITE; // Set the initial time for next update; m_newData = newData; // Now run throug the list ENTER_AND_TRY( EnumerateElements(this); ); // return the mintime return m_minTime; } // Define the enumeratiron method void ForEachElement(void* ref, CDataServerEntry &element) { ASSERT_VALID( (CObject*)ref ); ASSERT( ((CObject*)ref)->IsKindOf(RUNTIME_CLASS(CDataServerList)) ); CDataServerList* pList = (CDataServerList*)ref; ASSERT_VALID(pList); if( element.m_scheduled || pList->m_newData){ // Test if the data server must be signaled if( pList->m_elapsed > element.m_remainingTime ){ // Signal the data server ASSERT_VALID(element.m_pDataServer); ASSERT_VALID(element.m_pDataServer->m_pForceDataRead); element.m_pDataServer->m_signaled = TRUE; VERIFY( ::SetEvent(*(element.m_pDataServer->m_pForceDataRead)) ); element.m_scheduled = FALSE; element.m_remainingTime = element.m_minPollTime; } else { // Do a countdown of the remaing time element.m_remainingTime = element.m_remainingTime - pList->m_elapsed; element.m_scheduled = TRUE; // Set the min time pList->m_minTime = min(pList->m_minTime, element.m_remainingTime); }; }; }
Last Modified 11 January 2019