Quick Overview of GNULinux Semaphores


Quick Overview of GNU/Linux Semaphores

Let s start our discussion with a whirlwind tour of the GNU/Linux semaphore API. We ll look at code examples illustrating each of the API capabilities such as creating a new semaphore, finding a semaphore, acquiring a semaphore, releasing a semaphore, configuring a semaphore, and finally, removing a semaphore. Once we ve finished the quick overview, we ll dig deeper into the semaphore API.

Note  

Semaphores in GNU/Linux are actually semaphore arrays . A single semaphore can represent an array of 64 semaphores. This unique feature of GNU/Linux permits atomic operations over numerous semaphores at the same time. In the early discussions of GNU/Linux semaphores, we ll explore single semaphore uses. In the detailed discussions that follow, we ll look at the more complex semaphore array examples.

Using the semaphore API requires that the function prototypes and symbols be available to the application. This is done by including the following three header files:

 #include <sys/types.h>     #include <sys/ipc.h>     #include <sys/sem.h> 

Creating a Semaphore

To create a semaphore (or get an existing semaphore), we use the semget API function. This function takes a semaphore key, a semaphore count, and a set of flags. The count represents the number of semaphores in the set. In this case, we ll specify the need for one semaphore. The semaphore flags, argument 3 as shown in Listing 16.1, specify that the semaphore is to be created ( IPC_CREAT ). We also specify the read/write permissions to use (in this case 0666 for read/write for the user , group , and system in octal). An important item to consider is that when a semaphore is created, its value is zero. This suits us for this example, but we ll investigate later how to initialize the semaphore s value.

Listing 16.1 demonstrates creating a semaphore. In the following examples, we ll use the key MY_SEM_ID to represent our globally unique semaphore. At line 10, we use the semget with our semaphore key, semaphore set count, and command (with read/write permissions).

Listing 16.1: Creating a Semaphore with semget (on the CD-ROM at ./source/ch16/_semcreate.c )
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include "common.h"  4:   5:  int main()  6:  {  7:  int semid;  8:   9:  /* Create the semaphore with the id MY_SEM_ID */  10:  semid =  semget  ( MY_SEM_ID, 1, 0666  IPC_CREAT );  11:   12:  if (semid >= 0) {  13:   14:  printf( "semcreate: Created a semaphore %d\n", semid );  15:   16:  }  17:   18:  return 0;  19:  } 
end example
 

Upon completion of this simple application, a new globally available semaphore would be available with a key identified by MY_SEM_ID . Any process in the system could use this semaphore.

Getting and Releasing a Semaphore

Now let s look at an application that attempts to acquire an existing semaphore and also another application that releases it. Recall that our previously created semaphore (in Listing 16.1) was initialized with a value of zero. This is identical to a binary semaphore already having been acquired .

Listing 16.2 illustrates an application acquiring our semaphore. The GNU/Linux semaphore API is a little more complicated than many semaphore APIs, but it is POSIX compliant and therefore important for porting to other UNIX-like operating systems.

Listing 16.2: Getting a Semaphore with semop
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include <stdlib.h>  4:  #include "common.h"  5:   6:  int main()  7:  {  8:  int semid;  9:  struct sembuf sb;  10:   11:  /* Get the semaphore with the id MY_SEM_ID */  12:  semid =  semget  ( MY_SEM_ID, 1, 0 );  13:   14:  if (semid >= 0) {  15:   16:  sb.sem_num = 0;  17:  sb.sem_op = -1;  18:  sb.sem_flg = 0;  19:   20:  printf( "semacq: Attempting to acquire semaphore %d\n", semid );  21:   22:  /* Acquire the semaphore */  23:  if (  semop  ( semid, &sb, 1 ) == -1 ) {  24:   25:  printf( "semacq: semop failed.\n" );  26:  exit(-1);  27:   28:  }  29:   30:  printf( "semacq: Semaphore acquired %d\n", semid );  31:   32:  }  33:   34:  return 0;  35:  } 
end example
 

