Semaphore Control

The semget system call (Table 7.3) is used to create or gain access to a set of semaphores. The semctl system call allows the user to perform a variety of generalized control operations on the system semaphore structure, on the semaphores as a set, and on individual semaphores. Additional manipulative operations on specific semaphores within a set are covered in the following section on semaphore operations (Section 7.4).

Table 7.3. Summary of the semctl System Call.

Include File(s)

 

Manual Section

2

Summary

int semctl(int semid, int semnum, int cmd, union semun arg);

Return

Success

Failure

Sets errno

0 or the value requested

-1

Yes

The semctl system call takes four arguments. The first argument, semid , is a valid semaphore identifier that was returned by a previous semget system call. The second argument, semnum , is the number of semaphores in the semaphore set. In most cases, this value is greater than 0 but less than the system limit. However, we will see occasions when the value for semnum is set to 0. These occasions arise when we ask semctl to perform an operation for which the number of semaphores in the set is not relevant. The third argument to semctl , cmd , is an integer command value (usually expressed as one of the symbolic constants found in the header files or ). As discussed in detail in Section 7.3.1, "Semaphore Control Details," the cmd value directs semctl to take one of several control actions. Each action requires specific access permissions to the semaphore control structure (i.e., read or alter ). The fourth argument to semctl , arg , is a union of type semun . Given the action specified by the preceding cmd argument, the data in arg can be one of any of the following four values:

  1. An integer used with SETVAL to indicate a specific value for a particular semaphore within the semaphore set.
  2. A reference to a semid_ds structure where information is returned when IPC_STAT or IPC_SET is specified.
  3. A reference to an array of type unsigned short integers; the array is used either to initialize the semaphore set (such as when stipulating SETALL) or as a return location when specifying GETALL.
  4. A reference to a seminfo structure when IPC_INFO is requested.

In some versions of UNIX the definition of the semun union is found in the include files required by semctl . However, technically the user should define the union. To this end, the manual page for semctl contains the following cryptic reference.

#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) /* union semun is defined by including */ #else /* according to X/OPEN we have to define it ourselves */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array; /* array for GETALL, SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ }; #endif

This sequence of preprocessor directives determines whether the programmer defines the semun union. If __GNU_LIBRARY__ has been defined (is nonzero), and _SEM_SEMUN_UNDEFINED has not been defined, the definition of the union semun is found in the include file . Otherwise, the user defines the union. To be safe, this set of preprocessor directives should be placed at the top any program that will make use of the fourth argument of the semctl call. Its omission may cause the semctl system call to fail, returning the value EFAULT (bad address). Further, when specifying arg as the fourth argument to semctl , the value for arg should be explicitly assigned (e.g., arg.buf=ptr_to_my_structure ). This assignment must be done prior to the calling of semctl (see Program 7.3).

7.3.1 Semaphore Control Details

The following cmd values cause semctl to act upon the system semaphore structure ( semid_ds ):

When specifying IPC_STAT, IPC_SET, or IPC_RMID, the value for semnum (the number of semaphores in the set) is not considered and can be set to 0.

The following cmd values cause semctl to act upon the entire set of semaphores:

The last set of semctl cmd values acts upon individual semaphores or upon specific members in the semid_ds structure. All of these commands require read permission except for SETVAL, which requires alter permission:

If semctl is successfully issues any of these commands, the requested integer value is returned: the value of semncnt for GETNCNT, the value of sempid for GETPID, the value of semval for GETVAL, or the value of semzcnt for GETZCNT. If semctl fails, it returns a value of -1 and sets errno to indicate the specific error. The errors returned by semctl with an explanation of their meaning are shown in Table 7.4.

Table 7.4. semctl Error Messages.

#

Constant

perror Message

Explanation

1

EPERM

Operation not permitted

Value for cmd is IPC_RMID or IPC_SET and the calling process in not the owner or superuser.

13

EACCES

Permission denied

The requested operation is not allowed by the current access permissions for this process.

14

EFAULT

Bad address

The fourth argument to semctl contains a reference to an illegal address (the union semun may not have been declared).

22

EINVAL

Invalid argument

  • The semaphore identifier is invalid.
  • The number of semaphores specified is less than 0 or greater than the number in the semaphore set.
  • The value for cmd is invalid.
  • The value for cmd is IPC_SET, but the value for sem_perm. uid or sem_perm.gid is invalid.

34

ERANGE

Numerical result out of range

The value for cmd is SETVAL or SETALL, and the value to be assigned is greater than the system maximum or less than 0.

43

EIDRM

Identifier removed

Specified semaphore set is marked for removal.

Program 7.3 uses the semctl system call to perform a number of semaphore control operations.

Program 7.3 Using semctl .

