Contact Session - 9
IPC Using Semaphores
SYSTEM V SEMAPHORES
• System V semaphores are not used to transfer data
between processes. Instead, they allow processes
to synchronize their actions.
• One common use of a semaphore is to synchronize
access to a block of shared memory, in order to
prevent one process from accessing the shared
memory at the same time as another process is
updating it.
• A semaphore is a kernel-maintained integer whose
value is restricted to greater than or equal to 0.
Various operations (i.e., system calls) can be performed
on a semaphore, including the following:
• setting the semaphore to an absolute value;
• adding a number to the current value of the
semaphore;
• subtracting a number from the current value of
the semaphore; and
• waiting for the semaphore value to be equal to
0.
Note: The last two of these operations may
cause the calling process to block.
• When lowering a semaphore value, the kernel
blocks any attempt to decrease the value below
0.
• Similarly, waiting for a semaphore to equal 0
blocks the calling process if the semaphore
value is not currently 0.
• In both cases, the calling process remains
blocked until some other process alters the
semaphore to a value that allows the operation
to proceed, at which point the kernel wakes the
blocked process.
Following Figure Shows
the use of a semaphore to synchronize the actions of two
processes that alternately
move the semaphore value between 0 and 1.
The general steps for using a System V
semaphore are the following:
• Create or open a semaphore set using semget().
• Initialize the semaphores in the set using the semctl()
SETVAL or SETALL operation. (Only one process should do
this.)
• Perform operations on semaphore values using semop().
The processes using the semaphore typically use this
operation to indicate acquisition and release of a shared
resource.
• When all processes have finished using the semaphore set,
remove the set using the semctl() IPC_RMID operation.
(Only one process should do this.)
• The number of semaphores in a set is specified
when the set is created using the semget()
system call.
• It is common to operate on a single semaphore
at a time, but the semop() system call allows
us to atomically perform a group of operations
on multiple semaphores in the same set.
A simple example of the use of the
various semaphore system calls.
• This program operates in two modes:
Given a single integer command-line argument, the program
creates a new semaphore set containing a single semaphore, and
initializes the semaphore to the value supplied in the command-
line argument. The program displays the identifier of the new
semaphore set.
Given two command-line arguments, the program interprets them
as (in order) the identifier of an existing semaphore set and a
value to be added to the first semaphore (numbered 0) in that
set. The program carries out the specified operation on that
semaphore. To enable us to monitor the semaphore operation,
the program prints messages before and after the operation. Each
of these messages begins with the process ID, so that we can
distinguish the output of multiple instances of the program.
Creating and operating on System V semaphores
–––––––––––––––––––––––––––––––––––svsem/svsem_demo.c
include <sys/types.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include<time.h>
#include "tlpi_hdr.h"
union semun
{
/* Used in calls to semctl() */
int val;
struct semid_ds * buf;
unsigned short * array;
#if defined(__linux__)
struct seminfo * __buf;
#endif
};
int main(int argc, char *argv[])
{
int semid;
if (argc < 2 || argc > 3 || strcmp(argv[1], "--help") == 0)
{
printf("%s init-value or: %s semid operation\n", argv[0], argv[0]);
exit(0);
}
if (argc == 2)
{
/* Create and initialize semaphore */
union semun arg;
semid = semget(IPC_PRIVATE, 1, S_IRUSR | S_IWUSR);
if (semid == -1)
{
perror("semid:");
exit(0);
}
[Link] = atoi(argv[1]);
if (semctl(semid, /* semnum= */ 0, SETVAL, arg) == -1)
{
perror("semctl:");
exit(0);
}
printf("Semaphore ID = %d\n", semid);
}
else
{
/* Perform an operation on first semaphore */
struct sembuf sop; /* Structure defining operation */
semid = atoi(argv[1]);
sop.sem_num = 0; /* Specifies first semaphore in set */
sop.sem_op = atoi(argv[2]);
/* Add, subtract, or wait for 0 */
sop.sem_flg = 0; /* No special options for operation */
time_t curtime1;
time(&curtime1);
printf("%ld: about to semop at %s\n", (long) getpid(),
ctime(&curtime1));
if (semop(semid, &sop, 1) == -1)
{
perror("semop:");
exit(0);
}
time_t curtime2;
time(&curtime2);
printf("%ld: semop completed at %s\n", (long) getpid(),
ctime(&curtime1));
}
exit(0);
}
The following shell session log demonstrates the
use of the above program
• $ ./svsem_demo 0
Semaphore ID = 98307 //ID of new semaphore set
• We then execute a background command that tries to decrease the semaphore
value by 2:
• $ ./svsem_demo 98307 -2 &
23338: about to semop at [Link]
[1] 23338
• This command blocked, because the value of the semaphore can’t be decreased
below 0. Now, we execute a command that adds 3 to the semaphore value:
• $ ./svsem_demo 98307 +3
23339: about to semop at [Link]
23339: semop completed at [Link]
23338: semop completed at [Link]
[1]+ Done ./svsem_demo 98307 -2
• The semaphore increment operation succeeded immediately, and caused the semaphore
decrement operation in the background command to proceed, since that operation could
now be performed without leaving the semaphore’s value below 0.
Creating or Opening a Semaphore Set
• The semget() system call creates a new
semaphore set or obtains the identifier of an
existing set.
#include <sys/types.h> /* For portability */
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
Returns semaphore set identifier on success, or –
1 on error
• First Argument: The key argument is a key generated
using one of the methods described in previous
section (i.e., usually the value IPC_PRIVATE or a key
returned by ftok()).
• Second Argument:
• If we are using semget() to create a new semaphore
set, then nsems specifies the number of semaphores
in that set, and must be greater than 0.
• If we are using semget() to obtain the identifier of an
existing set, then nsems must be less than or equal to
the size of the set (or the error EINVAL results). It is
not possible to change the number of semaphores in
an existing set.
• Third Argument: The semflg argument is used to
specifying the permissions to be placed on a new
semaphore set or checked against an existing set.
These permissions are specified in the same manner
as for files. In addition, zero or more of the following
flags can be ORed (|) in semflg to control the
operation of semget():
IPC_CREAT
If no semaphore set with the specified key
exists, create a new set.
IPC_EXCL
If IPC_CREAT was also specified, and a
semaphore set with the specified key already exists,
fail with the error EEXIST.
• On success, semget() returns the identifier for
the new or existing semaphore set.
• Subsequent system calls referring to individual
semaphores must specify both the semaphore
set identifier and the number of the
semaphore within that set.
• The semaphores within a set are numbered
starting at 0.
Semaphore Control Operations
• The semctl() system call performs a variety of control
operations on a semaphore set or on an individual
semaphore within a set.
#include <sys/types.h> /* For portability */
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ... /* union
semun arg */);
Returns nonnegative integer on success (see text);
returns –1 on error
• First Argument: The semid argument is the identifier
of the semaphore set on which the operation is to be
performed.
• Second Argument: For those operations performed
on a single semaphore, the semnum argument
identifies a particular semaphore within the set. For
other operations, this argument is ignored, and we
can specify it as 0.
• Third Argument: The cmd argument specifies the
operation to be performed.
• Fourth Argument: Certain operations require a fourth
argument to semctl(), which we refer to by the name
arg. This argument is a union as show in the semun.h
file. We must explicitly define this union in our
Definition of the semun union
–––––––––––––––––––––––––––– semun.h
#ifndef SEMUN_H
#define SEMUN_H /* Prevent accidental double inclusion */
#include <sys/types.h> /* For portability */
#include <sys/sem.h>
union semun { /* Used in calls to semctl() */
int val;
struct semid_ds * buf;
unsigned short * array;
#if defined(__linux__)
struct seminfo * __buf;
#endif
};
#endif
• The various control operations that can be specified
for cmd.
• The following operations are the same ones that
can be applied to other types of System V IPC
objects. In each case, the semnum argument is
ignored.
IPC_RMID:
Immediately remove the semaphore set and
its associated semid_ds data structure. Any
processes blocked in semop() calls waiting
on semaphores in this set are immediately
awakened, with semop() reporting the error
EIDRM. The arg argument is not required.
IPC_STAT
Places a copy of the semid_ds data
structure associated with this semaphore set
in the buffer pointed to by [Link]. We
describe the semid_ds structure in next
Section.
IPC_SET
Update selected fields of the semid_ds
data structure associated with this semaphore
set using values in the buffer pointed to by
[Link].
Retrieving and initializing semaphore
values
• The following operations retrieve or initialize the
value(s) of an individual semaphore or of all
semaphores in a set. Retrieving a semaphore value
requires read permission on the semaphore, while
initializing the value requires alter (write)
permission.
GETVAL
semctl() returns the value of the semnum-th
semaphore in the semaphore set specified by
semid. The arg argument is not required.
SETVAL
The value of the semnum-th semaphore in the
set referred to by semid is initialized to the value
specified in [Link].
GETALL
Retrieve the values of all of the semaphores in
the set referred to by semid, placing them in the
array pointed to by [Link]. The programmer must
ensure that this array is of sufficient size. (The
number of semaphores in a set can be obtained from
the sem_nsems field of the semid_ds data structure
retrieved by an IPC_STAT operation.) The semnum
argument is ignored i,e it is made as 0)
SETALL
Initialize all semaphores in the set
referred to by semid, using the values supplied
in the array pointed to by [Link]. The
semnum argument is ignored.
Retrieving per-semaphore information
The following operations return (via the
function result value) information about the
semnum-th semaphore of the set referred to by
semid. For all of these operations, read
permission is required on the semaphore set,
and the arg argument is not required.
GETPID
Return the process ID of the last process to
perform a semop() on this semaphore; this is referred
to as the sempid value. If no process has yet performed
a semop() on this semaphore, 0 is returned.
GETNCNT
Return the number of processes currently
waiting for the value of this semaphore to increase;
this is referred to as the semncnt value.
GETZCNT
Return the number of processes currently
waiting for the value of this semaphore to become 0;
this is referred to as the semzcnt value.
• Note: As with the GETVAL and GETALL operations described above,
the information returned by the GETPID, GETNCNT, and GETZCNT
operations may already be out of date by the time the calling
process comes to use it.
• Semaphore Associated Data Structure
struct semid_ds
{
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Time of last semop() */
time_t sem_ctime; /* Time of last change */
unsigned long sem_nsems;
/* Number of semaphores in set */
};
• The fields of the semid_ds structure are implicitly updated by various
semaphore system calls, and certain subfields of the sem_perm field
can be explicitly updated using the semctl() IPC_SET operation.
sem_perm
When the semaphore set is created, the fields
of this substructure are initialized as described
previous section. The uid, gid, and mode subfields
can be updated via IPC_SET.
sem_otime
This field is set to 0 when the semaphore set is
created, and then set to the current time on each
successful semop(), or when the semaphore value is
modified as a consequence of a SEM_UNDO
operation. This field and sem_ctime are typed as
time_t, and store time in seconds since the Epoch.
sem_ctime
This field is set to the current time when
the semaphore set is created and on each
successful IPC_SET, SETALL, or SETVAL
operation. (On some UNIX implementations,
the SETALL and SETVAL operations don’t
modify sem_ctime.)
sem_nsems
When the set is created, this field is
initialized to the number of semaphores in the
set.
Monitoring a semaphore set
• The program makes use of various semctl()
operations to display information about the
existing semaphore set whose identifier is
provided as its command-line argument. The
program first displays the time fields from the
semid_ds data structure. Then, for each
semaphore in the set, the program displays
the semaphore’s current value, as well as its
sempid, semncnt, and semzcnt values.
A semaphore monitoring program
––––––––––––––– svsem/svsem_mon.c
#include <sys/types.h>
#include <sys/sem.h>
#include <time.h>
#include "semun.h" /* Definition of semun union */
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
struct semid_ds ds;
union semun arg, dummy; /* Fourth argument for semctl() */
int semid, j;
if (argc != 2 || strcmp(argv[1], "--help") == 0)
{
printf("%s semid\n", argv[0]);
exit(0);
}
semid = atoi(argv[1]);
[Link] = &ds;
if (semctl(semid, 0, IPC_STAT, arg) == -1)
{
perror("semctl:");
exit(0);
}
printf("Semaphore changed: %s", ctime(&ds.sem_ctime));
printf("Last semop(): %s", ctime(&ds.sem_otime));
/* Display per-semaphore information */
[Link] = calloc(ds.sem_nsems, sizeof([Link][0]));
if ([Link] == NULL)
{
perror("calloc:");
exit(0);
}
if (semctl(semid, 0, GETALL, arg) == -1)
{
perror("semctl-GETALL");
exit(0);
}
printf("Sem # Value SEMPID SEMNCNT SEMZCNT\n");
for (j = 0; j < ds.sem_nsems; j++)
printf("%3d %5d %5d %5d %5d\n", j, [Link][j],
semctl(semid, j, GETPID, dummy),
semctl(semid, j, GETNCNT, dummy),
semctl(semid, j, GETZCNT, dummy));
exit(0);
}
Initializing all semaphores in a set
• The program provides a command-line
interface for initializing all of the semaphores
in an existing set. The first command-line
argument is the identifier of the semaphore
set to be initialized. The remaining command-
line arguments specify the values to which the
semaphores are to be initialized (there must
be as many of these arguments as there are
semaphores in the set).
Using the SETALL operation to initialize a System V
semaphore set
––––––––––––––– svsem/svsem_setall.c
#include <sys/types.h>
#include <sys/sem.h>
#include "semun.h" /* Definition of semun union */
#include "tlpi_hdr.h"
int
main(int argc, char *argv[])
{
struct semid_ds ds;
union semun arg; /* Fourth argument for semctl() */
int j, semid;
if (argc < 3 || strcmp(argv[1], "--help") == 0)
{
printf("%s semid val...\n", argv[0]);
exit(0);
}
semid = atoi(argv[1]);
/* Obtain size of semaphore set */
[Link] = &ds;
if (semctl(semid, 0, IPC_STAT, arg) == -1)
{
printf("semctl");
exit(0);
}
if (ds.sem_nsems != argc - 2)
{
printf("Set contains %ld semaphores, but %d values were supplied\n",
(long) ds.sem_nsems, argc - 2);
exit(0);
}
/* Set up array of values; perform semaphore initialization */
[Link] = calloc(ds.sem_nsems, sizeof([Link][0]));
if ([Link] == NULL)
{
perror("calloc:");
exit(0);
}
for (j = 2; j < argc; j++)
[Link][j - 2] = atoi(argv[j]);
if (semctl(semid, 0, SETALL, arg) == -1)
{
perror("semctl-SETALL:");
exit(0);
}
printf("Semaphore values changed (PID=%ld)\n", (long) getpid());
exit(0);
}
Semaphore Initialization
• The programmer must explicitly initialize the semaphores
using the semctl() system call.
• Semaphore creation and initialization must be performed by
separate system calls, leads to possible race conditions when
initializing a semaphore. we detail the nature of the race
here.
• Suppose that we have an application consisting of multiple
peer processes employing a semaphore to coordinate their
actions.
• Since no single process is guaranteed to be the first to use
the semaphore (this is what is meant by the term peer), each
process must be prepared to create and initialize the
semaphore if it doesn’t already exist.
Race Problem is demonstrated using
following program.
Incorrectly initializing a System V semaphore
––––––––– from svsem/svsem_bad_init.c
/* Create a set containing 1 semaphore */
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms);
if (semid != -1) { /* Successfully created the semaphore */
union semun arg;
/* XXXX */
[Link] = 0; /* Initialize semaphore */
if (semctl(semid, 0, SETVAL, arg) == -1)
errExit("semctl");
}
else { /* We didn't create the semaphore */
if (errno != EEXIST) { /* Unexpected error from semget() */
errExit("semget");
semid = semget(key, 1, perms); /* Retrieve ID of existing set */
if (semid == -1)
errExit("semget");
}
/* Now perform some operation on the semaphore */
sops[0].sem_op = 1; /* Add 1... */
sops[0].sem_num = 0; /* to semaphore 0 */
sops[0].sem_flg = 0;
if (semop(semid, sops, 1) == -1)
errExit("semop")
• The problem with the previous code is that if
two processes execute it at the same time,
then the sequence shown in Figure 47-2 could
occur, if the first process’s time slice happens
to expire at the point marked XXXX in the code.
• This sequence is problematic for two reasons.
First, process B performs a semop() on an
uninitialized semaphore (i.e., one whose value
is arbitrary).
Second, the semctl() call in process A
overwrites the changes made by process B.
The solution to race problem
• When a semaphore set is first created, the sem_otime
field is initialized to 0, and it is changed only by a
subsequent semop() call.
• We can exploit this feature to eliminate the race
condition described above.
• We do this by inserting extra code to force the second
process (i.e., the one that does not create the
semaphore) to wait until the first process has both
initialized the semaphore and executed a semop() call
that updates the sem_otime field, but does not modify
the semaphore’s value.
Initializing a System V semaphore
–––––––––––––––from svsem/svsem_good_init.c
semid = semget(key, 1, IPC_CREAT | IPC_EXCL | perms);
if (semid != -1) { /* Successfully created the semaphore */
union semun arg;
struct sembuf sop;
[Link] = 0; /* So initialize it to 0 */
if (semctl(semid, 0, SETVAL, arg) == -1)
errExit("semctl");
/* Perform a "no-op" semaphore operation - changes sem_otime
so other processes can see we've initialized the set. */
sop.sem_num = 0; /* Operate on semaphore 0 */
sop.sem_op = 0; /* Wait for value to equal 0 */
sop.sem_flg = 0;
if (semop(semid, &sop, 1) == -1)
errExit("semop");
else { /* We didn't create the semaphore set */
const int MAX_TRIES = 10;
int j;
union semun arg;
struct semid_ds ds;
if (errno != EEXIST) { /* Unexpected error from semget() */
errExit("semget");
semid = semget(key, 1, perms); /* Retrieve ID of existing set */
if (semid == -1)
errExit("semget");
/* Wait until another process has called semop() */
[Link] = &ds;
for (j = 0; j < MAX_TRIES; j++) {
if (semctl(semid, 0, IPC_STAT, arg) == -1)
errExit("semctl");
if (ds.sem_otime != 0) /* semop() performed? */
break; /* Yes, quit loop */
sleep(1); /* If not, wait and retry */
}
if (ds.sem_otime == 0) /* Loop ran to completion! */
fatal("Existing semaphore not initialized");
}
Semaphore Operations
• The semop() system call performs one or more operations on
the semaphores in the semaphore set identified by semid.
#include <sys/types.h> /* For portability */
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned int nsops);
Returns 0 on success, or –1 on error
The sops argument is a pointer to an array that contains the
operations to be performed.
nsops gives the size of this array (which must contain at least
one element). The operations are performed atomically and
in array order.
• The elements of the sops array are structures of the following
form:
struct sembuf
{
unsigned short sem_num; /* Semaphore number */
short sem_op; /* Operation to be performed */
short sem_flg; /* Operation flags (IPC_NOWAIT and
SEM_UNDO) */
};
• The sem_num field identifies the semaphore within the set
upon which the operation is to be performed.
• The sem_op field specifies the operation to be performed:
• If sem_op is greater than 0, the value of sem_op is added to the
semaphore value. As a result, other processes waiting to decrease
the semaphore value may be awakened and perform their
operations. The calling process must have write permission on
the semaphore.
• If sem_op equals 0, the value of the semaphore is checked to see
whether it currently equals 0. If it does, the operation completes
immediately; otherwise, semop() blocks until the semaphore
value becomes 0. The calling process must have read permission
on the semaphore.
• If sem_op is less than 0, decrease the value of the semaphore by
the amount specified in sem_op. If the current value of the
semaphore is greater than or equal to the absolute value of
sem_op, the operation completes immediately. Otherwise,
semop() blocks until the semaphore value has been increased to a
level that permits the operation to be performed without
resulting in a negative value. The calling process must have alter
permission on the semaphore.
Semantic Meaning of Operations
• Increasing the value of a semaphore corresponds
to making a resource available so that others can
use it.
• Decreasing the value of a semaphore corresponds
to reserving a resource for (exclusive) use by this
process. When decreasing the value of a
semaphore, the operation is blocked if the
semaphore value is too low—that is, if some
other process has already reserved the resource.
• When a semop() call blocks, the process remains
blocked until one of the following occurs:
Another process modifies the value of the semaphore
such that the requested operation can proceed.
A signal interrupts the semop() call. In this case, the
error EINTR results.
Another process deletes the semaphore referred to by
semid. In this case, semop() fails with the error
EIDRM.
We can prevent semop() from blocking when
performing an operation on a particular semaphore
by specifying the IPC_NOWAIT flag in the
corresponding sem_flg field.
Using semop() to perform operations on
multiple System V semaphores
struct sembuf sops[3];
sops[0].sem_num = 0; /* Subtract 1 from semaphore 0 */
sops[0].sem_op = -1;
sops[0].sem_flg = 0;
sops[1].sem_num = 1; /* Add 2 to semaphore 1 */
sops[1].sem_op = 2;
sops[1].sem_flg = 0;
sops[2].sem_num = 2; /* Wait for semaphore 2 to equal 0 */
sops[2].sem_op = 0;
sops[2].sem_flg = IPC_NOWAIT; /* But don't block if operation
can't be performed immediately */
if (semop(semid, sops, 3) == -1) {
if (errno == EAGAIN) /* Semaphore 2 would have blocked */
printf("Operation would have blocked\n");
else
errExit("semop"); /* Some other error */
The semtimedop() system call
• The semtimedop() system call performs the same
task as semop(), except that the timeout argument
specifies an upper limit on the time for which the
call will block.
#define _GNU_SOURCE
#include <sys/types.h> /* For portability */
#include <sys/sem.h>
int semtimedop(int semid, struct sembuf *sops,
unsigned int nsops, struct timespec *timeout);
Returns 0 on success, or –1 on error
• The timeout argument is a pointer to a
timespec structure which allows a time interval
to be expressed as a number of seconds and
nanoseconds.
• If the specified time interval expires before it is
possible to complete the semaphore operation,
semtimedop() fails with the error EAGAIN.
• If timeout is specified as NULL, semtimedop() is
exactly the same as semop().
Example program
• The program provides a command-line interface to the
semop() system call. The first argument to this program is
the identifier of the semaphore set upon which operations
are to be performed.
• Each of the remaining command-line arguments specifies a
group of semaphore operations to be performed in a single
semop() call. The operations within a single command-line
argument are delimited by commas. Each operation has one
of the following forms:
semnum+value: add value to semaphore semnum.
semnum-value: subtract value from semaphore semnum.
semnum=0: test semaphore semnum to see if it equals 0.
• At the end of each operation, we can optionally include
an n, a u, or both. The letter n means include
IPC_NOWAIT in the sem_flg value for this operation.
The letter u means include SEM_UNDO in sem_flg.
• The following command line specifies two semop() calls
on the semaphore set whose identifier is 0:
$ ./svsem_op 0 0=0 0-1,1-2n
• The first command-line argument specifies a semop()
call that waits until semaphore zero equals 0.
• The second argument specifies a semop() call that
subtracts 1 from semaphore 0, and subtracts 2 from
semaphore 1. For the operation on semaphore 0,
sem_flg is 0; for the operation on semaphore 1,
sem_flg is IPC_NOWAIT.
svsem_create.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include "tlpi_hdr.h"
static void
usageError(const char *progName, const char *msg)
{
if (msg != NULL)
fprintf(stderr, "%s", msg);
fprintf(stderr, "Usage: %s [-cx] {-f pathname | -k key | -p} "
"num-sems [octal-perms]\n", progName);
fprintf(stderr, " -c Use IPC_CREAT flag\n");
fprintf(stderr, " -x Use IPC_EXCL flag\n");
fprintf(stderr, " -f pathname Generate key using ftok()\n");
fprintf(stderr, " -k key Use 'key' as key\n");
fprintf(stderr, " -p Use IPC_PRIVATE key\n");
exit(0);
}
int
main(int argc, char *argv[])
{
int numKeyFlags; /* Counts -f, -k, and -p options */
int flags, semid, numSems, opt;
unsigned int perms;
long lkey;
key_t key;
/* Parse command-line options and arguments */
numKeyFlags = 0;
flags = 0;
while ((opt = getopt(argc, argv, "cf:k:px")) != -1) {
switch (opt) {
case 'c':
flags |= IPC_CREAT;
break;
case 'f': /* -f pathname */
key = ftok(optarg, 1);
if (key == -1)
{
perror("ftok:");
exit(0);
}
numKeyFlags++;
break;
case 'k': /* -k key (octal, decimal or hexadecimal) */
if (sscanf(optarg, "%li", &lkey) != 1)
{
printf("-k option requires a numeric argument\n");
exit(0);
}
key = lkey;
numKeyFlags++;
break;
case 'p':
key = IPC_PRIVATE;
numKeyFlags++;
break;
case 'x':
flags |= IPC_EXCL;
break;
default:
usageError(argv[0], NULL);
}
}
if (numKeyFlags != 1)
usageError(argv[0], "Exactly one of the options -f, -k, "
"or -p must be supplied\n");
if (optind >= argc)
usageError(argv[0], "Must specify number of semaphores\n");
numSems = atoi(argv[2]);
perms = S_IRUSR | S_IWUSR;
semid = semget(key, numSems, flags | perms);
if (semid == -1)
{
perror("semget:");
exit(0);
}
printf("%d\n", semid); /* On success, display semaphore set id */
exit(0);
}
#include <sys/types.h>
svsem_op.c
#include <sys/sem.h>
#include <ctype.h>
#include <time.h>
#include "tlpi_hdr.h"
#define MAX_SEMOPS 1000 /* Maximum operations that we permit for a single semop() */
static void
usageError(const char *progName)
{
fprintf(stderr, "Usage: %s semid op[,op...] ...\n\n", progName);
fprintf(stderr, "'op' is either: <sem#>{+|-}<value>[n][u]\n");
fprintf(stderr, " or: <sem#>=0[n]\n");
fprintf(stderr, " \"n\" means include IPC_NOWAIT in 'op'\n");
fprintf(stderr, " \"u\" means include SEM_UNDO in 'op'\n\n");
fprintf(stderr, "The operations in each argument are performed in a single semop() call\n\n");
fprintf(stderr, "e.g.: %s 12345 0+1,1-2un\n", progName);
fprintf(stderr, " %s 12345 0=0n 1+1,2-1u 1=0\n", progName);
exit(0);
/* Parse comma-delimited operations in 'arg', returning them in the
array 'sops'. Return number of operations as function result. */
static int
parseOps(char *arg, struct sembuf sops[])
{
char *comma, *sign, *remaining, *flags;
int numOps; /* Number of operations in 'arg' */
for (numOps = 0, remaining = arg; ; numOps++) {
if (numOps >= MAX_SEMOPS)
{
printf("Too many operations (maximum=%d): \"%s\"\n",MAX_SEMOPS, arg);
exit(0);
}
if (*remaining == '\0')
{
printf("Trailing comma or empty argument: \"%s\"", arg);
exit(0);
}
if (!isdigit((unsigned char) *remaining))
{
printf("Expected initial digit: \"%s\"\n", arg);
exit(0);
}
sops[numOps].sem_num = strtol(remaining, &sign, 10);
if (*sign == '\0' || strchr("+-=", *sign) == NULL)
{
printf("Expected '+', '-', or '=' in \"%s\"\n", arg);
exit(0);
}
if (!isdigit((unsigned char) *(sign + 1)))
{
printf("Expected digit after '%c' in \"%s\"\n", *sign, arg);
exit(0);
}
sops[numOps].sem_op = strtol(sign + 1, &flags, 10);
if (*sign == '-') /* Reverse sign of operation */
sops[numOps].sem_op = - sops[numOps].sem_op;
else if (*sign == '=') /* Should be '=0' */
if (sops[numOps].sem_op != 0)
{
printf("Expected \"=0\" in \"%s\"\n", arg);
exit(0);
}
sops[numOps].sem_flg = 0;
for (;; flags++) {
if (*flags == 'n')
sops[numOps].sem_flg |= IPC_NOWAIT;
else if (*flags == 'u')
sops[numOps].sem_flg |= SEM_UNDO;
else
break;
}
if (*flags != ',' && *flags != '\0')
{
printf("Bad trailing character (%c) in \"%s\"\n", *flags, arg);
exit(0);
}
comma = strchr(remaining, ',');
if (comma == NULL)
break; /* No comma --> no more ops */
else
remaining = comma + 1;
}
return numOps + 1;
}
int
main(int argc, char *argv[])
{
struct sembuf sops[MAX_SEMOPS];
int ind, nsops;
if (argc < 2 || strcmp(argv[1], "--help") == 0)
usageError(argv[0]);
for (ind = 2; argv[ind] != NULL; ind++) {
nsops = parseOps(argv[ind], sops);
time_t curtime1;
time(&curtime1);
printf("%5ld, %s: about to semop() [%s]\n", (long) getpid(),
ctime(&curtime1), argv[ind]);
if (semop(atoi(argv[1]), sops, nsops) == -1)
{
printf("semop (PID=%ld)", (long) getpid());
exit(0);
}
time_t curtime2;
time(&curtime2);
printf("%5ld, %s: semop() completed [%s]\n", (long) getpid(),
ctime(&curtime2), argv[ind]);
}
exit(0);
}
OutPut
• $ ./svsem_create -p 2
32769 ID of semaphore set
• $ ./svsem_setall 32769 1 0
Semaphore values changed (PID=3658)
• $ ./svsem_op 32769 0-1,1-1 & Operation 1
3659, [Link] about to semop() [0-1,1-1]
[1] 3659
• $ ./svsem_op 32769 1-1 & Operation 2
3660, [Link] about to semop() [1-1]
[2] 3660
• $ ./svsem_op 32769 0=0 & Operation 3
3661, [Link] about to semop() [0=0]
[3] 3661
• $ ./svsem_mon 32769
Semaphore changed: Sun Jul 25 [Link] 2010
Last semop(): Thu Jan 1 [Link] 1970
Sem # Value SEMPID SEMNCNT SEMZCNT
0 1 0 0 1
1 0 0 2 0
• $ ./svsem_op 32769 0=0n Operation 4
3673, [Link] about to semop() [0=0n]
ERROR [EAGAIN/EWOULDBLOCK Resource temporarily unavailable] semop
(PID=3673)
• $ ./svsem_op 32769 1+1 Operation 5
3674, [Link] about to semop() [1+1]
3659, [Link] semop() completed [0-1,1-1] Operation 1 completes
3661, [Link] semop() completed [0=0] Operation 3 completes
3674, [Link] semop() completed [1+1] Operation 5 completes
[1] Done ./svsem_op 32769 0-1,1-1
[3]+ Done ./svsem_op 32769 0=0
• $ ./svsem_mon 32769
Semaphore changed: Sun Jul 25 [Link] 2010
Last semop(): Sun Jul 25 [Link] 2010
Sem # Value SEMPID SEMNCNT SEMZCNT
0 0 3661 0 0
1 0 3659 1 0