Port Windows IPC apps to Linux, Part 2: Semaphores and events_semop namedevent-程序员宅基地

技术标签: linux  semaphore  attributes  thread  windows  events  

A mapping guide for complex, multithreaded, multiprocess applications

Srinivasan Muthuswamy ( [email protected]), Software Engineer, IBM Global Services Group
Kavitha Varadarajan ( [email protected]), Software Engineer, IBM India Software Lab

 

Summary:  The wave of migration to open source in business has the potential to cause a tremendous porting traffic jam as developers move the pervasive Windows applications to the Linux platform. In this three-part series, get a mapping guide, complete with examples, to ease your transition from Windows to Linux. Part 2 examines two synchronization object types, semaphores and events.

View more content in this series

Tags for this article:  events, ipc, linux, port, semaphores, win32, windows

 

 

Date:  25 May 2005
Level:  Advanced

Activity:  20931 views
Comments:   0 (View | Add comment - Sign in)

Average rating 4 stars based on 51 votes Average rating (51 votes)
Rate this article

 

Today many global businesses and services are going open source -- all the major corporate players in the industry are pushing for it. This trend has spurred a major migration exercise in which lots of existing products maintained for various platforms (Windows, OS2, Solaris, etc.) are being ported to open source Linux platforms.

Many applications are designed without consideration of the need to port them to Linux. This has the potential to be a porting nightmare, but it doesn't have to be. The goal of this series of articles is to help you migrate complex applications involving IPC and threading primitives from Windows to Linux. We will share our experiences in moving these critical Windows IPC applications, applications that include multithreaded apps that require thread synchronization and multiprocess apps that require interprocess synchronization.

In short, this series can be called a mapping document -- it provides mapping of various Windows calls to Linux calls related to threads, processes, and interprocess communication elements (mutexes, semaphores, etc.). To create easily digestible chunks, we've divided the series into three articles:

  • In Part 1, we dealt with processes and threads.
  • This installment handles semaphores and events.
  • Part 3 will cover mutexes, critical sections, and wait functions.

We'll continue our Windows-to-Linux mapping guide by starting with synchronization.

Synchronization

In Windows, synchronization is achieved by using one of the synchronization objects in one of the wait functions. The synchronization objects take either a signaled and non-signaled state. When the synchronization object is used in one of the wait functions, the wait function blocks the calling thread until the state of the synchronized object is set to signaled.

Following are some of the synchronization objects available on Windows:

  • Events
  • Semaphores
  • Mutexes
  • Critical sections

In Linux, there are different synchronization primitives available. The difference with Linux is that each primitive has its own wait functions (functions for changing the state of the synchronization primitive) -- in Windows there are common wait functions to achieve the same end. The following synchronization primitives are available in Linux:

  • Semaphores
  • Conditional variables
  • Mutexes

Various libraries are available for Linux to provide synchronization using the previously named primitives.

Table 1. Synchronization mapping

Windows Linux -- threads Linux -- process
Mutex Mutex - pthread library System V semaphores
Critical section Mutex - pthread library Not applicable as critical sections are used only between the threads of the same process
Semaphore Conditional Variable with mutex - pthreads
POSIX semaphores
System V Semaphores
Event Conditional Variable with mutex - pthreads System V Semaphores

Semaphores

Windows semaphores are count variables allowing a limited number of threads/processes to access the shared resource. Linux POSIX semaphores are count variables and can be used to achieve the Windows functionality of semaphores on Linux.

The various points that needs to be considered for semaphores during the mapping process are as follows:

  • Type of semaphore: Windows provides named and unnamed semaphores. Named semaphores extend the synchronization between processes. On Linux, POSIX semaphores are used only between the threads of same process. Between processes, System V semaphores can be used.
  • Timeout in wait functions: When used in one of the wait functions, timeout value can be specified for Windows semaphore objects. This is not provided for in Linux -- it needs to be handled in the application logic only.

Table 2. Semaphore mapping

Windows Linux Threads Linux Process Classification
CreateSemaphore sem_init semget
semctl
Context specific
OpenSemaphore Not applicable semget Context specific
WaitForSingleObject sem_wait
sem_trywait
semop Context specific
ReleaseSemaphore sem_post semop Context specific
CloseHandle sem_destroy semctl Context specific

