Kernel Object, Handles, and Synchronization - 2020
Bookmark and Share
ms_icon.png

Waht is Handle?

Handle value is actually the index into the process's handle table that identifies where the kernel object's information is stored.



Duplicate Handle?

As shown in the picture below, an application creates an event object. The CreateEvent function creates the event object and returns an object handle.

DuplicateHandleA.png

After the event object has been created, the application can use the event handle to set or wait on the event. The handle remains valid until the application closes the handle or terminates.

Most kernel objects support multiple handles to a single object. For example, the application in the preceding illustration could obtain additional event object handles by using the OpenEvent function, as shown in the following illustration.



DuplicateHandleB.png

This method enables an application to have handles with different access rights. For example, Handle 1 might have set and wait access to the event, and Handle 2 might have only wait access.

If another process knows the event name and has security access to the object, it can create its own event object handle by using OpenEvent. The creating application could also duplicate one of its handles into the same process or into another process by using the DuplicateHandle function.



Kernel Objects?

Each kernel object is simply a memory block allocated by the kernel and is accessible only by the kernel. This memory block is a data structure whose members maintain information about the object. Some members (security descriptor, usage count, and so on) are the same across all object types, but most are specific to a particular object type.



Kernel object Creator function Destroyer function
Event CreateEvent, CreateEventEx, OpenEvent CloseHandle
File CreateFile CloseHandle, DeleteFile
I/O completion port CreateIoCompletionPort CloseHandle
Mutex CreateMutex, CreateMutexEx, OpenMutex CloseHandle
Process CreateProcess, OpenProcess, GetCurrentProcess CloseHandle, TerminateProcess
Semaphore CreateSemaphore, CreateSemaphoreEx, OpenSemaphore CloseHandle
Socket socket, accept closesocket
Timer CreateWaitableTimer, CreateWaitableTimerEx, OpenWaitableTimer CloseHandle

Complete kernel objects list, here.





How do we manipulate Kernel Objects?

If we cannot alter these Kernel Object structures directly, how do our applications manipulate these kernel objects?

Windows offers a set of functions that manipulate these structures. These kernel objects are always accessible via these functions. When we call a function that creates a kernel object, the function returns a handle that identifies the object.

In other words, whenever we call a function that accepts a kernel object handle as an argument, we pass the value returned by one of the Create* functions. Internally, the function looks in our process's handle table to get the address of the kernel object you want to manipulate and then manipulates the object's data structure.



Which is the data member common to all the kernel object?

The usage count is one of the data members common to all kernel object types.



What happens when the CloseHandle is called?

This function first checks the calling process's handle table to ensure that the index (handle) passed to it identifies an object that the process does in fact have access to. If the index is valid, the system gets the address of the kernel object's data structure and decrements the usage count member in the structure. If the usage count is zero, the kernel destroys the kernel object from memory.



How we can identify the difference between the kernel object and user object?

The easiest way to determine whether an object is a kernel object is to examine the creator function. Almost all functions that create kernel objects have a parameter that allows you to specify security attribute information.




Unnamed Objects?

When we're creating the kernel objects with the help of API's like CreateMutex(, , , ,pzname). When the pzname parameter is NULL , we're indicating to the system that you want to create an unnamed (anonymous) kernel object. When we create an unnamed object, we can share the object across processes by using either inheritance or DuplicateHandle.



Named Objects?

Method available for sharing kernel objects across process boundaries is to name the objects. Below are the kernel named objects:

  1. Events
  2. file mapping
  3. job object
  4. mutex
  5. semaphore
  6. waitableTimers
There are APIs to create these objects with last parameter as the object name.




What is Synchronization Objects?

Synchronization object s are use to co-ordinate the execution of multiple threads. Which kernel objects are use for Thread Synchronization on different processes?

  • Event - Event is the thread synchronization object to set signaled state or non-signaled state.
  • Mutex
  • Semaphore


For more threading topics, please visit
http://www.bogotobogo.com/cplusplus/multithreaded.php.



What is signaled and non signaled state?

An event is in signaled state means that it has the capacity to release the threads waiting for this event to be signaled. An event is in non signaled state means that it will not release any thread that is waiting for this particular event.

Applications can use event objects in a number of situations to notify a waiting thread of the occurrence of an event.

As an example, think about the event objects to prevent several threads from reading from a shared memory buffer while a master thread is writing to that buffer. First, the master thread uses the CreateEvent function to create a manual-reset event object whose initial state is non-signaled. Then it creates several reader threads. The master thread performs a write operation and then sets the event object to the signaled state when it has finished writing.

Before starting a read operation,
each reader thread uses WaitForSingleObject to wait for the manual-reset event object to be signaled.
When WaitForSingleObject returns, this indicates that the main thread is ready for it to begin its read operation.

A mutex object is a synchronization object whose state is set to signaled when it is not owned by any thread, and non-signaled when it is owned.

For example, to prevent two threads from writing to shared memory at the same time, each thread waits for ownership of a mutex object before executing the code that accesses the memory. After writing to the shared memory, the thread releases the mutex object.

Signaled/Non-signaled could be confusing, but to make it easy to remember, I interpret that this way:
When a writing thread finished writing, it sends a signal to a reading thread via WaitForSingleObject to notify the release of its mutex. So, the mutex becomes not owned (so available) and it is now in signaled state.



APIs for creating event and set and reset the events?
  • CreateEvent- to create the event
  • OpenEvent - to open already created event
  • SetEvent - to set the event signaled state
  • RestEvent - To set the Event To non-Signaled State


How can other threads own the mutex or semaphore?
  • Mutex

    Threads in other processes can open a handle to an existing named mutex object by specifying its name in a call to the OpenMutex - function. Any thread with a handle to a mutex object can use one of the wait functions to request ownership of the mutex object. If the mutex object is owned by another thread, the wait function blocks the requesting thread until the owning thread releases the mutex object using theReleaseMutex - function.

  • Semaphore

    A semaphore object is a synchronization object that maintains a count between zero and a specified maximum value. The count is decremented each time a thread completes a wait for the semaphore object and incremented each time a thread releases the semaphore.

    When the count reaches zero, no more threads can successfully wait for the semaphore object state to become signaled. The state of a semaphore is set to signaled when its count is greater than zero, and non-signaled when its count is zero.

    The semaphore object is useful in controlling a shared resource that can support a limited number of users. It acts as a gate that limits the number of threads sharing the resource to a specified maximum number.

    For example, an application might place a limit on the number of windows that it creates. It uses a semaphore with a maximum count equal to the window limit, decrementing the count whenever a window is created and incrementing it whenever a window is closed. The application specifies the semaphore object in call to one of the wait functions before each window is created. When the count is zero - indicating that the window limit has been reached - the wait function blocks execution of the window-creation code.