Next | Prev | Up | Top | Contents | Index

System V Semaphores

Semaphore IPC allows processes to communicate through the exchange of data items called semaphores. A single semaphore is represented as a positive integer (0 through 32,767). Semaphores are usually used to manage resources; each semaphore indicates whether or not a specific data item is currently in use (and by how many different processes). Since many applications require the use of more than one semaphore, the UNIX operating system has the ability to create sets or arrays of semaphores at one time. A semaphore set can contain one or more semaphores, up to a limit set by the system administrator. (This limit, a tunable parameter called SEMMSL, has a default value of 25.) To create a set of semaphores, use the semget() system call.

The process performing the semget() system call becomes the owner/creator of the semaphore set, determines how many semaphores are in the set, and sets the operation permissions for the set. This process can subsequently relinquish ownership of the set or change the operation permissions using the semctl() (semaphore control) system call.The creating process remains the creator as long as the semaphore set exists, but other processes with permission can use semctl() to perform other control functions.

Provided a process has alter permission, it can manipulate the semaphore set. Each semaphore within a set can be either increased or decreased with the semop() system call (see the semop(2) reference page for more information).

To increase a semaphore, pass a positive integer value of the desired magnitude to the semop() system call. To decrease a semaphore, pass a negative integer value of the desired magnitude.

The UNIX operating system ensures that only one process can manipulate a semaphore set at any given time. Simultaneous requests are performed sequentially in an arbitrary manner.

A process can test for a semaphore value to be greater than a certain value by attempting to decrement the semaphore by one more than that value. If the process is successful, then the semaphore value is greater than that certain value. Otherwise, the semaphore value is not. While doing this, the process can have its execution suspended (IPC_NOWAIT flag not set) until the semaphore value would permit the operation (other processes increment the semaphore), or the semaphore facility is removed.

The ability to suspend execution is called a "blocking semaphore operation." This ability is also available for a process that is testing for a semaphore to become zero or equal to zero; only read permission is required for this test, and it is accomplished by passing a value of zero to the semop() system call.

On the other hand, if the process is not successful and the process does not request to have its execution suspended, it is called a "nonblocking semaphore operation." In this case, the process is returned a known error code (-1), and the external errno variable is set accordingly.

The blocking semaphore operation allows processes to communicate based on the values of semaphores at different points in time. Remember also that IPC facilities remain in the UNIX operating system until removed by a permitted process or until the system is reinitialized.

Operating on a semaphore set is done by using the semop(), semaphore operation, system call.

When a set of semaphores is created, the first semaphore in the set is semaphore number zero. The last semaphore in the set is numbered one less than the number of semaphores in the set.

An array of these "blocking/nonblocking operations" can be performed on a set containing more than one semaphore. When performing an array of operations, the "blocking/nonblocking operations" can be applied to any or all of the semaphores in the set. Also, the operations can be applied in any order of semaphore number. However, no operations are done until they can all be done successfully. This requirement means that preceding changes made to semaphore values in the set must be undone when a "blocking semaphore operation" on a semaphore in the set cannot be completed successfully; no changes are made until they can all be made. For example, if a process has successfully completed three of six operations on a set of ten semaphores, but is "blocked" from performing the fourth operation, no changes are made to the set until the fourth and remaining operations are successfully performed. Additionally, any operation preceding or succeeding the "blocked" operation, including the blocked operation, can specify that when all operations can be performed successfully, that the operation should be undone. Otherwise, the operations are performed and the semaphores are changed, or one "nonblocking operation" is unsuccessful and none are changed. This is commonly referred to as being "atomically performed."

The ability to undo operations requires the UNIX operating system to maintain an array of "undo structures" corresponding to the array of semaphore operations to be performed. Each semaphore operation that is to be undone has an associated adjust variable used for undoing the operation, if necessary.

Remember, any unsuccessful "nonblocking operation" for a single semaphore or a set of semaphores causes immediate return with no operations performed at all. When this occurs, a known error code (-1) is returned to the process, and the external variable errno is set accordingly.

System calls make these semaphore capabilities available to processes. The calling process passes arguments to a system call, and the system call attempts to perform its function. If the system call is successful, it performs its function and returns the appropriate information. Otherwise, a known error code (-1) is returned to the process, and the external variable errno is set accordingly.


Using Semaphores

Before you can use semaphores, you must request a uniquely identified semaphore set and its associated data structure with a system call. The unique identifier is called the semaphore identifier (semid); it is used to reference a particular semaphore set and data structure.

The semaphore set contains a predefined number of structures in an array, one structure for each semaphore in the set. You may select the number of semaphores (nsems) in a set. The following members are in each instance of the structure within a semaphore set:

Each semaphore set has an associated data structure. This data structure contains information related to the semaphore set:

