The previous sections showed how semaphore sets could be created and accessed. This section will show you how you can query the set that you have and make changes to it.
The semctl(2) function provides the IPC_STAT command to allow you to retrieve information about the semaphore set. Of particular interest is the value that indicates how many semaphores are in the set and the permission information. Information for IPC_STAT is returned in the structure semid_ds, which is shown in the following synopsis:
struct semid_ds { struct ipc_perm sem_perm; /* operation permission struct */ struct sem *sem_base; /* pointer to first semaphore in set */ u_short sem_nsems; /* number of sems in set */ time_t sem_otime; /* last operation time */ time_t sem_ctime; /* last change time */ };
The structure definition for ipc_perm is repeated for your convenience, as follows:
struct ipc_perm { ushort cuid; /* creator user id */ ushort cgid; /* creator group id */ ushort uid; /* user id */ ushort gid; /* group id */ ushort mode; /* r/w permission */ ushort seq; /* sequence # (to generate unique msg/sem/shm id) */ key_t key; /* user specified msg/sem/shm key */ };
In order to receive this information, you must make use of the fourth argument, of type semun. The POSIX standard states that you must define the union semun in your own code. Many releases of UNIX define it for you in the include files. The union is defined in the include file semop.h for the utility, which is shown in Listing 24.3.
The union is only compiled if the C macro HAVE_SEMUN is not defined. This is defined in the Makefile as the compiler command-line argument -DHAVE_SEMUN. Remove this option if you need the union defined.
Listing 24.4 shows the how the semctl(2) function is called for the IPC_STAT command.
The section of code that is important to the discussion is found in lines 31–42. The union named un is declared in line 33. The address of the external buffer of type struct semid_ds is pointed to in the union in line 35. The union un is passed as the fourth argument to the semctl(2) function in line 37.
The struct semid_ds sembuf is declared externally in this module in line 5. The values placed here are also used by other modules that perform the IPC_SET operation.
The following example uses the utility program ./semop to create a new semaphore set, and invokes IPC_STAT on it.
$ ./semop -k0xFEEDF00D -c3 -s
-c 0xFEEDF00D => IPC ID 196608
-c 3 : Created semaphore set -k 0xFEEDF00D
-s {
sem_nsems = 3
sem_perm {
cuid = 1001 (ehg)
cgid = 1001 (ehg)
uid = 1001 (ehg)
gid = 1001 (ehg)
mode = 0640
key = 0xFEEDF00D
};
};
$
The options -k and -c create the set. Option -s requests the IPC_STAT command, and the most important values are reported to standard output. The mode value was affected by the current umask(2) in effect, due to the umask(2) calls that were made in lines 17–19 of Listing 24.1. The mask that was in effect was
$ umask
0027
$
Keep this semaphore set around for the subsequent sections.
After the semaphore set is created, it may be necessary to modify the ownership of the semaphore or change its permission bits. The IPC_SET command of the semctl(2) function allows you to make these changes. Listing 24.5 shows how IPC_SET is used.
The function ctl_chmod() is called upon to change the permission bits (mode) of the semaphore set within the utility. Again, the union is declared in line 8, and the buffer pointer is established in line 10. The permission bits in the external variable sembuf are altered in line 11. Line 13 invokes the semctl(2) function to cause the permission bit changes to occur.
The following example accesses the previous semaphore set and changes the permissions using the -m option. Note the careful use of the leading zero in 0600 to specify the value in octal notation. The -s option follows to display the new values for this set:
$ ./semop -k0xFEEDF00D -a -m 0600 -s
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-m 0600
-s {
sem_nsems = 3
sem_perm {
cuid = 1001 (ehg)
cgid = 1001 (ehg)
uid = 1001 (ehg)
gid = 1001 (ehg)
mode = 0600
key = 0xFEEDF00D
};
};
$
Indeed the output shows that the new mode value is 0600. Additional source code for the utility that allows the owner and group to be altered is shown in Listing 24.6.
The functions ctl_chown() and ctl_chgrp() are called for the utility options -x and -y, respectively. The actual changes are made in lines 23 and 47. The function calls in lines 26 and 50 cheat by pretending to change the mode value, but pass the existing mode value instead. This causes the changes in variable sembuf to be written to the semaphore set using IPC_SET.
The semctl(2) command GETVAL allows a program to query the current value of a specific semaphore within a set. Listing 24.7 shows a source module that performs this function for the semop utility program.
The ctl_getval() function is called by the -g n option of the utility. The value n represents the zero-based semaphore number within the set, which is passed to ctl_getval() in the argument semx in Listing 24.7. The semctl(2) call is made in line 10. Since the semaphore value cannot be negative, the value -1 represents an error return value, which is tested for in line 11. Otherwise, the semaphore number and its value are reported in line 16.
The following example reports the values of semaphore 0 and semaphore 2:
$ ./semop -k0xFEEDF00D -a -g0 -g2
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-g 0 => 0
-g 2 => 0
$
Querying any semaphore is a snapshot view of the values, since these values could be changing. If you need a snapshot of all of the semaphores, the GETALL control function provides a consistent result. Listing 24.8 shows how the GETALL command is used.
The function ctl_getall() is called when the ./semop option -G is encountered. An array is declared in line 9 to receive all of the semaphore values. The semun union is pointed to this array in line 12. The GETALL command is invoked in line 13. If the call succeeds, the results are reported in lines 19 and 20.
The following shows GETALL in action:
$ ./semop -k0xFEEDF00D -a -G
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-G : semaphore # 0 = 0
-G : semaphore # 1 = 0
-G : semaphore # 2 = 0
$
Since you have not yet initialized this set of semaphores with values, you know that these are the default values for new semaphores under FreeBSD. Some UNIX platforms use different defaults, however.
Sometimes it is necessary to adjust a specific semaphore to a specific value. This can be accomplished with the semctl(2) SETVAL command, as you can see in Listing 24.9.
The ctl_setval() function is invoked when the -v n=x option is encountered. The semaphore number n is passed as the argument semx, while the value x is passed as the argument value. The union has the value assigned to its val member, which is then passed in the call in line 11. An example of the SETVAL being used is shown as follows:
$ ./semop -k0xFEEDF00D -a -v2=13 -G
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-v 2=13
-G : semaphore # 0 = 0
-G : semaphore # 1 = 0
-G : semaphore # 2 = 13
$
Using the -v option, semaphore 2 in the set was assigned the new value of 13. This was reported by the -G option, which followed on the command line.
The semctl(2) function allows the application writer to establish the values of all semaphores in one atomic operation, using the SETALL command. This command is used in Listing 24.10.
The ./semop utility calls ctl_setall() when the option -V is encountered. This option is followed by a comma-separated list of initial values for the entire semaphore set. The number of values must exactly match the set. The array of values is passed in the argument array in line 6. A conversion from int type to u_short type is made in lines 12 and 13 for the purpose of the semctl(2) call in line 16. The address of the array is established in the union member array (line 15).
The following example changes all three semaphores in the existing set that you have been using:
$ ./semop -k0xFEEDF00D -a -V9,8,7 -G
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-V 9,8,7
-G : semaphore # 0 = 9
-G : semaphore # 1 = 8
-G : semaphore # 2 = 7
$
The option -V9,8,7 sets the values for semaphores 0, 1, and 2 in the set of three.
When you are debugging a complex set of applications that modify a set of semaphores, it is sometimes useful to be able to determine which process was the last one to modify a semaphore. The GETPID command of the semctl(2) system call performs this function. The source code in Listing 24.11 combines code that uses GETPID and some other information return commands.
The command GETPID is passed in the argument cmd of function ctl_get() in line 6 when the -p or -P options are used on the command line. The specific semaphore number is specified in the argument semx, which is later used in the call to semctl(2) in line 10. Since the value being returned for all acceptable commands cannot be negative, the value -1 still identifies an error return in line 11. The returned value is reported in line 19, when the semctl(2) call is successful.
The following example shows how to report the process ID for semaphore number 2 and 0 using the -p option:
$ ./semop -k0xFEEDF00D -a -p2 -p0
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-p 2 => 2347
-p 0 => 2346
$
If you are following along and doing these commands on your system, you may see zeros reported instead. Zero values indicate that no process has done a wait, zero, or notify operation on your semaphore (you will do this later in the chapter). This example was preceded by a few semaphore operations to provide non-zero process ID results.
The following example uses the -P convenience option to invoke GETPID on each semaphore in the set:
$ ./semop -k0xFEEDF00D -a -P
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-p 0 => 2346
-p 1 => 2348
-p 2 => 2347
$
The GETNCNT command allows your process to query how many processes are waiting on a particular semaphore. Some applications may be able to make use of this information in order to gauge the workload being processed.
The GETNCNT requests are invoked with the -n option. The module shown in Listing 24.11 handles this information request. The following command lists how many processes are waiting on the third semaphore (semaphore number 2):
$ ./semop -k0xFEEDF00D -a -n2
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-n => 0
$
The returned value of zero indicates that no processes are currently waiting on this semaphore.
In addition to waiting and notifying semaphores, a process can also perform a wait for zero operation. This might be used to report that a particular resource is exhausted, for example. The GETZCNT command allows the caller to determine how many processes are waiting for zero on a particular semaphore. The GETZCNT operation is also handled by the code shown in Listing 24.11. The following example shows how the -z option is used to report the number of processes waiting for zero on semaphore 0:
$ ./semop -k0xFEEDF00D -a -z0
-a 0xFEEDF00D => IPC ID 196608
There are 3 semaphores in this set.
-z => 0
$
In this example, there are no processes waiting for a zero on semaphore 0 of this set.
18.190.219.65