Ipc Using Shared Memory
Ipc Using Shared Memory
Shared Memory is a type of IPC where the two processes share same memory chunk and use it for IPC. One process writes into that memory and
other reads it.
After running the Server you can see the attached Shared Memory
vgupta80@linux unixprog> ipcs -m
------ Shared Memory Segments -------key
shmid
owner
perms
0x0000162e 65537
vgupta80 666
bytes
27
nattch
bytes
27
status
nattch
//SHMServer.C
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE
27
status
//SHMClient.C
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE
27
void die(char *s)
{
perror(s);
exit(1);
}
int main()
{
int shmid;
key_t key;
char *shm, *s;
key = 5678;
if ((shmid = shmget(key, MAXSIZE, 0666)) < 0)
die("shmget");
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1)
die("shmat");
//Now read what the server put in the memory.
for (s = shm; *s != '\0'; s++)
putchar(*s);
putchar('\n');
/*
*Change the first character of the
*segment to '*', indicating we have read
*the segment.
*/
*shm = '*';
exit(0);
}
There are various ways to communicate from one process to another. A few ways to do so are through Unix domain sockets, sockets on the local
loopback, signals between processes, open files, message queues, pipes, and even memory mapped files. One interesting way is to use shared
memory segments using a key to identify where in memory the shared segment will be. I have done a fair amount of interprocess communication
(IPC) using sockets on the loopback as well as signalling, so I thought it would be good to delve into shared memory as a medium for IPC.
The main idea behind shared memory is based on the server / client paradigm. The server maps a section of memory and the client may have
access to that shared memory for reading or writing, in doing so there is a window between the two processes in which data can be exchanged.
There are a set of functions that are used to take advantage of using shared memory.
Contents [hide]
3 Share this:
This function returns the identifier associated with the value of the first argument key. The shmget function takes three parameters. The first
parameter key is an integer value used as the identifier in which both processes use to map the memory segment. The second parameter, size is
the amount of memory to map, where size is equal to the value of size rounded up to a multiple of PAGE_SIZE. You can view the system
PAGE_SIZE from the command line via getconf PAGESIZE, for more getconf information check out my earlier article. In my case PAGESIZE
is 4096 bytes. Finally, the third parameter is used for access permissions to the shared memory segment. The values are analogous to the open(2)
permission settings. In our case we use IPC_CREATE | 0666.
1
2
3
4
5
6
The shmat function returns the attached shared memory segment. The first argument is the return value from the shmget function call. The
second argument is shmaddr, if it is NULL, the system chooses a suitable (unused) address at which to attach the segment. The third argument is
akin to the shmflg mentioned above and is used for permissions. The return value is a pointer to the shared memory and can be acted upon.
Finally, once all work is complete on our address. We can close our handle using shmdt.
1
2
3
4
5
6
If you specified a value other than NULL on the shmat call, then you would pass that value into shmaddr. I just pass the return value from shmat
into shmdt and check the return value. If shmdt fails it returns -1 and sets errno appropriately.
Those are the three main functions for setting up memory mapping between processes. I always find examples useful, so I have created a server
and client example. The client grabs user input from standard input and writes it to memory then the server reads that input from the first byte of
memory and prints it to the screen.
Examples Program Using Shared Memory
The first bit of code here is the server code, it initially creates the shared memory segment and sets the permissions accordingly. Its goal is to
listen for changes to the first byte in memory and display them to standard output. You will notice the use of shmget, shmat, and shmdt within my
code.
Shared Memory Server Side
1
2
3
4
5
6
#include
#include
#include
#include
#include
#include
<sys/types.h>
<sys/ipc.h>
<sys/shm.h>
<stdio.h>
<unistd.h>
<string.h>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include
#include
#include
#include
#include
<sys/types.h>
<sys/ipc.h>
<sys/shm.h>
<stdio.h>
<unistd.h>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <string.h>
#define SHMSZ
1024
main()
{
int shmid;
key_t key;
char *shm, *s;
/*
* We need to get the segment named
* "1234", created by the server.
*/
key = 1234;
/*
* Locate the segment.
*/
if ((shmid = shmget(key, SHMSZ, 0666)) < 0) {
perror("shmget");
return 1;
}
/*
* Now we attach the segment to our data space.
*/
if ((shm = shmat(shmid, NULL, 0)) == (char *) -1) {
perror("shmat");
return 1;
}
/*
* Zero out memory segment
*/
memset(shm,0,SHMSZ);
s = shm;
/*
* Client writes user input character to memory
* for server to read.
*/
for(;;){
char tmp = getchar();
// Eat the enter key
getchar();
if(tmp == 'q'){
*shm = 'q';
break;
}
*shm = tmp;
}
if(shmdt(shm) != 0)
fprintf(stderr, "Could not close memory segment.\n");
return 0;
}
The output of the two programs is as follows. I start the server first as it creates the memory segment, then within the client I enter the characters I
want the server to output.
This is a basic example of passing data between processes. Rather than simply modifying one character in memory I could pass a structure
containing various fields or structs within structs, you are only limited by the amount of memory and imagination as to what you could pass back
and forth. There you have it, a basic example of two separate processes passing data between each other.
Write a C program that illustrates inter process communication using shared memory system calls.
#include <stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#define SEGSIZE 100
int main(int argc, char *argv[ ])
{
int shmid,cntr;
key_t key;
char *segptr;
char buff[ ]=Hello world;
key=ftok(.,s);
if((shmid=shmget(key, SEGSIZE, IPC_CREAT |
IPC_EXCL | 0666))= = -1)
{
if((shmid=shmget(key,SEGSIZE,0))= = -1)
{
perror(shmget);
exit(1);
}
}
else
{
printf(Creating a new shared memory seg \n);
printf(SHMID:%d, shmid);
}
system(ipcs m);
if((segptr=shmat(shmid,0,0))= =(char*)-1)
{
perror(shmat);
exit(1);
}
printf(Writing data to shared memory\n);
strcpy(segptr,buff);
printf(DONE\n);
printf(Reading data from shared memory\n);
printf(DATA:-%s\nsegptr);
printf(DONE\n);
print(Removing shared memory Segment\n);
if(shmctl(shmid,IPC_RMID,0)= = -1)
printf(Cant Remove Shared memory Segment\n);
else
printf(Removed Successfully);
}
track until the semaphore is in a state that permits travel. When the train enters the
track, the semaphore changes state to prevent other trains from entering the track.
A train that is leaving this section of track must again change the state of the
semaphore to allow another train to enter. In the computer version, a semaphore
appears to be a simple integer. A process (or a thread) waits for permission to
proceed by waiting for the integer to become 0. The signal if it proceeds signals that
this by performing incrementing the integer by 1. When it is finished, the process
changes the semaphore's value by subtracting one from it.
Semaphores let processes query or alter status information. They are often used to monitor and
control the availability of system resources such as shared memory segments.
Semaphores can be operated on as individual units or as elements in a set. Because System V
IPC semaphores can be in a large array, they are extremely heavy weight. Much lighter weight
semaphores are available in the threads library (see man semaphore and also Chapter 30.3) and
POSIX semaphores (see below briefly). Threads library semaphores must be used with mapped
memory . A semaphore set consists of a control structure and an array of individual semaphores.
A set of semaphores can contain up to 25 elements.
In a similar fashion to message queues, the semaphore set must be initialized using semget();
the semaphore creator can change its ownership or permissions using semctl(); and semaphore
operations are performed via the semop() function. These are now discussed below:
Controlling Semaphores
semctl()
follows:
int semctl(int semid, int semnum, int cmd, union semun arg);
It must be called with a valid semaphore ID, semid. The semnum value selects a semaphore
within an array by its index. The cmd argument is one of the following control flags:
GETVAL
-- Set the value of a single semaphore. In this case, arg is taken as arg.val, an
int.
GETPID
-- Return the PID of the process that performed the last operation on the
semaphore or array.
GETNCNT
-- Return the values for all semaphores in a set. In this case, arg is taken as
arg.array, a pointer to an array of unsigned shorts (see below).
SETALL
-- Set values for all semaphores in a set. In this case, arg is taken as
arg.array, a pointer to an array of unsigned shorts.
IPC_STAT
-- Return the status information from the control structure for the semaphore
set and place it in the data structure pointed to by arg.buf, a pointer to a
buffer of type semid_ds.
IPC_SET
-- Set the effective user and group identification and permissions. In this case,
arg is taken as arg.buf.
IPC_RMID
A process must have an effective user identification of owner, creator, or superuser to perform an
IPC_SET or IPC_RMID command. Read and write permission is required as for the other control
commands. The following code illustrates semctl ().
The fourth argument union semun arg is optional, depending upon the operation requested. If
required it is of type union semun, which must be explicitly declared by the application program
as:
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun {
int val;
struct semid_ds *buf;
ushort *array;
} arg;
int i;
int semnum = ....;
int cmd = GETALL; /* get value */
...
i = semctl(semid, semnum, cmd, arg);
if (i == -1) {
perror("semctl: semctl failed");
exit(1);
}
else
...
Semaphore Operations
semop()
The semid argument is the semaphore ID returned by a previous semget() call. The sops
argument is a pointer to an array of structures, each containing the following information about a
semaphore operation:
sem_num;
sem_op;
sem_flg;
/* semaphore number */
/* semaphore operation */
/* operation flags */
The nsops argument specifies the length of the array, the maximum size of which is determined
by the SEMOPM configuration option; this is the maximum number of operations allowed by a
single semop() call, and is set to 10 by default. The operation to be performed is determined as
follows:
A positive integer increments the semaphore value by that amount.
A negative integer decrements the semaphore value by that amount.
An attempt to set a semaphore to a value less than zero fails or blocks,
depending on whether IPC_NOWAIT is in effect.
A value of zero means to wait for the semaphore value to reach zero.
There are two control flags that can be used with semop():
IPC_NOWAIT
-- Can be set for any operations in the array. Makes the function return
without changing any semaphore value if any operation for which IPC_NOWAIT
is set cannot be performed. The function fails if it tries to decrement a
semaphore more than its current value, or tests a nonzero semaphore to be
equal to zero.
SEM_UNDO
This function takes a pointer, sops, to an array of semaphore operation structures. Each structure
in the array contains data about an operation to perform on a semaphore. Any process with read
permission can test whether a semaphore has a zero value. To increment or decrement a
semaphore requires write permission. When an operation fails, none of the semaphores is altered.
The process blocks (unless the IPC_NOWAIT flag is set), and remains blocked until:
Only one process at a time can update a semaphore. Simultaneous requests by different processes
are performed in an arbitrary order. When an array of operations is given by a semop() call, no
updates are done until all operations on the array can finish successfully.
If a process with exclusive use of a semaphore terminates abnormally and fails to undo the
operation or free the semaphore, the semaphore stays locked in memory in the state the process
left it. To prevent this, the SEM_UNDO control flag makes semop() allocate an undo structure for
each semaphore operation, which contains the operation that returns the semaphore to its
previous state. If the process dies, the system applies the operations in the undo structures. This
prevents an aborted process from leaving a semaphore set in an inconsistent state. If processes
share access to a resource controlled by a semaphore, operations on the semaphore should not be
made with SEM_UNDO in effect. If the process that currently has control of the resource terminates
abnormally, the resource is presumed to be inconsistent. Another process must be able to
recognize this to restore the resource to a consistent state. When performing a semaphore
operation with SEM_UNDO in effect, you must also have it in effect for the call that will perform
the reversing operation. When the process runs normally, the reversing operation updates the
undo structure with a complementary value. This ensures that, unless the process is aborted, the
values applied to the undo structure are cancel to zero. When the undo structure reaches zero, it
is removed.
NOTE:Using SEM_UNDO inconsistently can lead to excessive resource consumption because
allocated undo structures might not be freed until the system is rebooted.
The following code illustrates the semop() function:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
...
int i;
int nsops; /* number of operations to do */
int semid; /* semid of semaphore set */
struct sembuf *sops; /* ptr to operations to perform */
...
if ((semid = semop(semid, sops, nsops)) == -1)
{
perror("semop: semop failed");
exit(1);
}
else
sem_init()
semaphore).
-- Ends the connection to an open semaphore.
sem_close()
sem_getvalue()
sem_post()
The basic operation of these functions is essence the same as described above, except note there
are more specialised functions, here. These are not discussed further here and the reader is
referred to the online man pages for further details.
semaphore.c
<stdio.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
union semun {
int val;
struct semid_ds *buf;
ushort *array;
};
main()
{ int i,j;
int pid;
int semid; /* semid of semaphore set */
key_t key = 1234; /* key to pass to semget() */
int semflg = IPC_CREAT | 0666; /* semflg to pass to semget() */
int nsems = 1; /* nsems to pass to semget() */
int nsops; /* number of operations to do */
struct sembuf *sops = (struct sembuf *) malloc(2*sizeof(struct sembuf));
/* ptr to operations to perform */
/* set up semaphore */
(void) fprintf(stderr, "\nsemget: Setting up seamaphore: semget(%#lx, %\
%#o)\n",key, nsems, semflg);
if ((semid = semget(key, nsems, semflg)) == -1) {
perror("semget: semget failed");
exit(1);
} else
(void) fprintf(stderr, "semget: semget succeeded: semid =\
%d\n", semid);
/* get child process */
if ((pid = fork()) < 0) {
perror("fork");
exit(1);
}
if (pid == 0)
{ /* child */
i = 0;
while (i
nsops = 2;
/* wait for semaphore to reach zero */
sops[0].sem_num = 0; /* We only use one track */
sops[0].sem_op = 0; /* wait for semaphore flag to become zero */
sops[0].sem_flg = SEM_UNDO; /* take off semaphore asynchronous */
sops[1].sem_num = 0;
sops[1].sem_op = 1; /* increment semaphore -- take control of track */
sops[1].sem_flg = SEM_UNDO | IPC_NOWAIT; /* take off semaphore */
/* Recap the call to be made. */
nsops = 2;
/* wait for semaphore to reach zero */
sops[0].sem_num = 0;
Each process waits for the track to become free and then
attempts to take control of track
o
-1
Illustrate the
semget()
function
/*
* semget.c: Illustrate the semget() function.
*
* This is a simple exerciser of the semget() function. It prompts
* for the arguments, makes the call, and reports the results.
*/
#include
#include
#include
#include
<stdio.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
extern void
extern void
exit();
perror();
main()
{
key_t key;
int semflg;
int nsems;
int semid;
(void) fprintf(stderr,
"All numeric input must follow C conventions:\n");
(void) fprintf(stderr,
"\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
(void) fprintf(stderr, "IPC_PRIVATE == %#lx\n", IPC_PRIVATE);
(void) fprintf(stderr, "Enter key: ");
(void) scanf("%li", &key);
(void) fprintf(stderr, "Enter nsems value: ");
(void) scanf("%i", &nsems);
(void) fprintf(stderr, "\nExpected flags for semflg are:\n");
(void) fprintf(stderr, "\tIPC_EXCL = \t%#8.8o\n", IPC_EXCL);
(void) fprintf(stderr, "\tIPC_CREAT = \t%#8.8o\n",
IPC_CREAT);
(void) fprintf(stderr, "\towner read = \t%#8.8o\n", 0400);
(void) fprintf(stderr, "\towner alter = \t%#8.8o\n", 0200);
(void) fprintf(stderr, "\tgroup read = \t%#8.8o\n", 040);
(void) fprintf(stderr, "\tgroup alter = \t%#8.8o\n", 020);
(void) fprintf(stderr, "\tother read = \t%#8.8o\n", 04);
(void) fprintf(stderr, "\tother alter = \t%#8.8o\n", 02);
(void) fprintf(stderr, "Enter semflg value: ");
(void) scanf("%i", &semflg);
(void) fprintf(stderr, "\nsemget: Calling semget(%#lx, %
%#o)\n",key, nsems, semflg);
if ((semid = semget(key, nsems, semflg)) == -1) {
perror("semget: semget failed");
exit(1);
} else {
(void) fprintf(stderr, "semget: semget succeeded: semid =
%d\n",
semid);
exit(0);
}
}
semctl.c:
/*
*
*
*
*
*
to
*
Illustrate the
semctl.c:
semctl()
function
to
* reset the permissions with this code if you do.
*/
#include
#include
#include
#include
#include
<stdio.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
<time.h>
void
void
char
void
void
do_semctl();
do_stat();
*malloc();
exit();
perror();
char
main()
{
union semun
arg;
/* union to pass to semctl() */
int
cmd,
/* command to give to semctl() */
i,
/* work area */
semid,
/* semid to pass to semctl() */
semnum;
/* semnum to pass to semctl() */
(void) fprintf(stderr,
"All numeric input must follow C conventions:\n");
(void) fprintf(stderr,
"\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
(void) fprintf(stderr, "Enter semid value: ");
(void) scanf("%i", &semid);
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
(void)
case GETZCNT:
/* Get the semaphore number for these commands. */
(void) fprintf(stderr, "\nEnter semnum value: ");
(void) scanf("%i", &semnum);
break;
case GETALL:
case SETALL:
/* Allocate a buffer for the semaphore values. */
(void) fprintf(stderr,
"Get number of semaphores in the set.\n");
arg.buf = &semid_ds;
do_semctl(semid, 0, IPC_STAT, arg);
if (arg.array =
(ushort *)malloc((unsigned)
(semid_ds.sem_nsems * sizeof(ushort)))) {
/* Break out if you got what you needed. */
break;
}
(void) fprintf(stderr,
"semctl: unable to allocate space for %d values\n",
semid_ds.sem_nsems);
exit(2);
}
/* Get the rest of the arguments needed for the specified
command. */
switch (cmd) {
case SETVAL:
/* Set value of one semaphore. */
(void) fprintf(stderr, "\nEnter semaphore value: ");
(void) scanf("%i", &arg.val);
do_semctl(semid, semnum, SETVAL, arg);
/* Fall through to verify the result. */
(void) fprintf(stderr,
"Do semctl GETVAL command to verify results.\n");
case GETVAL:
/* Get value of one semaphore. */
arg.val = 0;
do_semctl(semid, semnum, GETVAL, arg);
break;
case GETPID:
/* Get PID of last process to successfully complete a
semctl(SETVAL), semctl(SETALL), or semop() on the
semaphore. */
arg.val = 0;
do_semctl(semid, 0, GETPID, arg);
break;
case GETNCNT:
/* Get number of processes waiting for semaphore value to
increase. */
arg.val = 0;
do_semctl(semid, semnum, GETNCNT, arg);
break;
case GETZCNT:
/* Get number of processes waiting for semaphore value to
become zero. */
arg.val = 0;
semop()
semop()
/*
* semop.c: Illustrate the semop() function.
*
* This is a simple exerciser of the semop() function. It lets you
* to set up arguments for semop() and make the call. It then
reports
* the results repeatedly on one semaphore set. You must have read
* permission on the semaphore set or this exerciser will fail.
(It
* needs read permission to get the number of semaphores in the set
* and to report the values before and after calls to semop().)
*/
#include
#include
#include
#include
static
extern
extern
extern
extern
int
void
void
char
void
<stdio.h>
<sys/types.h>
<sys/ipc.h>
<sys/sem.h>
ask();
exit();
free();
*malloc();
perror();
semid_ds;
static char
error_mesg1[] = "semop: Can't allocate space for %d\
semaphore values. Giving up.\n";
static char
error_mesg2[] = "semop: Can't allocate space for %d\
i;
/* work area */
/* number of operations to do */
/* semid of semaphore set */
*sops;
/* ptr to operations to perform */
(void) fprintf(stderr,
"All numeric input must follow C conventions:\n");
(void) fprintf(stderr,
"\t0x... is interpreted as hexadecimal,\n");
(void) fprintf(stderr, "\t0... is interpreted as octal,\n");
(void) fprintf(stderr, "\totherwise, decimal.\n");
/* Loop until the invoker doesn't want to do anymore. */
while (nsops = ask(&semid, &sops)) {
/* Initialize the array of operations to be performed.*/
for (i = 0; i < nsops; i++) {
(void) fprintf(stderr,
"\nEnter values for operation %d of %d.\n",
i + 1, nsops);
(void) fprintf(stderr,
"sem_num(valid values are 0 <= sem_num < %d): ",
semid_ds.sem_nsems);
(void) scanf("%hi", &sops[i].sem_num);
(void) fprintf(stderr, "sem_op: ");
(void) scanf("%hi", &sops[i].sem_op);
(void) fprintf(stderr,
"Expected flags in sem_flg are:\n");
(void) fprintf(stderr, "\tIPC_NOWAIT =\t%#6.6o\n",
IPC_NOWAIT);
(void) fprintf(stderr, "\tSEM_UNDO =\t%#6.6o\n",
SEM_UNDO);
(void) fprintf(stderr, "sem_flg: ");
(void) scanf("%hi", &sops[i].sem_flg);
}
/* Recap the call to be made. */
(void) fprintf(stderr,
"\nsemop: Calling semop(%d, &sops, %d) with:",
semid, nsops);
for (i = 0; i < nsops; i++)
{
(void) fprintf(stderr, "\nsops[%d].sem_num = %d, ", i,
sops[i].sem_num);
(void) fprintf(stderr, "sem_op = %d, ", sops[i].sem_op);
(void) fprintf(stderr, "sem_flg = %#o\n",
sops[i].sem_flg);
}
/* Make the semop() call and report the results. */
if ((i = semop(semid, sops, nsops)) == -1) {
perror("semop: semop failed");
} else {
(void) fprintf(stderr, "semop: semop returned %d\n", i);
}
/*
* Ask if user wants to continue.
*
* On the first call:
* Get the semid to be processed and supply it to the caller.
* On each call:
* 1. Print current semaphore values.
* 2. Ask user how many operations are to be performed on the next
*
call to semop. Allocate an array of sembuf structures
*
sufficient for the job and set caller-supplied pointer to
that
*
array. (The array is reused on subsequent calls if it is big
*
enough. If it isn't, it is freed and a larger array is
*
allocated.)
*/
static
ask(semidp, sopsp)
int
*semidp; /* pointer to semid (used only the first time) */
struct sembuf
**sopsp;
{
static union semun
arg; /* argument to semctl */
int
i; /* work area */
static int
nsops = 0; /* size of currently allocated
sembuf array */
static int
semid = -1;
/* semid supplied by user */
static struct sembuf
*sops;
/* pointer to allocated array */
if (semid < 0) {
/* First call; get semid from user and the current state of
the semaphore set. */
(void) fprintf(stderr,
"Enter semid of the semaphore set you want to use: ");
(void) scanf("%i", &semid);
*semidp = semid;
arg.buf = &semid_ds;
if (semctl(semid, 0, IPC_STAT, arg) == -1) {
perror("semop: semctl(IPC_STAT) failed");
/* Note that if semctl fails, semid_ds remains filled
with zeros, so later test for number of semaphores will
be zero. */
(void) fprintf(stderr,
"Before and after values are not printed.\n");
} else {
if ((arg.array = (ushort *)malloc(
(unsigned)(sizeof(ushort) * semid_ds.sem_nsems)))
== NULL) {
(void) fprintf(stderr, error_mesg1,
semid_ds.sem_nsems);
exit(1);
}
}
}
/* Print current semaphore values. */
if (semid_ds.sem_nsems) {
(void) fprintf(stderr,
"There are %d semaphores in the set.\n",
semid_ds.sem_nsems);
if (semctl(semid, 0, GETALL, arg) == -1) {
perror("semop: semctl(GETALL) failed");
} else {
(void) fprintf(stderr, "Current semaphore values are:");
for (i = 0; i < semid_ds.sem_nsems;
(void) fprintf(stderr, " %d", arg.array[i++]));
(void) fprintf(stderr, "\n");
}
}
/* Find out how many operations are going to be done in the
next
call and allocate enough space to do it. */
(void) fprintf(stderr,
"How many semaphore operations do you want %s\n",
"on the next call to semop()?");
(void) fprintf(stderr, "Enter 0 or control-D to quit: ");
i = 0;
if (scanf("%i", &i) == EOF || i == 0)
exit(0);
if (i > nsops) {
if (nsops)
free((char *)sops);
nsops = i;
if ((sops = (struct sembuf *)malloc((unsigned)(nsops *
sizeof(struct sembuf)))) == NULL) {
(void) fprintf(stderr, error_mesg2, nsops);
exit(2);
}
}
*sopsp = sops;
return (i);
}