The definition for the semaphore set is:

struct sem
{
    ushort semval; /* semaphore text map address */
    short sempid; /* pid of last operation */
    ushort semncnt; /* # awaiting semval > cval */
    ushort semzcnt; /* # awaiting semval = 0 */
};
This definition is located in the /usr/include/sys/sem.h header file. The structure definition for the associated semaphore data structure is also located in the same header file. Note that the sem_perm member of this structure uses ipc_perm as a template. The ipc_perm data structure is the same for all IPC facilities, and it is located in the /usr/include/sys/ipc.h header file.

The semget() system call performs two tasks when only the IPC_CREAT flag is set in the semflg argument that it receives:

The task performed is determined by the value of the key argument passed to the semget() system call. For the first task, if the key is not already in use for an existing semid, a new semid is returned with an associated data structure and semaphore set created for it, provided no system tuning parameter would be exceeded.

Also, a provision exists for specifying a key of value zero, known as the private key (IPC_PRIVATE = 0). When specified, a new semid is always returned with an associated data structure and semaphore set created for it, unless a system tuning parameter would be exceeded. When the ipcs command is performed, the KEY field for the semid is all zeros.

When performing the first task, the process that calls semget() becomes the owner/creator, and the associated data structure is initialized accordingly. Remember, ownership can be changed, but the creating process always remains the creator; see "Controlling Semaphores With semctl()" for more information. The creator of the semaphore set also determines the initial operation permissions for the facility.

For the second task, if a semid exists for the key specified, the value of the existing semid is returned. If you do not want an existing semid returned, you can specify IPC_EXCL in the semflg argument passed to the system call. The system call fails if it is passed a value for the number of semaphores (nsems) that is greater than the number actually in the set. If you do not know how many semaphores are in the set, use 0 for nsems. The details of the semget() system call are discussed in "Getting Semaphores With semget()."

Once a uniquelyidentified semaphore set and data structure are created, use semaphore operations (semop()) and semaphore control (semctl()).

Semaphores can be increased, decreased, or tested for zero. A single system call, semop(), is used to perform these operations. Refer to "Operations on Semaphores: semop()" for details on this system call.

Semaphore control is done by using the semctl() system call. Use control operations to control the semaphore facility in the following ways:

Refer to "Controlling Semaphores With semctl()" for details of the semctl() system call.


Getting Semaphores With semget()

This section contains a detailed description of using the semget() system call along with an example program illustrating its use.

The function prototype found in the semget(2) reference page is as follows:

int semget(key_t key, int nsems, int semflg);

Type key_t is declared in the types.h header file to be an integer.

The integer returned from this system call upon successful completion is the semaphore set identifier (semid) discussed above. A new semid with an associated semaphore set and data structure is provided when either of the following conditions is true:

The value passed to the semflg argument specifies the following:

Access permissions and execution modes are collectively referred to as "operation permissions."

Table 2-4 shows the numeric values (expressed in octal notation) for the valid operation permissions codes.

Operation Permissions Codes
Operation PermissionsOctal Value
Read by User00400
Alter by User00200
Read by Group00040
Alter by Group00020
Read by Others00004
Alter by Others00002

A specific octal value is derived by adding the octal values for the operation permissions desired. That is, if read by user and read and alter by others is desired, the code value would be 00406 (00400 plus 00004 plus 00002).

Control commands constants are declared in the ipc.h header file. Table 2-5 contains the names of the constants that apply to the semget() system call along with their values.

Control Commands (Flags)
Control CommandValue
IPC_CREAT0001000
IPC_EXCL0002000

The value for semflg is, therefore, a combination of operation permissions and control commands. After determining the value for the operation permissions as previously described, the desired flag(s) can be specified using a bitwise OR with the operation permissions.

semid = semget (key, nsems, (IPC_CREAT | 0400));
semid = semget (key, nsems, (IPC_CREAT | IPC_EXCL | 0400));
As specified by the semget(2) reference page, success or failure of this system call depends upon the actual argument values for key, nsems, semflg, as limited by system tuning parameters. The system call attempts to return a new semid if either of the following conditions is true:

The key argument can be set to IPC_PRIVATE as follows:

semid = semget (IPC_PRIVATE, nsems, semflg);

Exceeding the SEMMNI, SEMMNS, or SEMMSL system tuning parameters always causes a failure. The SEMMNI system tuning parameter determines the maximum number of unique semaphore sets (semids) in the UNIX operating system. The SEMMNS system tuning parameter determines the maximum number of semaphores in all semaphore sets system wide. The SEMMSL system tuning parameter determines the maximum number of semaphores in each semaphore set.