We begin by identifying the semaphore identifier with semget at line 12. If this is successful, we build our semaphore operations structure (identified by the sembuf structure). This structure contains the semaphore number, the operation to be applied to the semaphore, and a set of operation flags. Since we have only one semaphore, we use the semaphore number zero to identify it. To acquire the semaphore, we specify an operation of -1. This subtracts one from the semaphore, but only if it s greater than zero to begin with. If the semaphore is already zero, the operation (and the process) will block until the semaphore value is incremented.

With our sembuf created (variable sb ), we use this with the API function semop to acquire the semaphore. We specify our semaphore identifier, our sembuf structure, and then the number of sembufs that were passed in (in this case, one). This implies that we can provide an array of sembuf s, which we ll investigate later. As long as the semaphore operation can finish (semaphore value is nonzero), then it returns with success (a non “1 value). This means that the process performing the semop has acquired the semaphore.

Let s now look at a release example. In this example, we ll demonstrate the semop API function from the perspective of releasing the semaphore.

Note  

In many cases, the release would follow the acquire in the same process. This usage allows synchronization between two processes. The first process attempts to acquire the semaphore and then blocks when it s not available. The second process, knowing that another process is sitting blocked on the semaphore, releases it, allowing the process to continue. This provides a lock-step operation between the processes and is practical and useful.

Listing 16.3: Releasing a Semaphore with semop (on the CD-ROM at ./source/ch16/_semrel.c )
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include <stdlib.h>  4:  #include "common.h"  5:   6:  int main()  7:  {  8:  int semid;  9:  struct sembuf sb;  10:   11:  /* Get the semaphore with the id MY_SEM_ID */  12:  semid =  semget  ( MY_SEM_ID, 1, 0 );  13:   14:  if (semid >= 0) {  15:   16:  printf( "semrel: Releasing semaphore %d\n", semid );  17:   18:  sb.sem_num = 0;  19:  sb.sem_op  = 1;  20:  sb.sem_flg = 0;  21:   22:  /* Release the semaphore */  23:  if (  semop  ( semid, &sb, 1 ) == -1) {  24:   25:  printf("semrel: semop failed.\n");  26:  exit(-1);  27:   28:  }  29:   30:  printf( "semrel: Semaphore released %d\n", semid );  31:   32:  }  33:   34:  return 0;  35:  } 
end example
 

At line 12 of Listing 16.3, we first identify our semaphore of interest using the semget API function. Having our semaphore identifier, we build our sembuf structure to release the semaphore at line 23 using the semop API function. In this example, our sem_op element is 1 (compared to the “1 in Listing 16.2). In this example, we re releasing the semaphore, which means that we re making it nonzero (and thus available).

Note  

It s important to note the symmetry the sembuf uses in Listings 16.2 and 16.3. To acquire the semaphore, we subtract 1 from its value. To release the semaphore, we add 1 to its value. When the semaphore s value is 0, it s unavailable, forcing any processing attempting to acquire it to block. An initial value of 1 for the semaphore defines it as a binary semaphore. If the semaphore value is greater than 0, it can be considered a counting semaphore.

Let s now look at a sample application of each of the functions discussed thus far. Listing 16.4 illustrates execution of Listing 16.1 semcreate , Listing 16.2 semacq , and Listing 16.3 semrel .

Listing 16.4: Execution of the Sample Semaphore Applications
start example
  1:  #  ./semcreate   2:  semcreate: Created a semaphore 1376259  3:  #  ./semacq  &  4:  [1] 12189  5:  semacq: Attempting to acquire semaphore 1376259  6:  # ./semrel  7:  semrel: Releasing semaphore 1376259  8:  semrel: Semaphore released 1376259  9:  # semacq: Semaphore acquired 1376259  10:   11:  [1]+  Done                    ./semacq  12:  # 
end example
 

At line 1, we create the semaphore. We emit the identifier associated with this semaphore, 1376259 (which is shown at line 2). Next , at line 3, we perform the semacq application, which acquires the semaphore. We run this in the background (identified by the trailing & symbol) because this application will immediately block as the semaphore is unavailable. At line 4, we see the creation of the new subprocess (where [1] represents the number of subprocesses and 12189 is its process ID, or pid). The semacq application prints out its message, indicating that it s attempting to acquire the semaphore, but then it blocks. We then execute the semrel application to release the semaphore (line 6). We see two messages from this application; the first at line 7 indicates that it is about to release the semaphore, and then at line 8, we see that it was successful. Immediately thereafter, we see the semacq application was able to acquire the newly released semaphore, given its output at line 9. Finally, at line 11, we see the semacq application subprocess finish. Since it unblocked (based upon the presence of its desired semaphore), the semacq s main function reached its return, and thus the process finished.