Creating a semaphore

In Windows, CreateSemaphore() is used to create or open a named or unnamed semaphore.

HANDLE CreateSemaphore(
  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
  LONG lInitialCount,
  LONG lMaximumCount,
  LPCTSTR lpName
);

In this code:

  • lpSemaphoreAttributes is a pointer to the security attributes. If this is null, the semaphore cannot be inherited.
  • lInitialCount is the initial count of the semaphore.
  • lMaximumCount is the maximum count of the semaphore and must be greater than zero.
  • lpName is the name of the semaphore. If this is NULL, the semaphore is shared only between the threads of the same process. Otherwise, it can be shared between threads of different process.

This function creates the semaphore and returns the handle to the semaphore. It also sets the initial count to the values specified in the call. Thus it allows limited number of threads to access a shared resource.

In Linux, sem_init() is used to create an unnamed POSIX semaphore which can be used between threads of the same process. This call also initializes the semaphore count: int sem_init(sem_t *sem, int pshared, unsigned int value). In this code:

  • value (semaphore count) is the initial value of the semaphore.
  • pshared can be ignored since the POSIX semaphore is not shared between the processes in the current implementation.

It is good to notice here that maximum count value is based on the SEM_VALUE_MAX defined in the header file semaphore.h.

In Linux, semget() is used to create the System V semaphore which can be used between threads of different process. This is used to achieve the functionality of a Windows named semaphore. This function returns the semaphore set identifier associated with the argument key. When creating a new semaphore set, semget() initializes the semaphore-associated data structure semid_ds as follows:

  • sem_perm.cuid and sem_perm.uid are set to the effective user ID of the calling process.
  • sem_perm.cgid and sem_perm.gid are set to the effective group ID of the calling process.
  • The low-order nine bits of sem_perm.mode are set to the low-order nine bits of semflg.
  • sem_nsems is set to the value of nsems.
  • sem_otime is set to 0.
  • sem_ctime is set to the current time.

This is the code used to create the System V semaphore: int semget(key_t key, int nsems, int semflg). Following are the attributes to this code:

  • The key is a unique identifier that is used by different processes to identify this semaphore set. A unique key can be generated using ftok(). IPC_PRIVATE is a special key_t value; when IPC_PRIVATE is used as key the system call ignores everything but the lowest order nine bits of semflg and creates a new semaphore set (when successful).
  • nsems is the number of semaphores in the semaphore set.
  • semflg are permissions on the new semaphore set. To create a new set, you can set bit-wise or the access permissions with IPC_CREAT. IPC_CREAT/IPC_EXCL flags will fail if the semaphore set with that key is already existing.

Notice that in System V semaphores, key is used to uniquely identify the semaphore; in Windows, the semaphore is identified by a name.

In order to initialize the semaphore-set data structure, use the semctl() system call with the IPC_SET command. Write the values of some members of the semid_ds structure pointed to by arg.buf to the semaphore set data structure, also updating its sem_ctime member. The members from the user-supplied struct semid_ds pointed to by arg.buf are the following:

  • sem_perm.uid
  • sem_perm.gid
  • sem_perm.mode (but only the lowest nine bits)

An effective user ID of the calling process should be that of super-user or at least match the creator or owner of the semaphore set: int semctl(int semid, int semnum, int cmd = IPC_SET, ...). In this code:

  • semid is the semaphore set identifier.
  • semnum is the semaphore subset offset (starts at 0 till nsems -1, where n is the number of subsets in the semaphore set). This argument is ignored.
  • cmd is the command; it uses IPC_SET for setting the semaphore value.
  • args are the values to be updated in the semaphore set data structure through this IPC_SET (explained in the sample).

Maximum count value is based on the SEMVMX defined in the header file.

Opening a semaphore

In Windows, OpenSemaphore() is used to open a named semaphore. This is required only if the semaphore is shared between two processes. Upon a successful opening, this function returns the handle to the semaphore so that it can be used in the subsequent calls.

HANDLE OpenSemaphore(
  DWORD dwDesiredAccess,
  BOOL bInheritHandle,
  LPCTSTR lpName
)