The second condition is satisfied if the value for key is not already associated with a semid, and the bitwise ANDing of semflg and IPC_CREAT is "true" (not zero). This means that the key is unique (not already in use) within the UNIX operating system for this facility type and that the IPC_CREAT flag has been set (using semflg | IPC_CREAT). SEMMNI, SEMMNS, and SEMMSL apply here also, just as for condition one.

IPC_EXCL is another control command used with IPC_CREAT to exclusively have the system call fail if, and only if, a semid exists for the specified key provided. This is necessary to prevent the process from thinking that it has received a new (unique) semid when it has not. In other words, when both IPC_CREAT and IPC_EXCL are specified, a new semid is returned if the system call is successful. Any value for semflg returns a new semid if the key equals zero (IPC_PRIVATE) and no system tuning parameters are exceeded.

Refer to the semget(2) reference page for specific associated data structure initialization for successful completion.


Example Program

The example program in this section (Example 2-7) is a menu-driven program that exercises all possible combinations of the semget() system call.

From studying this program, you can observe the method of passing arguments and receiving return values. The user-written program requirements are pointed out.

This program begins (lines 4-8) by including the required header files as specified by the semget(2) reference page. Note that the errno.h header file is included as opposed to declaring errno as an external variable (either method works).

Variable names have been chosen to be as close as possible to those in the synopsis. Their declarations are self-explanatory. The variables declared for this program and their purposes are as follows:

keyThe value for the desired key
oppermThe desired operation permissions
flagsThe desired control commands (flags)
opperm_flagsThe combination from the logical ORing of the opperm and flags variables; used in the system call to pass the semflg argument
semidThe value returned by semget().

The program begins by prompting for a hexadecimal key, an octal operation permissions code, and the control command combinations (flags), which are selected from a menu (lines 15-32). All possible combinations are allowed, even though they might not be viable. This allows you to observe the errors for illegal combinations.

Next, the menu selection for the flags is combined with the operation permissions, and the result is stored in the opperm_flags variable (lines 36-52).

Then, the number of semaphores for the set is requested (lines 53-57), and its value is stored in nsems. The system call is made next, and the result is stored in the semid variable (lines 60, 61).

Since the semid variable now contains a valid semaphore set identifier or the error code (-1), it is tested to see if an error occurred (line 63). If semid equals -1, a message indicates that an error resulted and the external errno variable is displayed (lines 65, 66). Remember that the external errno variable is set only when a system call fails; it should only be tested immediately following system calls.

If no error occurred, the returned semaphore set identifier is displayed (line 70).

The example program for the semget() system call is shown in Example 2-7.

Example 2-7 : semget() System Call Example

1   /*This is a program to illustrate
2    *the semaphore get, semget(), 
3    *system call capabilities.*/
4   #include <stdio.h> 
5   #include <sys/types.h> 
6   #include <sys/ipc.h> 
7   #include <sys/sem.h> 
8   #include <errno.h>
9   /*Start of main C language program*/ 
10   main() 
11  {
12     key_t key; /*declare as long integer*/ 
13     int opperm, flags, nsems; 
14     int semid, opperm_flags;
15     /*Enter the desired key*/ 
16     printf("\nEnter the desired key in hex = "); 
17       scanf("%x", &key);
18     /*Enter the desired octal operation 
19     permissions.*/
20       printf("\nEnter the operation\n"); 
21     printf("permissions in octal = "); 
22     scanf("%o", &opperm);
23     /*Set the desired flags.*/
24       printf("\nEnter corresponding number to\n"); 
25     printf("set the desired flags:\n"); 
26     printf("No flags = 0\n"); 
27     printf("IPC_CREAT = 1\n"); 
28     printf("IPC_EXCL = 2\n"); 
29     printf("IPC_CREAT and IPC_EXCL = 3\n"); 
30     printf(" Flags = "); 
31     /*Get the flags to be set.*/ 
32     scanf("%d", &flags);
33     /*Error checking (debugging)*/ 
34     printf ("\nkey =0x%x, opperm = 0%o, flags = 0%o\n", 
35       key, opperm, flags); 
36     /*Incorporate the control fields (flags) with 
37       the operation permissions.*/ 
38     switch (flags) 
39     {
40     case 0: /*No flags are to be set.*/ 
41       opperm_flags = (opperm | 0); 
42       break; 
43     case 1: /*Set the IPC_CREAT flag.*/ 
44       opperm_flags = (opperm | IPC_CREAT); 
45       break; 
46     case 2: /*Set the IPC_EXCL flag.*/
47       opperm_flags = (opperm | IPC_EXCL); 
48       break; 
49     case 3: /*Set the IPC_CREAT and IPC_EXCL 
50         flags.*/
51       opperm_flags = (opperm | IPC_CREAT | IPC_EXCL); 
52     }
53     /*Get the number of semaphores for this set.*/ 
54     printf("\nEnter the number of\n");
55     printf("desired semaphores for\n"); 
56     printf("this set (25 max) = "); 
57     scanf("%d", &nsems);
58     /*Check the entry.*/
59     printf("enNsems = %d\n", nsems);
60     /*Call the semget system call.*/ 
61     semid = semget(key, nsems, opperm_flags);
62     /*Perform the following if the call is unsuccessful.*/
63     if(semid == -1) 
64     { 
65       printf("The semget system call failed!\n"); 
66       printf("The error number = %d\n", errno);
67     }
68     /*Return the semid upon successful completion.*/ 
69     else 
70       printf("\nThe semid = %d\n", semid);
71     exit(0); 
72   }

