|
SYSTEM CALL: semctl(); PROTOTYPE: int semctl ( int semid, int semnum, int cmd, union semun arg ); RETURNS: positive integer on success -1 on error: errno = EACCESS (permission denied) EFAULT (invalid address pointed to by arg argument) EIDRM (semaphore set was removed) EINVAL (set doesn't exist, or semid is invalid) EPERM (EUID has no privileges for cmd in arg) ERANGE (semaphore value out of range) NOTES: Performs control operations on a semaphore set
Both system calls utilize a cmd argument, for specification of the command to be performed on the IPC object. The remaining difference lies in the final argument to both calls. In msgctl, the final argument represents a copy of the internal data structure used by the kernel. Recall that we used this structure to retrieve internal information about a message queue, as well as to set or change permissions and ownership of the queue. With semaphores, additional operational commands are supported, thus requiring a more complex data type as the final argument. The use of a union confuses many neophyte semaphore programmers to a substantial degree. We will dissect this structure carefully, in an effort to prevent any confusion.
The first argument to semctl() is the key value (in our case returned by a call to semget). The second argument (semun) is the semaphore number that an operation is targeted towards. In essence, this can be thought of as an index into the semaphore set, with the first semaphore (or only one) in the set being represented by a value of zero (0).
The cmd argument represents the command to be performed against the set. As you can see, the familiar IPC_STAT/IPC_SET commands are present, along with a wealth of additional commands specific to semaphore sets:
Retrieves the semid_ds structure for a set, and stores it in the address of the buf argument in the semun union.
Sets the value of the ipc_perm member of the semid_ds structure for a set. Takes the values from the buf argument of the semun union.
Removes the set from the kernel.
Used to obtain the values of all semaphores in a set. The integer values are stored in an array of unsigned short integers pointed to by the array member of the union.
Returns the number of processes currently waiting for resources.
Returns the PID of the process which performed the last semop call.
Returns the value of a single semaphore within the set.
Returns the number of processes currently waiting for 100% resource utilization.
Sets all semaphore values with a set to the matching values contained in the array member of the union.
Sets the value of an individual semaphore within the set to the val member of the union.
The arg argument represents an instance of type semun. This particular union is declared in linux/sem.h as follows:
/* arg for semctl system calls. */ union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ ushort *array; /* array for GETALL & SETALL */ struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; };
Used when the SETVAL command is performed. Specifies the value to set the semaphore to.
Used in the IPC_STAT/IPC_SET commands. Represents a copy of the internal semaphore data structure used in the kernel.
A pointer used in the GETALL/SETALL commands. Should point to an array of integer values to be used in setting or retrieving all semaphore values in a set.
The remaining arguments __buf and __pad are used internally in the semaphore code within the kernel, and are of little or no use to the application developer. As a matter of fact, these two arguments are specific to the Linux operating system, and are not found in other UNIX implementations.
Since this particular system call is arguably the most difficult to grasp of all the System V IPC calls, we'll examine multiple examples of it in action.
The following snippet returns the value of the passed semaphore. The final argument (the union) is ignored when the GETVAL command is used:
int get_sem_val( int sid, int semnum ) { return( semctl(sid, semnum, GETVAL, 0)); }
#define MAX_PRINTERS 5 printer_usage() { int x; for(x=0; x<MAX_PRINTERS; x++) printf("Printer %d: %d\n\r", x, get_sem_val( sid, x )); }
void init_semaphore( int sid, int semnum, int initval) { union semun semopts; semopts.val = initval; semctl( sid, semnum, SETVAL, semopts); }
Recall from the msgtool project that the IPC_STAT and IPC_SET commands were used to alter permissions on the queue. While these commands are supported in the semaphore implementation, their usage is a bit different, as the internal data structure is retrieved and copied from a member of the union, rather than as a single entity. Can you locate the bug in this code?
/* Required permissions should be passed in as text (ex: "660") */ void changemode(int sid, char *mode) { int rc; struct semid_ds mysemds; /* Get current values for internal data structure */ if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1) { perror("semctl"); exit(1); } printf("Old permissions were %o\n", semopts.buf->sem_perm.mode); /* Change the permissions on the semaphore */ sscanf(mode, "%o", &semopts.buf->sem_perm.mode); /* Update the internal data structure */ semctl(sid, 0, IPC_SET, semopts); printf("Updated...\n"); }
Recall that the IPC_SET/IPC_STAT commands use the buf member of the union, which is a pointer to a type semid_ds. Pointers are pointers are pointers are pointers! The buf member must point to some valid storage location in order for our function to work properly. Consider this revamped version:
void changemode(int sid, char *mode) { int rc; struct semid_ds mysemds; /* Get current values for internal data structure */ /* Point to our local copy first! */ semopts.buf = &mysemds; /* Let's try this again! */ if((rc = semctl(sid, 0, IPC_STAT, semopts)) == -1) { perror("semctl"); exit(1); } printf("Old permissions were %o\n", semopts.buf->sem_perm.mode); /* Change the permissions on the semaphore */ sscanf(mode, "%o", &semopts.buf->sem_perm.mode); /* Update the internal data structure */ semctl(sid, 0, IPC_SET, semopts); printf("Updated...\n"); }
Hosting by: Hurra Communications Ltd.
Generated: 2007-01-26 17:57:43