In this code:

  • dwDesiredAccess is the requested access for the semaphore object.
  • bInheritHandle is the flag which controls the inheritance of the semaphore handle. If TRUE, handle can be inherited.
  • lpName is the name of the semaphore.

In Linux, the same semget() call is used to open the semaphore with 0 as the value for the semflg: int semget(key,nsems,0). In this code:

  • key should point to the semaphore set key, which you want to open.
  • nsems and flags can be 0 to open an already existing semaphore. The semflg value is set while creation is verified for the access permissions before returning semaphore set identifier.

Acquiring a semaphore

In Windows, wait functions provide the facility for acquiring the synchronization objects. There are different types of wait functions available -- in this section, we're only considering WaitForSingleObject() (the other types will be discussed separately). This function takes the handle to the semaphore object and waits until it is signaled or a timeout occurs.

DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds );

In this code:

  • hHandle is the pointer to the mutex handle.
  • dwMilliseconds is the timeout value in milliseconds. If the value is INFINITE then it blocks the calling thread/process indefinitely.

In Linux, sem_wait() is used to acquire the semaphore access. This function suspends the calling thread until the semaphore has a non-zero count. It then atomically decreases the semaphore count: int sem_wait(sem_t * sem).

The timeout option is not available in POSIX semaphores. This can be achieved by issuing a non-blocking sem_trywait() within a loop, which counts the timeout value: int sem_trywait(sem_t * sem).

When a System V semaphore is used, semop() has to be used to acquire the semaphore once the initial value is set through semctl() using the IPC_SET command. semop() performs the operations specified in the operation set and blocks the calling thread/process until the semaphore value is zero or more: int semop(int semid, struct sembuf *sops, unsigned nsops).

The function semop() performs the set of operations contained in sops atomically -- that is, the operations are performed at the same time and only if they can all be simultaneously performed. Each of the nsops elements in the array pointed to by sops specifies an operation to be performed on a semaphore by a struct sembuf, including the following members:

  • unsigned short sem_num; (semaphore number)
  • short sem_op; (semaphore operation)
  • short sem_flg; (operation flags)

To acquire the semaphore, semop() is called with sem_op as -1 to acquire the semaphore; after using the semaphore, semop() is called with sem_op as 1 to release the semaphore. By calling semop() with sem_op as -1, the semaphore count is decreased by 1 and if the value falls less than zero (since semaphore values cannot go less than zero), the semaphore count is not decreased but the calling thread/process is blocked until the semaphore is signaled.

Flags recognized in sem_flg are IPC_NOWAIT and SEM_UNDO. If an operation asserts SEM_UNDO, it will be undone when the process exits. If sem_op is set to 0, semop() will wait for semval to become 0. This is a "wait-for-zero" operation and can be used to acquire a semaphore.

Remember that the timeout option is not available in System V semaphore. This can be achieved by issuing non-blocking semop() (by setting sem_flg as IPC_NOWAIT) within a loop, which counts the timeout value.

Releasing a semaphore

In Windows, ReleaseSemaphore() is used release the semaphore.

BOOL ReleaseSemaphore(
  HANDLE hSemaphore,
  LONG lReleaseCount,
  LPLONG lpPreviousCount
);

In this code:

  • hSemaphore is a pointer to the handle of the semaphore.
  • lReleaseCount is the semaphore count incremented by the specified amount.
  • lpPreviousCount is the pointer to the variable where the previous semaphore count is returned. The parameter can be NULL if the previous semaphore count value is not required.

This function increments the semaphore count by the value specified in lReleaseCount and sets the state of the semaphore to signaled state.

In Linux, sem_post() is used to release semaphore. This wakes up any of the threads blocked on the semaphore. The count of the semaphore is incremented only by 1. To increment the semaphore count by a specified number (like in Windows), this function can be called multiple times along with a mutex variable: int sem_post(sem_t * sem).

For System V semaphores, semop() has to be used to release the semaphore: int semop(int semid, struct sembuf *sops, unsigned nsops).

The function semop() performs a set of operations contained in sops atomically (all operations performed at the same time only if they can be performed at the same time). Each of the nsops elements in the array pointed to by sops specifies an operation to be performed on a semaphore by a struct sembuf including the following members:

  • unsigned short sem_num; (semaphore number)
  • short sem_op; (semaphore operation)
  • short sem_flg; (operation flags)