Controlling Semaphores With semctl()

This section details the semctl() system call and provides an example program that exercises all of its capabilities.

The prototype found in the semctl(2) reference page is as follows:

union semun {
      int val;
      struct semid_ds *buf;
      ushort *array;
     };
int semctl (int semid, int semnum, int cmd, union semun arg) 
The semctl() system call requires three or four arguments to be passed to it, and it returns an integer value.

The semid argument must be a nonnegative, integer returned by semget(). The semnum argument selects a semaphore within a set by its number. The first semaphore in a set is number 0.

The cmd argument can be replaced by one of the following control commands (flags):

GETVALReturn the value of a single semaphore within a semaphore set.
SETVALSet the value of a single semaphore within a semaphore set.
GETPIDReturn the process identifier (PID) of the process that performed the last operation on the semaphore within a semaphore set.
GETNCNTReturn the number of processes waiting for the value of a particular semaphore to become greater than its current value.
GETZCNTReturn the number of processes waiting for the value of a particular semaphore to be equal to zero.
GETALLReturn the values for all semaphores in a semaphore set.
SETALLSet all semaphore values in a semaphore set.
IPC_STATReturn the status information contained in the associated data structure for the specified semid, and place it in the data structure pointed to by the *buf pointer in the user memory area; arg.buf is the union member that contains the value of buf.
IPC_SETFor the specified semaphore set (semid), set the effective user/group identification and operation permissions.
IPC_RMIDRemove the specified (semid) semaphore set along with its associated data structure.

A process must have an effective user identification of owner/creator of the semaphore set, or of superuser, to perform an IPC_SET or IPC_RMID control command. Read/alter permission is required for other control commands.

Use the arg argument to pass the appropriate union member to the system call for the control command to be performed.

The details of this system call are discussed in the example program for it. If you have problems understanding the logic manipulations in this program, read "Getting Semaphores With semget()"; it goes into more detail than would be practical to do for every system call.


Example Program

The example program in this section (Example 2-8 on page 91) is a menu-driven program that exercises all possible combinations of using the semctl() system call. From studying this program, you can observe the method of passing arguments and receiving return values. The user-written program requirements are pointed out.

This program begins (lines 5-9) by including the required header files as specified by the semctl(2) reference page. Note that in this program errno is declared as an external variable, and therefore the errno.h header file does not have to be included.

Variable, structure, and union names have been chosen to be as close as possible to those in the synopsis. Their declarations are self-explanatory. Those declared for this program and their purpose are as follows:

semid_dsThe specified semaphore set identifier's data structure when an IPC_STAT control command is performed.
cThe input values from the scanf() function (line 117) when performing a SETALL control command.
iGeneral counter/index.
lengthThe number of semaphores in a set (lines 97, 115).
uidThe IPC_SET value for the effective user identification.
gidThe IPC_SET value for the effective group identification.
modeThe IPC_SET value for the operation permissions.
rtrnThe return integer from the system call.
semidThe semaphore set ID.
semnumThe semaphore number to the system call.
cmdThe code for the desired control command.
choiceMember (uid, gid, mode) for the IPC_SET control command that is to be changed.
argThe union argument to IPC_STAT, IPC_SET, GETALL, and SETALL.

Note that the semid_ds data structure in this program (line 14) uses the data structure located in the sem.h header file of the same name as a template for its declaration. This is a perfect example of the advantage of local variables.

The arg union (lines 18-22) serves three purposes in one. The compiler allocates enough storage to hold its largest member. The program can then use the union as any member by referencing union members as if they were regular structure members. Note that the array is declared to have 25 elements (0 through 24). This number corresponds to the maximum number of semaphores allowed per set (SEMMSL), a system tuning parameter.

The next important program aspect to observe is that although the *buf pointer member (arg.buf) of the union is declared to be a pointer to a data structure of the semid_ds type, it must also be initialized to contain the address of the user memory area data structure (line 24). Because of the way this program is written, the pointer does not need to be reinitialized later. If it was used to increment through the array, it would need to be reinitialized just before calling the system call.