Configuring a Semaphore

While there are a number of elements that can be configured for a semaphore, let s look here specifically at reading and writing the value of the semaphore (the current count).

In the first example, Listing 16.5, we ll demonstrate reading the current value of the semaphore. We achieve this using the semctl API function.

Listing 16.5: Retrieving the Current Semaphore Count (on the CD-ROM at ./source/_ch16/semcrd.c )
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include <stdlib.h>  4:  #include "common.h"  5:   6:  int main()  7:  {  8:  int semid, cnt;  9:   10:  /* Get the semaphore with the id MY_SEM_ID */  11:  semid =  semget  ( MY_SEM_ID, 1, 0 );  12:   13:  if (semid >= 0) {  14:   15:  /* Read the current semaphore count */  16:  cnt =  semctl  ( semid, 0, GETVAL );  17:   18:  if (cnt != -1) {  19:   20:  printf("semcrd: current semaphore count %d.\n", cnt);  21:   22:  }  23:   24:  }  25:   26:  return 0;  27:  } 
end example
 

Reading the semaphore count is performed at line 16. We specify the semaphore identifier, the index of the semaphore (0), and the command ( GETVAL ). Note that the semaphore is identified by an index because it could represent an array of semaphores (rather than one). The return value from this command is either “1 for error or the count of the semaphore.

We can configure a semaphore with a count using a similar mechanism (as shown in Listing 16.6).

Listing 16.6: Setting the Current Semaphore Count
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include <stdlib.h>  4:  #include "common.h"  5:   6:  int main()  7:  {  8:  int semid, ret;  9:   10:  /* Get the semaphore with the id MY_SEM_ID */  11:  semid =  semget  ( MY_SEM_ID, 1, 0 );  12:   13:  if (semid >= 0) {  14:   15:  /* Read the current semaphore count */  16:  ret =  semctl  ( semid, 0, SETVAL, 6 );  17:   18:  if (ret != -1) {  19:   20:  printf( "semcrd: semaphore count updated.\n" );  21:   22:  }  23:   24:  }  25:   26:  return 0;  27:  } 
end example
 

As with retrieving the current semaphore value, we can set this value using the semctl API function. The difference here is that along with the semaphore identifier ( semid ) and semaphore index (0), we specify the set command ( SETVAL ) and a value. In this example (line 16 of Listing 16.6), we re setting the semaphore value to 6. Setting the value to 6, as shown here, changes the binary semaphore to a counting semaphore. Six semaphore acquires would be permitted before an acquiring process would block.

Removing a Semaphore

Removing a semaphore is also performed through the semctl API function. After retrieving the semaphore identifier (line 10 in Listing 16.7), we use this to remove the semaphore using the semctl API function and the IPC_RMID command (at line 14).

Listing 16.7: Removing a Semaphore
start example
  1:  #include <stdio.h>  2:  #include <sys/sem.h>  3:  #include "common.h"  4:   5:  int main()  6:  {  7:  int semid, ret;  8:   9:  /* Get the semaphore with the id MY_SEM_ID */  10:  semid =  semget  ( MY_SEM_ID, 1, 0 );  11:   12:  if (semid >= 0) {  13:   14:  ret =  semctl  ( semid, 0, IPC_RMID);  15:   16:  if (ret != -1) {  17:   18:  printf( "Semaphore %d removed.\n", semid );  19:   20:  }  21:   22:  }  23:   24:  return 0;  25:  } 
end example
 

As you can probably see, the semaphore API probably is not the simplest that you ve used before.

That s it for our whirlwind tour; next we ll explore the semaphore API in greater detail and look at some of its other capabilities.




GNU/Linux Application Programming
GNU/Linux Application Programming (Programming Series)
ISBN: 1584505680
EAN: 2147483647
Year: 2006
Pages: 203
Authors: M. Tim Jones

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net