To release the semaphore, semop() is called with sem_op as 1. By calling semop() with sem_op as 1, the semaphore count is incremented by 1 and semaphore is signaled.

Closing/destroying a semaphore

In Windows, CloseHandle() is used to close or destroy the semaphore object.

BOOL CloseHandle(
  HANDLE hObject
);

hObject is the pointer to the handle to the synchronization object.

In Linux, sem_destroy() destroys the semaphore object, freeing the resources it might hold: int sem_destroy(sem_t *sem). For System V semaphores, semctl() with IPC_RMID command has to be used to close the semaphore set: int semctl(int semid, int semnum, int cmd = IPC_RMID, ...).

This command immediately removes the semaphore set and its data structures, awakening all waiting processes (with an error return and errno set to EIDRM). The effective user ID of the calling process must be that of the super-user or match the creator or owner of the semaphore set. The argument semnum is ignored.

Examples

Following are examples for semaphores.


Listing 1. Windows un-named semaphore code
HANDLE hSemaphore;
LONG   lCountMax = 10;
LONG   lPrevCount;
DWORD  dwRetCode;

// Create a semaphore with initial and max. counts of 10.

hSemaphore = CreateSemaphore(
                      NULL,        // no security attributes
                      0,           // initial count
                      lCountMax,   // maximum count
                      NULL);       // unnamed semaphore



// Try to enter the semaphore gate.

dwRetCode = WaitForSingleObject(
                   hSemaphore,  // handle to semaphore
                   2000L);   // zero-second time-out interval

switch (dwRetCode)
{
    // The semaphore object was signaled.
    case WAIT_OBJECT_0:
        // Semaphore is signaled
        // go ahead and continue the work

        goto success:

        break;

    case WAIT_TIMEOUT:
        // Handle the time out case
        break;
}

Success:

// Job done, release the semaphore
ReleaseSemaphore(
        hSemaphore,  // handle to semaphore
        1,           // increase count by one
        NULL)        // not interested in previous count



// Close the semaphore handle
CloseHandle(hSemaphore);


Listing 2. Linux equivalent code using POSIX semaphores
// Main thread
#define TIMEOUT 200  /* 2 secs */

// Thread 1
sem_t sem     ; // Global Variable


int   retCode ;

// Initialize event semaphore
retCode = sem_init(
                   sem,   // handle to the event semaphore
                   0,     // not shared
                   0);    // initially set to non signaled state


while (timeout < TIMEOUT ) {
   delay.tv_sec = 0;
   delay.tv_nsec = 1000000;  /* 1 milli sec */

   // Wait for the event be signaled
   retCode = sem_trywait(
                   &sem); // event semaphore handle
                          // non blocking call
   if (!retCode)  {
        /* Event is signaled */


        break;
   }
   else {
       /* check whether somebody else has the mutex */
       if (retCode == EPERM ) {
            /* sleep for delay time */
            nanosleep(&delay, NULL);
            timeout++ ;
       }
       else{
           /* error  */
       }
   }
}


// Completed the job,
// now destroy the event semaphore
retCode = sem_destroy(
                      &sem);   // Event semaphore handle
// Thread 2
// Condition met
// now signal the event semaphore
sem_post(
         &sem);    // Event semaphore Handle
 


Listing 3. Linux semaphore code using System V semaphores
// Process 1
#define TIMEOUT 200

//Definition of variables
    key_t key;
    int semid;
    int Ret;
    int timeout = 0;
    struct sembuf operation[1] ;

    union semun
    {
        int val;
        struct semid_ds *buf;
        USHORT *array;
    } semctl_arg,ignored_argument;


    key = ftok(); // Generate a unique key, U can also supply a value instead

    semid = semget(key,             // a unique identifier to identify semaphore set
                    1,              // number of semaphore in the semaphore set
                   0666 | IPC_CREAT // permissions (rwxrwxrwx) on the new
                                    //    semaphore set and creation flag
                    );

   //Set Initial value for the resource
    semctl_arg.val = 0; //Setting semval to 0
    semctl(semid, 0, SETVAL, semctl_arg);


    //Wait for Zero

    while(timeout < TIMEOUT)
    {
        delay.tv_sec = 0;
        delay.tv_nsec = 1000000;  /* 1 milli sec */

        //Call Wait for Zero with IPC_NOWAIT option,so it will be non blocking

        operation[0].sem_op = -1; // Wait until the semaphore count becomes 0
        operation[0].sem_num = 0;
        operation[0].sem_flg = IPC_NOWAIT;

        ret = semop(semid, operation,1);

        if(ret < 0)
        {
            /* check whether somebody else has the mutex */
            if (retCode == EPERM )
            {
                /* sleep for delay time */
                nanosleep(&delay, NULL);
                timeout++ ;
            }
            else
            {
                printf("ERROR while wait ");
                break;
            }
        }
        else
        {
            /*semaphore got triggered */
            break;
        }

    }

    //Close semaphore
    iRc = semctl(semid, 1, IPC_RMID , ignored_argument);

}