Now all of the required declarations have been presented for this program. The next paragraphs explain how it works.

First, the program prompts for a valid semaphore set identifier, which is stored in the semid variable (lines 25-27). This is required for all semctl() system calls.

Then, the code for the desired control command must be entered (lines 28-42), and the code is stored in the cmd variable. The code is tested to determine the control command for subsequent processing.

If the GETVAL control command is selected (code 1), a message prompting for a semaphore number is displayed (lines 49, 50). When it is entered, it is stored in the semnum variable (line 51). Then, the system call is performed, and the semaphore value is displayed (lines 52-55). If the system call is successful, a message indicates this along with the semaphore set identifier used (lines 195, 196); if the system call is unsuccessful, an error message is displayed along with the value of the external errno variable (lines 191-193).

If the SETVAL control command is selected (code 2), a message prompting for a semaphore number is displayed (lines 56, 57). When it is entered, it is stored in the semnum variable (line 58). Next, a message prompts for the value to which the semaphore is to be set, and it is stored as the arg.val member of the union (lines 59, 60). Then, the system call is performed (lines 62). Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the GETPID control command is selected (code 3), the system call is made immediately since all required arguments are known (lines 64-67), and the PID of the process performing the last operation is displayed. Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the GETNCNT control command is selected (code 4), a message prompting for a semaphore number is displayed (lines 68-72). When entered, it is stored in the semnum variable (line 73). Then, the system call is performed, and the number of processes waiting for the semaphore to become greater than its current value is displayed (lines 74-77). Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the GETZCNT control command is selected (code 5), a message prompting for a semaphore number is displayed (lines 78-81). When it is entered, it is stored in the semnum variable (line 82). Then the system call is performed, and the number of processes waiting for the semaphore value to become equal to zero is displayed (lines 83, 86). Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the GETALL control command is selected (code 6), the program first performs an IPC_STAT control command to determine the number of semaphores in the set (lines 88-93). The length variable is set to the number of semaphores in the set (line 91). Next, the system call is made and, upon success, the arg.array union member contains the values of the semaphore set (line 96). Now, a loop is entered that displays each element of the arg.array (lines 97-103). The semaphores in the set are displayed on a single line, separated by a space. Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the SETALL control command is selected (code 7), the program first performs an IPC_STAT control command to determine the number of semaphores in the set (lines 106-108). The length variable is set to the number of semaphores in the set (line 109). Next, the program prompts for the values to be set and enters a loop that takes values from the keyboard and initializes the arg.array union member to contain the desired values of the semaphore set (lines 113-119). The system call is then made (lines 120-122). Depending upon success or failure, the program returns the same messages as for GETVAL above.

If the IPC_STAT control command is selected (code 8), the system call is performed (line 127), and the status information returned is printed out (lines 128-139); only the members that can be set are printed out in this program. Note that if the system call is unsuccessful, the status information of the last successful one is printed out. In addition, an error message is displayed, and the errno variable is printed out (lines 191, 192).

If the IPC_SET control command is selected (code 9), the program gets the current status information for the semaphore set identifier specified (lines 143-146). This is necessary because this example program provides for changing only one member at a time, and the semctl() system call changes all of them. Also, if an invalid value happened to be stored in the user memory area for one of these members, it would cause repetitive failures for this control command until corrected. The next thing the program does is prompt for a code corresponding to the member to be changed (lines 147-153). This code is stored in the choice variable (line 154). Now, depending upon the member picked, the program prompts for the new value (lines 155-178). The value is placed in the appropriate member in the user memory area data structure, and the system call is made (line 181). Depending on success or failure, the program returns the same messages as for GETVAL above.

If the IPC_RMID control command (code 10) is selected, the system call is performed (lines 183-185). The semid is removed from the UNIX operating system along with its associated data structure and semaphore set. Depending on success or failure, the program returns the same messages as for the other control commands.

The example program for the semctl() system call is shown in Example 2-8.

Example 2-8 : semctl() System Call Example

