Before a semaphore set can be used, it must be created. The creation of the semaphore set generates a unique data structure that the system uses to identify and manipulate the semaphores. The definition of system semaphore data structure is found in the system-dependent include file . This file is not directly referenced by the programmer, since the standard include file < sys/sem.h> includes this file.
struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ __time_t sem_otime; /* last semop() time */ unsigned long int __unused1; __time_t sem_ctime; /* last time changed by semctl() */ unsigned long int __unused2; unsigned long int sem_nsems; /* number of semaphores in set */ unsigned long int __unused3; unsigned long int __unused4; };
In keeping with its origins, and for System V compatibility, the semid_ds structure is also defined in as
struct semid_ds { struct ipc_perm sem_perm; /* permissions .. see ipc.h */ __kernel_time_t sem_otime; /* last semop time */ __kernel_time_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ struct sem_queue *sem_pending; /* pending operations to be processed */ struct sem_queue **sem_pending_last;/* last pending operation */ struct sem_undo *undo; /* undo requests on this array */ unsigned short sem_nsems; /* no. of semaphores in array */ };
As with message queues, there is a bit of a disconnect between the way we view and discuss semaphores and the way they may actually be implemented at a system level. Additional system-specific details can be found in the source code for semaphore implementation in the kernel source directory /usr/src/linux-XX.XX.XX/ipc (where XX is the appropriate version number). On our system the files sem.c and util.h contain additional semaphore implementation details.
Using the definition as our reference, the system semaphore data structure semid_ds contains a permission structure of type ipc_perm , which is used to specify the access permissions for the semaphore set. The access permission structure is followed by two time members . These store the time of the last operation on the semaphore ( sem_otime ) and the time of its last modification ( sem_ctime ). The next member is a reference, sem_base , to an array (set) of structures of type sem. The sem structure contains the semaphore value and the ID of the last process to operate on the semaphore. Here is the definition of a sem structure:
struct sem { int semval; /* current value */ int sempid; /* pid of last operation */ };
Following the pointer to the base of the semaphore array are three additional pointers. The sem_pending member references a linked list (treated as a queue) of pending semaphore operations. Normally, semaphore operations are done immediately, so requests are only added to this list if for some reason the request cannot processed immediately. The sem_pending_last member references the end of the same list. The sem_undo member references a list of undo operations. These operations, stored when a semaphore operation sets the SEM_UNDO flag, can be used to undo requested semaphore operations to return the semaphore to its previous state. The kernel uses the undo list information to reverse semaphore operations when a process exits without releasing its allocated semaphores. This action helps reduce the chance of deadlock. The system semaphore data structure also keeps track of the number of semaphores in the set ( sem_nsems ).
A conceptual arrangement of a system semaphore structure for a newly allocated set of three semaphores is shown in Figure 7.1.
Figure 7.1. Data structures for a set of three semaphores.
To create a semaphore or gain access to one that exists, the semget system call, shown in Table 7.1, is used.
Table 7.1. Summary of the semget System Call.
Include File(s) |
|
Manual Section |
2 |
|
Summary |
int semget (key_t key,intnsems,int semflg); |
|||
Return |
Success |
Failure |
Sets errno |
|
The semaphore identifier |
-1 |
Yes |
The semget system call takes three arguments. The first argument, key , is used by the system to generate a unique semaphore identifier. The second argument, nsems , is the number of semaphores in the set. The system uses the nsems value when allocating the array of sem structures. Remember, as with all arrays in C/C++, the array of sem structures that represents the set of semaphores is indexed starting at 0. The nsems value should be less than or equal to the SEMMSL value, which sets the upper boundary for the number of semaphores for a given semaphore ID. The third argument, semflg , is used to specify access permission and/or special creation conditions. The low-order bits of the semflg value are used to specify the access permissions for the owner, group , and other. Read and write permissions control reading and alteration of the semaphore; execute permission settings are ignored. The flags IPC_CREAT and IPC_EXCL may be OR ed with the permission value.
If the semget system call is successful, a nonnegative integer, the semaphore identifier, is returned. If the value for key is IPC_PRIVATE or the value for key does not have a semaphore identifier associated with it, and IPC_CREAT has been specified, a new set of semaphores is created. When created, the semaphore set represented by the array of sem structures is not initialized . If IPC_CREAT is specified (but not IPC_EXCL) and the semaphore set for the indicated key value already exists, the semget system call returns the associated semaphore identifier. When using semget to access an established semaphore set (such as in a client process), the value of nsems can be set to 0 (a don't-care value).
When the semaphore is created, the system sets, respectively, the semid_ds members sem_perm.cuid , sem_perm.uid , sem_perm.cgid , and sem_perm.gid to the effective user and group ID of the invoking process. The member sem_otime is set to 0, and sem_ctime is assigned the current time. The nsems member stores the number of semaphores in the semaphore set.
If the semget system call fails, it returns a -1 and sets the value stored in errno . Error messages for semget are shown in Table 7.2.
Table 7.2. semget Error Messages.
# |
Constant |
perror Message |
Explanation |
---|---|---|---|
2 |
EOENT |
No such file or directory |
Semaphore identifier does not exist for this key , and IPC_CREAT was not set. |
12 |
ENOMEM |
Cannot allocate memory |
Insufficient system memory to allocate the semaphore set. |
13 |
EACCES |
Permission denied |
Semaphore identifier exists for this key , but requested operation is not allowed by current access permissions. |
17 |
EEXIST |
File exists |
Semaphore identifier exists for this key , but the flags IPC_CREAT and IPC_EXCL are both set. |
28 |
ENOSPC |
No space left on device |
System-imposed limit (SEMMNI) for the number of semaphore sets or systemwide maximum number of semaphores (SEMMNS) has been reached. |
43 |
EIDRM |
Identifier removed |
Specified semaphore set is marked for removal. |
Program 7.1 attempts to create several semaphore sets, each containing three semaphores.
Program 7.1 Creating semaphore sets.
File : p7.1.cxx /* Creating sets of semaphores */ #include #include #include + #include #include using namespace std; int main( ){ 10 int sem1, sem2, sem3; key_t ipc_key; ipc_key = ftok(".", 'S'); if ((sem1 = semget(ipc_key, 3, IPC_CREAT 0666)) == -1) { cerr << "semget: IPC_CREAT 0666" << endl; + } cout << "sem1 identifier " << sem1 << endl; if ((sem2 = semget(ipc_key, 3, IPC_CREATIPC_EXCL0666)) == -1) { cerr << "semget: IPC_CREAT IPC_EXCL 0666" << endl; } 20 cout << "sem2 identifier " << sem2 << endl; if ((sem3 = semget(IPC_PRIVATE, 3, 0600)) == -1) { cerr << "semget: IPC_PRIVATE" << endl; } cout << "sem3 identifier " << sem3 << endl; + return 0; }
The first call to semget , provided the system limits have not been reached, creates a set of three semaphores. The permissions for the set are read and alter (write) for the owner, group, and others (world). The value of the semaphore identifier is tied to the key value that is produced by the call to ftok . The second call to semget attempts to create a second set of three semaphores. The call uses the same key value as the first but includes the specification IPC_EXCL. With the IPC_EXCL flag set and the previous successful creation of the semaphore set using the same key value, this invocation of semget will fail. The third call to semget creates a three-semaphore set used by specifying IPC_PRIVATE instead of using the ftok key value. The semaphore identifier generated for this set will be private to this process.
When the program is run twice consecutively, the output generated will be similar to that shown in Figure 7.2.
Figure 7.2 Two consecutive runs of Program 7.1.
linux$ p7.1 sem1 identifier 9797637 semget: IPC_CREAT IPC_EXCL 0666: File exists sem2 identifier -1 sem3 identifier 9830406 linux$ p7.1 sem1 identifier 9797637 semget: IPC_CREAT IPC_EXCL 0666: File exists sem2 identifier -1 sem3 identifier 9863175
Notice that when the program is run the second time, the same semaphore identifier (9797637) is returned from the initial call to semget . Without the IPC_EXCL flag, the semget system call will not fail if the semaphore set has already been created, but will instead return the associated semaphore identifier. The creation of a second private semaphore set by the second invocation of the program produces another unique semaphore identifier (9863175), which is different from the first private semaphore identifier (9830406). The output of the ipcs command, shown in Figure 7.3, verifies the presence and permissions of the three semaphore sets that were created by the user gray . Notice that the key for each of the private semaphore sets is 0.
Figure 7.3 ipcs output.
linux$ ipcs -s -----Semaphore Arrays------ key semid owner perms nsems status 0x53157f08 9797637 gray 666 3 0x00000000 9830406 gray 600 3 0x00000000 9863175 gray 600 3
As written, Program 7.1 does not remove the semaphore sets it creates. Semaphores, like message queues, are a limited resource. In a programming setting, semaphores can be removed with the semctl system call (see the following section). Semaphores may also be removed at the command-line level using the ipcrm command (as discussed in Chapter 6, Section 6.1). If there are several semaphores to remove, a shell script, such as that shown in Program 7.2, can be used to automate the removal process.
Program 7.2 A Korn shell script to remove all semaphores for a user.
File : clean #!/bin/ksh # # Korn Shell script to remove all existing semaphores for a user # + list=$(ipcs -s grep "$USER" cut -d' ' -f2) count=0 for semaphore in $list do ipcrm sem $semaphore > /dev/null 10 ((count=count+1)) done print "$count semaphore(s) for $USER removed"
Programs and Processes
Processing Environment
Using Processes
Primitive Communications
Pipes
Message Queues
Semaphores
Shared Memory
Remote Procedure Calls
Sockets
Threads
Appendix A. Using Linux Manual Pages
Appendix B. UNIX Error Messages
Appendix C. RPC Syntax Diagrams
Appendix D. Profiling Programs