// Process 2

key_t key = KEY; // Process 2 should know key value in order to open the
                 //    existing semaphore set

    struct sembuf operation[1] ;

    //Open semaphore
    semid = semget(key, 1, 0);

    operation[0].sem_op = 1; // Release the resource so Wait in process 1 will
                             //    be triggered
    operation[0].sem_num = 0;
    operation[0].sem_flg = SEM_UNDO;

    //Release semaphore
    semop(semid, operation,0);
}
 


Events

In Windows, event objects are one of the synchronization objects whose state needs to be explicitly set to signaled using the SetEvent() function. Event objects come in two types:

  • In the manual reset event, the state of the object remains signaled until explicitly reset using the ResetEvent() function.
  • In the auto reset event, the state of the object remains signaled until a single waiting thread is released. When the waiting thread is released, the state is reset to non-signaled state.

Event objects have two states, signaled and non-signaled. The wait function on the event object blocks the calling thread until its state is set to signaled state.

The following points should be considered during migration:

  • Windows provides named and un-named event objects. Named event objects are provided to provide synchronization between the processes, but in Linux, both the pthreads and POSIX provides synchronization between threads. To achieve functionality of named event objects in Linux, System V semaphore or signals can be used.
  • Windows provides two types of event objects -- manual and auto reset. Linux provides only auto-reset event features.
  • In Windows, event objects initial state is set to signaled. In Linux, pthreads does not provide an initial state, but POSIX semaphores provide an initial state.
  • Windows event objects are asynchronous. In Linux, POSIX semaphores and System V semaphores are asynchronous but pthreads conditional variables are not asynchronous.
  • When used in one of the wait functions, Windows event objects timeout value can be specified. In Linux, only pthreads provides a timeout feature in wait functions.

It is also important to note that:

  • POSIX semaphores are count semaphores, but when the count is set to 1 they provide similar functionality to the Windows event object. They don't provide timeout in the wait functions. POSIX semaphores are preferred when the timeout is not the factor in the porting.
  • When used along with the Mutex, pthreads conditional variables provide event-based synchronization between threads, but they are synchronous. Based on the application logic, this can be selected as a choice for implementing the functionality on Linux during porting.

Table 3. Event objects mapping

Windows Linux Threads Linux Process Classification
CreateEvent
OpenEvent
pthread_cond_init
sem_init
semget
semctl
context specific
SetEvent pthread_cond_signal
sem_post
semop context specific
ResetEvent N/A N/A context specific
WaitForSingleObject pthread_cond_wait
pthread_cond_timedwait
sem_wait
sem_trywait
semop context specific
CloseHandle pthread_cond_destroy
sem_destroy
semctl context specific

Creating/opening an event object

In Windows, CreateEvent() is used to create an event object.

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes,
  BOOL bManualReset,
  BOOL bInitialState,
  LPCTSTR lpName
)

In this code:

  • lpEventAttributes is a pointer to the attributes that determines whether the handle can be inherited or not. If this is NULL, the object handle cannot be inherited.
  • bManualReset is a flag and if it is TRUE, a manual-reset event is created and ResetEvent() should be called explicitly to set the state to non-signaled.
  • bInitialState is the initial state of the event object. If true, the initial state is set to signaled.
  • lpName is the pointer to the name of the event object. It is kept NULL for un-named event object.

This function creates a manual-reset or auto-reset event object and also sets the initial state of the object. This function returns the handle to the event object and can be used in subsequent calls to the event object.