1   /*This is a program to illustrate
2    *the semaphore control, semctl(), 
3    *system call capabilities. 
4    */
5     /*Include necessary header files.*/ 
6     #include <stdio.h>
7     #include <sys/types.h>
8     #include <sys/ipc.h> 
9     #include <sys/sem.h>
10    /*Start of main C language program*/ 
11    main() 
12    { 
13      extern int errno; 
14     struct semid_ds semid_ds;
15     int c, i, length; 
16     int uid, gid, mode; 
17     int retrn, semid, semnum, cmd, choice; 
18     union semun { 
19       int val;
20       struct semid_ds *buf; 
21       ushort array[25]; 
22     } arg;
23     /*Initialize the data structure pointer.*/
24     arg.buf = &semid_ds;
25     /*Enter the semaphore ID.*/ 
26     printf("Enter the semid = "); 
27     scanf("%d", &semid);
28     /*Choose the desired command.*/
29     printf("\nEnter the number for\n"); 
30     printf("the desired cmd:\n"); 
31     printf("GETVAL = 1\n"); 
32     printf("SETVAL = 2\n"); 
33     printf("GETPID = 3\n");
34     printf("GETNCNT = 4\n");
35     printf("GETZCNT = 5\n");
36     printf("GETALL = 6\n"); 
37     printf("SETALL = 7\n"); 
38     printf("IPC_STAT = 8\n"); 
39     printf("IPC_SET = 9\n"); 
40     printf("IPC_RMID = 10\n");
41     printf("Entry = "); 
42     scanf("%d", &cmd);
43     /*Check entries.*/
44     printf ("\nsemid =%d, cmd = %d\n\n", 
45      semid, cmd);
46     /*Set the command and do the call.*/ 
47     switch (cmd)
48     {
49     case 1: /*Get a specified value.*/ 
50       printf("\nEnter the semnum = "); 
51       scanf("%d", &semnum); 
52       /*Do the system call.*/ 
53       retrn = semctl(semid, semnum, GETVAL, 0); 
54       printf("\nThe semval = %d\n", retrn); 
55       break; 
56     case 2: /*Set a specified value.*/
57       printf("\nEnter the semnum = "); 
58       scanf("%d", &semnum); 
59       printf("\nEnter the value = "); 
60       scanf("%d", &arg.val);
61       /*Do the system call.*/ 
62       retrn = semctl(semid, semnum, SETVAL, arg.val);
63       break;
64     case 3: /*Get the process ID.*/ 
65       retrn = semctl(semid, 0, GETPID, 0); 
66       printf("\nThe sempid = %d\n", retrn); 
67       break; 
68     case 4: /*Get the number of processes 
69       waiting for the semaphore to 
70       become greater than its current 
71       value.*/
72       printf("\nEnter the semnum = "); 
73       scanf("%d", &semnum);
74       /*Do the system call.*/ 
75       retrn = semctl(semid, semnum, GETNCNT, 0); 
76       printf("\nThe semncnt = %d", retrn); 
77       break;
78     case 5: /*Get the number of processes 
79       waiting for the semaphore 
80       value to become zero.*/ 
81       printf("\nEnter the semnum = "); 
82       scanf("%d", &semnum); 
83       /*Do the system call.*/ 
84       retrn = semctl(semid, semnum, GETZCNT, 0); 
85       printf("\nThe semzcnt = %d", retrn); 
86       break;
87     case 6: /*Get all of the semaphores.*/ 
88       /*Get the number of semaphores in
89       the semaphore set.*/
90       retrn = semctl(semid, 0, IPC_STAT, arg.buf);
91       length = arg.buf->sem_nsems; 
92       if(retrn == -1) 
93         goto ERROR;
94       /*Get and print all semaphores in the 
95       specified set.*/ 
96       retrn = semctl(semid, 0, GETALL, arg.array);
97       for (i = 0; i < length; i++) 
98       { 
99         printf("%d", arg.array[i]); 
100        /*Seperate each 
101        semaphore.*/ 
102        printf("%c", ' '); 
103      } 
104      break;
105    case 7: /*Set all semaphores in the set.*/ 
106      /*Get the number of semaphores in
107      the set.*/ 
108      retrn = semctl(semid, 0, IPC_STAT, arg.buf); 
109      length = arg.buf->sem_nsems; 
110      printf("Length = %den", length);
111      if(retrn == -1) 
112        goto ERROR; 
113      /*Set the semaphore set values.*/ 
114      printf("\nEnter each value:\n"); 
115      for(i = 0; i < length ; i++) 
116      { 
117        scanf("%d", &c); 
118        arg.array[i] = c; 
119      } 
120      /*Do the system call.*/ 
121      retrn = semctl(semid, 0, SETALL, arg.array);
122      break;
123    case 8: /*Get the status for the semaphore set.*/ 
125      /*Get and print the current status values.*/ 
127      retrn = semctl(semid, 0, IPC_STAT, arg.buf); 
128      printf ("\nThe USER ID = %d\n", 
129        arg.buf->sem_perm.uid); 
130      printf ("The GROUP ID = %d\n", 
131        arg.buf->sem_perm.gid); 
132      printf ("The operation permissions = 0%o\n", 
133        arg.buf->sem_perm.mode); 
134      printf ("The number of semaphores in set = %d\n", 
135        arg.buf->sem_nsems); 
136      printf ("The last semop time = %d\n", 
137        arg.buf->sem_otime);
138      printf ("The last change time = %d\n", 
139        arg.buf->sem_ctime); 
140      break;
141    case 9: /*Select and change the desired 
142            member of the data structure.*/
143      /*Get the current status values.*/ 
144      retrn = semctl(semid, 0, IPC_STAT, arg.buf); 
145      if(retrn == -1) 
146        goto ERROR; 
147      /*Select the member to change.*/ 
148      printf("\nEnter the number for the\n"); 
149      printf("member to be changed:\n"); 
150      printf("sem_perm.uid = 1\n"); 
151      printf("sem_perm.gid = 2\n"); 
152      printf("sem_perm.mode = 3\n"); 
153      printf("Entry = "); 
154      scanf("%d", &choice); 
155      switch(choice){
156      case 1: /*Change the user ID.*/ 
157        printf("\nEnter USER ID = "); 
158        scanf ("%d", &uid); 
159          arg.buf->sem_perm.uid = uid; 
160        printf("enUSER ID = %d\n", 
161          arg.buf->sem_perm.uid); 
162        break;
163      case 2: /*Change the group ID.*/ 
164        printf("\nEnter GROUP ID = "); 
165        scanf("%d", &gid);
166          arg.buf->sem_perm.gid = gid; 
167        printf("\nGROUP ID = %d\n", 
168          arg.buf->sem_perm.gid); 
169        break;
170      case 3: /*Change the mode portion of
171          the operation
172          permissions.*/
173        printf("\nEnter MODE = "); 
174        scanf("%o", &mode); 
175          arg.buf->sem_perm.mode = mode; 
176        printf("\nMODE = 0%o\n",
177          arg.buf->sem_perm.mode); 
178        break; 
179    } 
180    /*Do the change.*/ 
181    retrn = semctl(semid, 0, IPC_SET, arg.buf); 
182    break; 
183    case 10: /*Remove the semid along with its 
184      data structure.*/ 
185      retrn = semctl(semid, 0, IPC_RMID, 0); 
186    } 
187    /*Perform the following if the call is unsuccessful.*/ 
188    if(retrn == -1) 
189    { 
190  ERROR: 
191      printf ("\n\nThe semctl system call failed!\n"); 
192      printf ("The error number = %d\n", errno); 
193      exit(0);
194    } 
195    printf ("\n\n semctl system call was successful\n"); 
196      printf ("for semid = %d\n", semid); 
197    exit (0); 
198 }