File : p7.3.cxx /* Using the semctl system call */ #include + #include #include #include #include #define NS 3 <-- 1 10 #if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) // definition in #else union semun { // We define: int val; // value for SETVAL + struct semid_ds *buf; // buffer for IPC_STAT, IPC_SET unsigned short int *array; // array for GETALL, SETALL struct seminfo *__buf; // buffer for IPC_INFO }; #endif 20 using namespace std; int main( ){ int sem_id, sem_value, i; key_t ipc_key; + struct semid_ds sem_buf; unsigned short int sem_array[NS] = {3, 1, 4}; union semun arg; ipc_key = ftok(".", 'S'); 30 if ((sem_id = semget(ipc_key, NS, IPC_CREAT 0660)) == -1) { perror("semget: IPC_CREAT 0660"); return 1; } cout << "Semaphore identifier " << sem_id << endl; + arg.buf = &sem_buf; <-- 2 if (semctl(sem_id, 0, IPC_STAT, arg) == -1) { perror("semctl: IPC_STAT"); return 2; } 40 cout << "Created " << ctime(&sem_buf.sem_ctime) << endl; arg.array = sem_array; <-- 3 if (semctl(sem_id, 0, SETALL, arg) == -1) { perror("semctl: SETALL"); return 3; + } for (i = 0; i < NS; ++i) { if ((sem_value = semctl(sem_id, i, GETVAL, 0)) == -1) { perror("semctl: GETVAL"); return 4; 50 } cout << "Semaphore " << i << " has value of " << sem_value << endl; } if (semctl(sem_id, 0, IPC_RMID, 0) == -1) { perror("semctl: IPC_RMID"); + return 5; } return 0; }

(1) Do we need to define semun union?

(2) Set arg to be the address of the storage for the returned values.

(3) Set arg to be the address of the initializing vector.

Program 7.3 creates a set of three semaphores. The semaphore identifier for the set is printed. In line 35, the address of sem_buf is assigned to the appropriate member of arg . The union arg now contains the location where the returned data will be stored. Then, by specifying IPC_STAT and passing the proper address, semctl obtains the current values of the system semaphore structure. The date and time the semaphore was created are displayed using the library function ctime . Using similar syntax, other members of the semid_ds structure could be displayed. However, there is another way to obtain the entire contents of the semid_ds structure (albeit on a temporary basis). To do this, compile Program 7.3 with the -g option and then use the debugger, gdb , to examine the semid_ds structure. This can be accomplished by invoking gdb with the executable program name , such as linux$ gdb p7.3 . When in gdb , direct gdb to stop at the correct line (say, break 40 ). The program is then run, and when gdb stops at line 40, it is asked to print the contents of the structure using the gdb command: print sem_buf . The output of such a sequence will display the contents of the entire sem_buf structure. On our system, Program 7.3 run in gdb produces the output shown in Figure 7.4:

Figure 7.4 dbx output of Program 7.3.

linux$ g++ -g -o p7.3 p7.3.cxx <-- 1 linux$ gdb p7.3 GNU gdb 5.0rh-5 Red Hat Linux 7.1 Copyright 2001 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux"... (gdb) break 40 <-- 2 Breakpoint 1 at 0x8048890: file p7.3.cxx, line 40. (gdb) run Starting program: /home/faculty/gray/revision/07/p7.3 Semaphore identifier 10027013 Breakpoint 1, main () at p7.3.cxx:40 40 cout << "Created " << ctime(&sem_buf.sem_ctime) << endl; Current language: auto; currently c++ (gdb) print sem_buf = {sem_perm = {__key = 1393917704, uid = 500, gid = 1000, cuid = 500, cgid = 1000, mode = 432, __pad1 = 0, __seq = 306, __pad2 = 0, __unused1 = 0, __unused2 = 0}, sem_otime = 0, __unused1 = 0, sem_ctime = 1016545082, __unused2 = 0, sem_nsems = 3 , __unused3 = 0, __unused4 = 0} (gdb)

(1) Compile the program with the g option.

(2) Stop at line 40 of the program.

Notice, as would be expected, that the number of semaphores in the set, three, has been stored in the sem_nsems member.

Program 7.3 uses the semctl system call to initialize the three-semaphore set to the values stored in the array sem_array . Again, notice that prior to calling semctl the address of the initializing vector (see line 41) is assigned to the proper member of arg . Once the values are assigned to the semaphore set, the program uses a loop to display to the screen the value stored in each semaphore. The last action of Program 7.3 is to use the semctl system call with the IPC_RMID flag to remove the semaphore set.

When run outside of gdb , the output of Program 7.3 should be similar to that shown in Figure 7.5.

Figure 7.5 Output of Program 7.3.

linux$ p7.3 Semaphore identifier 10027013 Created Tue Mar 19 08:38:02 2002 Semaphore 0 has value of 3 Semaphore 1 has value of 1 Semaphore 2 has value of 4

EXERCISE

After generating a set of, say, three semaphores, can semctl be used to alter the values of sem_nsems to indicate an increase or decrease in the number of semaphores in a set? Is this a bug or a feature? Provide a program segment that supports your answer

EXERCISE

In earlier IPC implementations , the base address of the semaphore set was stored in the member sem_base . In current versions, the user does not have access to this address. Why do you suppose the developers removed the ability to access the semaphore set directly?

Категории