OpenEvent() is used to open an existing named event object. This function returns handle to the event object.

HANDLE OpenEvent(
  DWORD dwDesiredAccess,
  BOOL bInheritHandle,
  LPCTSTR lpName
)

In this code:

  • dwDesiredAccess is the requested access for the event object.
  • bInheritHandle is a flag. If true, the handle can be inherited; otherwise, it cannot be inherited.
  • lpName is a pointer to the name of the event object.

In Linux, the call sem_init() creates a POSIX semaphore: int sem_init(sem_t *sem, int pshared, unsigned int value) (in which value (semaphore count) is set to the initial value of the semaphore).

Linux pthreads uses pthread_cond_init() to create a conditional variable: int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr).

Conditional variables of type pthread_cond_t can be initialized statically using the constant PTHREAD_COND_INITIALIZER. They can also be initialized using pthread_condattr_init() which initializes the attributes associated with the conditional variable. The call pthread_condattr_destroy() is used to destroy the attributes:

int pthread_condattr_init(pthread_condattr_t *attr)
int pthread_condattr_destroy(pthread_condattr_t *attr)

Waiting on an event

In Windows, wait functions provide the facility of acquiring the synchronization objects. Different types of wait functions are available (we're only considering WaitForSingleObject() here). This function takes the handle to the mutex object and waits until it is signaled or timeout occurs.

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD dwMilliseconds
);

In this code:

  • hHandle is the pointer to the mutex handle.
  • dwMilliseconds is the timeout value in milliseconds. If the value is INFINITE then it blocks the calling thread/process indefinitely.

Linux POSIX semaphores use sem_wait() to suspend the calling thread until the semaphore has a non-zero count. It then atomically decreases the semaphore count: int sem_wait(sem_t * sem).

The timeout option is not available in the POSIX semaphore. This can be achieved by issuing non-blocking sem_trywait() within a loop which counts the timeout value: int sem_trywait(sem_t * sem).

Linux pthreads uses pthread_cond_wait() to block the calling thread indefinitely: int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex). On the other hand, if the calling thread needs to be blocked for a specific time, then pthread_cond_timedwait() is used to block the thread. If the conditional variable is not posted within the specified time, pthread_cond_timedwait() returns with an error: int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime). Here, the abstime parameter specifies an absolute time (specifically, the time elapsed since 00:00:00 GMT, January 1, 1970.)

Signaling an event object

The function SetEvent() is used to set the state of the event object to signaled state. Setting an already-set event object has no effect .

BOOL SetEvent(
  HANDLE hEvent
)

Linux POSIX semaphores use sem_post() to post an event semaphore. This wakes any of the threads blocked on the semaphore: int sem_post(sem_t * sem).

The call pthread_cond_signal() is used in LinuxThreads to wake a thread waiting on the conditional variable, while pthread_cond_broadcast() is used to wake all the threads that are waiting on the conditional variable.

int pthread_cond_signal(pthread_cond_t *cond)
int pthread_cond_broadcast(pthread_cond_t *cond)

Note that condition functions are not asynchronously signal-safe and should not be called from a signal handler. In particular, calling pthread_cond_signal() or pthread_cond_broadcast() from a signal handler may deadlock the calling thread.

Resetting an event

In Windows, ResetEvent() is used to reset the state of the event object to a non-signaled state.

BOOL ResetEvent(
  HANDLE hEvent
);

In Linux, conditional variable and POSIX semaphores are of the auto-reset type.

Closing/destroying an event object

In Windows, CloseHandle() is used to close or destroy the event object.

BOOL CloseHandle(
  HANDLE hObject
);

In the code, hObject is the pointer to the handle to the synchronization object.

In Linux, sem_destroy()/ pthread_cond_destroy() destroys semaphore objects or conditional variables, freeing the resources each might hold:

int sem_destroy(sem_t *sem)

int pthread_cond_destroy(pthread_cond_t *cond)

Named event object

In Linux, the named event objects functionality between processes can be achieved by using a System V semaphore. System V semaphores are count variables, so to achieve the Windows event-object functionality, the initial count of the semaphore is set to 0 using semctl().

To signal an event, semop() is used with sem_op value as 1. To wait on an event, semop() function is used with sem_op value as -1 thus blocking the calling process until it is signaled.