Operations on Semaphores: semop()

This section details the semop() system call and provides an example program that exercises its capabilities.

The prototype found in the semop(2) reference page is as follows:

int semop (int semid, struct sembuf *sops, size_t nsops)

semop() returns 0 on success, or -1 on failure.

The semid argument must be a nonnegative integer returned by semget().

The sops argument is a pointer to an array of structures in the user memory area that contains the following for each semaphore to be changed:

The *sops declaration means that a pointer can be initialized to the address of the array, or the array name can be used since it is the address of the first element of the array. Sembuf is the tag name of the data structure used as the template for the structure members in the array; it is located in the /usr/include/sys/sem.h header file.

The nsops argument specifies the length of the array (the number of structures in the array). The maximum size of this array is determined by the SEMOPM system tuning parameter. Therefore, a maximum of SEMOPM operations can be performed for each semop() system call.

The semaphore number determines the particular semaphore within the set on which the operation is to be performed.

The operation to be performed is determined by the following:

The following operation commands (flags) can be used:


Example Program

The example program in this section (Example 2-9) is a menu-driven program that exercises all possible combinations of the semop() system call.

From studying this program, you can observe the method of passing arguments and receiving return values. The user-written program requirements are pointed out.

This program begins (lines 5-9) by including the required header files as specified by the semop(2) reference page. Note that in this program errno is declared as an external variable, and therefore, the errno.h header file does not have to be included.

Variable and structure names have been chosen to be as close as possible to those in the synopsis. Their declarations are self-explanatory. These names make the program more readable, and this is perfectly legal since the declarations are local to the program.

The variables declared for this program and their purposes are as follows:

sembuf[10]An array buffer (line 14) to contain a maximum of ten sembuf type structures; ten equals SEMOPM, the maximum number of operations on a semaphore set for each semop() system call
*sopsUsed as a pointer (line 14) to sembuf[10] for the system call and for accessing the structure members within the array
rtrnThe return values from the system call
flagsThe code of the IPC_NOWAIT or SEM_UNDO flags for the semop() system call (line 60)
iA counter (line 32) for initializing the structure members in the array, and used to print out each structure in the array (line 79)
nsopsSpecifies the number of semaphore operations for the system call; must be less than or equal to SEMOPM
semidThe desired semaphore set identifier for the system call

First, the program prompts for a semaphore set identifier that the system call is to perform operations on (lines 19-22). The ID is stored in the semid variable (line 23).

A message is displayed requesting the number of operations to be performed on this set (lines 25-27). The number of operations is stored in the nsops variable (line 28).

Next, a loop is entered to initialize the array of structures (lines 30-77). The semaphore number, operation, and operation command (flags) are entered for each structure in the array. The number of structures equals the number of semaphore operations (nsops) to be performed for the system call, so nsops is tested against the i counter for loop control. Note that sops is used as a pointer to each element (structure) in the array, and sops is incremented just like i. sops is then used to point to each member in the structure for setting them.

After the array is initialized, all of its elements are printed out for feedback (lines 78-85). The sops pointer is set to the address of the array (lines 86, 87). Sembuf could be used directly, if desired, instead of sops in the system call.

The system call is made (line 89), and depending upon success or failure, a corresponding message is displayed. The results of the operation(s) can be viewed by using semctl() with the GETALL control command.

Example 2-9 : semop() System Call Example

1  /*This is a program to illustrate
2   *the semaphore operations, semop(), 
3   *system call capabilities. 
4   */
5   /*Include necessary header files.*/ 
6   #include <stdio.h>
7   #include <sys/types.h>
8   #include <sys/ipc.h> 
9   #include <sys/sem.h> 
10  *Start of main C language program*/ 
11  main() 
12  { 
13    extern int errno; 
14    struct sembuf sembuf[10], *sops;
15    char string[]; 
16    int retrn, flags, sem_num, i, semid; 
17    unsigned nsops; 
18    sops = sembuf; /*Pointer to array sembuf.*/
19    /*Enter the semaphore ID.*/
20    printf("\nEnter the semid of\n");
21    printf("the semaphore set to\n"); 
22    printf("be operated on = "); 
23    scanf("%d", &semid); 
24    printf("ensemid = %d", semid);
25    /*Enter the number of operations.*/
26    printf("\nEnter the number of semaphore\n"); 
27    printf("operations for this set = "); 
28    scanf("%d", &nsops); 
29    printf("ennosops = %d", nsops);
30    /*Initialize the array for the 
31       number of operations to be performed.*/ 
32    for(i = 0; i < nsops; i++, sops++) 
33    {
34      /*This determines the semaphore in
35      the semaphore set.*/
36      printf("\nEnter the semaphore\n"); 
37      printf("number (sem_num) = "); 
38      scanf("%d", &sem_num); 
39      sops->sem_num = sem_num; 
40      printf("\nThe sem_num = %d", sops->sem_num);
41     /*Enter a (-)number to decrement,
42       an unsigned number (no +) to increment, 
43       or zero to test for zero. These values 
44       are entered into a string and converted
45       to integer values.*/ 
46     printf("\nEnter the operation for\n"); 
47     printf("the semaphore (sem_op) = "); 
48     scanf("%s", string); 
49     sops->sem_op = atoi(string);
50     printf("ensem_op = %d\n", sops->sem_op);
51     /*Specify the desired flags.*/ 
52     printf("\nEnter the corresponding\n"); 
53     printf("number for the desired\n"); 
54     printf("flags:\n"); 
55     printf("No flags = 0\n");
56     printf("IPC_NOWAIT = 1\n"); 
57     printf("SEM_UNDO = 2\n");
58     printf("IPC_NOWAIT and SEM_UNDO = 3\n"); 
59     printf(" Flags = "); 
60     scanf("%d", &flags);
61     switch(flags) 
62     { 
63     case 0: 
64       sops->sem_flg = 0;
65       break; 
66     case 1: 
67       sops->sem_flg = IPC_NOWAIT;
68       break; 
69     case 2:
70       sops->sem_flg = SEM_UNDO;
71       break;
72     case 3:
73       sops->sem_flg = IPC_NOWAIT | SEM_UNDO; 
74       break;
75     } 
76     printf("\nFlags = 0%o\n", sops->sem_flg); 
77   }
78   /*Print out each structure in the array.*/ 
79   for(i = 0; i < nsops; i++)
80   { 
81       printf("\nsem_num = %d\n", sembuf[i].sem_num); 
82       printf("sem_op = %d\n", sembuf[i].sem_op); 
83       printf("sem_flg = %o\n", sembuf[i].sem_flg);
84       printf("%c", ' '); 
85     }
86     sops = sembuf; /*Reset the pointer to
87         sembuf[0].*/
88     /*Do the semop system call.*/ 
89     retrn = semop(semid, sops, nsops); 
90     if(retrn == -1) {
91       printf("\nSemop failed. ");
92       printf("Error = %d\n", errno);
93     } 
94     else { 
95       printf ("\nSemop was successful\n"); 
96       printf("for semid = %d\n", semid);
97       printf("Value returned = %d\n", retrn); 
98   }
99 }

Next | Prev | Up | Top | Contents | Index