A semaphore can be owned by setting the initial count of the semaphore to 0 using semctl(). After using the shared resource, the semaphore count can be set to 1 by using semop(). Refer to the section on semaphores in this article for the prototype for each of these System V semaphores.

Examples

Following are examples to help illustrate what we've discussed in this section.


Listing 4. Windows un-named event object code
// Main thread
HANDLE hEvent; // Global Variable

// Thread 1
DWORD  dwRetCode;

// Create Event
hEvent = CreateEvent(
                     NULL,    // no security attributes
                     FALSE,   // Auto reset event
                     FALSE,   // initially set to non signaled state
                     NULL);   // un named event

// Wait for the event be signaled
dwRetCode = WaitForSingleObject(
                                hEvent,    // Mutex handle
                              INFINITE);   // Infinite wait

switch(dwRetCode) {
          case WAIT_OBJECT_O :

                 // Event is signaled
                 // go ahead and proceed the work


         default :
                   // Probe for error
}


// Completed the job,
// now close the event handle
CloseHandle(hEvent);


// Thread 2
// Condition met for the event hEvent
// now set the event
SetEvent(
         hEvent);    // Event Handle


Listing 5. Linux equivalent code using POSIX semaphores
// Main thread
sem_t sem     ; // Global Variable


// Thread 1
int   retCode ;

// Initialize event semaphore
retCode = sem_init(
                   sem,   // handle to the event semaphore
                   0,     // not shared
                   0);    // initially set to non signaled state


// Wait for the event be signaled
retCode = sem_wait(
                   &sem); // event semaphore handle
                          // Indefinite wait

// Event Signaled
// a head and proceed the work




// Completed the job,
// now destroy the event semaphore
retCode = sem_destroy(
                      &sem);   // Event semaphore handle

// Thread 2
// Condition met
// now signal the event semaphore
sem_post(
         &sem);    // Event semaphore Handle


Listing 6. Equivalent code in Linux using conditional variables
// Main thread
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condvar = PTHREAD_COND_INITIALIZER;

// Thread 1
 ...
pthread_mutex_lock(&mutex);

// signal one thread to wake up
pthread_cond_signal(&condvar);
pthread_mutex_unlock(&mutex);

// this signal is lost as no one is waiting

// Thread 1 now tries to take the mutex lock
// to send the signal but gets blocked
     ...
    pthread_mutex_lock(&mutex);


// Thread 1 now gets the lock and can
// signal thread 2 to wake up
      pthread_cond_signal(&condvar);
      pthread_mutex_unlock(&mutex);

// Thread 2
pthread_mutex_lock(&mutex);
pthread_cond_wait(&condvar, &mutex);
pthread_mutex_unlock(&mutex);

// Thread 2 blocks indefinitely
// One way of avoiding losing the signal is as follows
// In Thread 2 - Lock the mutex early to avoid losing signal

pthread_mutex_lock (&mutex);
// Do work
.......

// This work may lead other threads to send signal to thread 2

// Thread 2 waits for indefinitely for the signal to be posted
pthread_cond_wait (&condvar, &Mutex );

// Thread 2 unblocks upon receipt of signal
pthread_mutex_unlock (&mutex);


Listing 7. Windows example for named events
// Process 1
DWORD  dwRetCode;
HANDLE hEvent; // Local variable

// Create Event
hEvent = CreateEvent(
                     NULL,        // no security attributes
                     FALSE,       // Auto reset event
                     FALSE,       // initially set to non signaled state
                     "myEvent");  // un named event

// Wait for the event be signaled
dwRetCode = WaitForSingleObject(
                                hEvent,    // Mutex handle
                              INFINITE);   // Infinite wait

switch(dwRetCode) {
          case WAIT_OBJECT_O :

                 // Event is signaled
                 // go ahead and proceed the work


         default :
                   // Probe for error
}


// Completed the job,
// now close the event handle
CloseHandle(hEvent);


// Process 2
HANDLE hEvent; // Local variable

// Open the Event
hEvent = CreateEvent(
                     NULL,        // no security attributes
                     FALSE,       // do not inherit handle
                     "myEvent");  // un named event

// Condition met for the event hEvent
// now set the event
SetEvent(
         hEvent);    // Event Handle

// completed the job, now close the event handle
CloseHandle(hEvent);


Listing 7. Windows example for named events
// Process 1
DWORD  dwRetCode;
HANDLE hEvent; // Local variable

// Create Event
hEvent = CreateEvent(
                     NULL,        // no security attributes
                     FALSE,       // Auto reset event
                     FALSE,       // initially set to non signaled state
                     "myEvent");  // un named event

// Wait for the event be signaled
dwRetCode = WaitForSingleObject(
                                hEvent,    // Mutex handle
                              INFINITE);   // Infinite wait

switch(dwRetCode) {
          case WAIT_OBJECT_O :

                 // Event is signaled
                 // go ahead and proceed the work


         default :
                   // Probe for error
}


// Completed the job,
// now close the event handle
CloseHandle(hEvent);


// Process 2
HANDLE hEvent; // Local variable

// Open the Event
hEvent = CreateEvent(
                     NULL,        // no security attributes
                     FALSE,       // do not inherit handle
                     "myEvent");  // un named event

// Condition met for the event hEvent
// now set the event
SetEvent(
         hEvent);    // Event Handle

// completed the job, now close the event handle
CloseHandle(hEvent);


Listing 8. Linux equivalent code using System V semaphores
// Process 1
int main()
{
    //Definition of variables
    key_t key;
    int semid;
    int Ret;
    int timeout = 0;
    struct sembuf operation[1] ;

    union semun
    {
        int val;
        struct semid_ds *buf;
        USHORT *array;
    } semctl_arg,ignored_argument;


    key = ftok(); /Generate a unique key, U can also supply a value instead

    semid = semget(key,                // a unique identifier to identify semaphore set
                     1,                // number of semaphore in the semaphore set
                     0666 | IPC_CREAT  // permissions (rwxrwxrwx) on the new
                                       //     semaphore set and creation flag
                    );
    if(semid < 0)
    {
        printf("Create semaphore set failed ");
        Exit(1);
    }

    //Set Initial value for the resource - initially not owned
    semctl_arg.val = 0; //Setting semval to 0
    semctl(semid, 0, SETVAL, semctl_arg);


    // wait on the semaphore
    // blocked until it is signaled

    operation[0].sem_op = -1;
    operation[0].sem_num = 0;
    operation[0].sem_flg = IPC_WAIT;

    ret = semop(semid, operation,1);

    // access the shared resource
    ...
    ...



    //Close semaphore
    iRc = semctl(semid, 1, IPC_RMID , ignored_argument);

}

// Process 2
int main()
{
    key_t key = KEY; //Process 2 shd know key value in order to open the
                // existing semaphore set
    struct sembuf operation[1] ;

    //Open semaphore
    semid = semget(key, 1, 0);

    // signal the semaphore by incrementing the semaphore count
    operation[0].sem_op = 1;
    operation[0].sem_num = 0;
    operation[0].sem_flg = SEM_UNDO;
    semop(semid, operation,0);

}
 


Next in the series

This second part of the series has introduced synchronization objects and primitives, starting with semaphores and events. Part 3 covers mutexes, critical sections, and wait functions.


Resources

About the authors

Srinivasan S. Muthuswamy

Srinivasan S. Muthuswamy works as a Software Engineer for IBM Global Services Group. He joined IBM in 2000 and his expertise in programming reaches from scripting languages to object- and procedure-oriented languages on multiple platforms (Linux, Windows, WebSphere, Lotus, and so on). Muthuswamy has developed solutions ranging from system programming on Linux and Windows to Web solutions for J2EE. His primary focus is on integration and porting and he holds a B.Eng. in Computer Engineering from the Government College of Technology, Coimbatore, India. You can contact him at [email protected].

Author photo

Kavitha Varadarajan has worked as a software Engineer in the IBM India Software Lab from December 2000. Her work experience involves development and support of host-access client products such as PCOMM and networking software such as the communication server. Varadarajan has a hands-on experience with a migration project that involves porting object-oriented IPC Windows applications to Linux. She holds a B.Eng. in Computer Science and Engineering from Shanmugha College of Engineering, Tanjore, India. She can be contacted at [email protected].

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/maikforever/article/details/6233244

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan