Locking/unlocking SysV semaphores

54 views
Skip to first unread message

Alexander Farber (EED)

unread,
Dec 6, 2000, 3:00:00 AM12/6/00
to
Hi,

I am reading "man semctl", "man semop" and the UNP-book
by Stevens and have the following SysV-related question:

What is the point of having this construct (UNP, p. 286):

struct sembuf ops[2];
ops[0].sem_num = 0; /* why this? */
ops[0].sem_op = 0; /* why this? */
ops[0].sem_flg = 0; /* why this? */
ops[1].sem_num = 0;
ops[1].sem_op = 1;
ops[1].sem_flg = SEM_UNDO;

Isn't using a SysV semaphore as simple that if its value
is 0, then it is locked; and if it's 1, then it's unlocked?

So the usual procedure (with error checks) would be:

init() {

call semget(IPC_PRIVATE,...) and save the returned semid for reference.
call semctl(semid,SETVAL) to init the semaphore and set its value to 1 (unlocked)
}

lock() {
call semctl(semid,...) to lock the semaphore (value: -1)
}

unlock() {
call semctl(semid,...) to unlock the semaphore (value: +1)
}

If this is correct, then why using

ops[0].sem_num = 0;
ops[0].sem_op = 0;
ops[0].sem_flg = 0;


And another question: how to implement shared locks?
Is there some trick using semncnt/semzcnt possible or
should I use some variable to count the reading processes?

Thank you very much
Alex

Joe Seigh

unread,
Dec 7, 2000, 3:00:00 AM12/7/00
to

You don't lock a semaphore. You can use semaphores to implement locks. You're correct
in that the usual lock implementation is to initialize a sempahore to 1 and use -1 to
lock and +1 to unlock. But in this case they're doing it a different way. They using
semval=0 to be unlocked and semval=1 to be locked, but sem_op = +1 never blocks so they
need to use sem_op = 0 to block until the semval goes to zero.

For shared locks you can do something like this which also has a invalid state if a
process with exclusive access exits or terminates without releasing the lock.

#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/stat.h>

#define setop(var, n, op, flg) {\
var.sem_num = n; \
var.sem_op = op; \
var.sem_flg = flg; \
}

int initialize(key_t key) {
struct sembuf sb[1];
int semid;
setop(sb[0], 0, +1, 0);

if ((semid=semget(key, 3, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR)) >= 0) {
if (semop(semid, sb, 1) < 0) {
perror("semop initialize");
exit(1);
}
}
else {
if (errno==EEXIST)
semid=semget(key, 3, S_IRUSR|S_IWUSR);
}
return semid;
}
int acquire_shared(int semid) {
struct sembuf sb[4];
setop(sb[0], 0, -1, 0);
setop(sb[1], 1, +1, SEM_UNDO);
setop(sb[2], 0, +1, 0);
setop(sb[3], 2, 0, IPC_NOWAIT);

return semop(semid, sb, 4);
}
int release_shared(int semid) {
struct sembuf sb[1];
setop(sb[0], 1, -1, SEM_UNDO);
return semop(semid, sb, 1);
}
int acquire_exclusive(int semid) {
struct sembuf sb[2];
int rc;
setop(sb[0], 0, -1, SEM_UNDO);
setop(sb[1], 2, 0, IPC_NOWAIT);
if ((rc=semop(semid, sb, 2))<0)
return rc;
setop(sb[0], 1, 0, 0);
setop(sb[1], 2, +1, 0);
if ((rc=semop(semid, sb, 2)) < 0 && errno!=EIDRM) {
setop(sb[0], 0, +1, SEM_UNDO);
if (semop(semid, sb, 1) < 0)
abort();
}
return rc;
}
int release_exclusive(int semid) {
struct sembuf sb[2];
setop(sb[0], 0, +1, SEM_UNDO);
setop(sb[1], 2, -1, 0);
return semop(semid, sb, 2);
}
int cleanup(int semid) {
return semctl(semid,0,IPC_RMID,0);
}


Joe Seigh

Alexander Farber

unread,
Dec 29, 2000, 7:00:35 PM12/29/00
to jse...@genuity.com
[mailed + posted since my reply is late, sorry]

Hi Joe (and others),

I appreciate you reply. Last few weeks I was busy at work and
have finally got to read it today. I have also read "man semXXX"
and the chapter about the SysV semaphores in the Stevens' book
for the 3rd time. But I still don't understand it. I think, only
a little bit is missing, please help me:

Joe Seigh wrote:


> Alexander Farber (EED) wrote:
> > What is the point of having this construct (UNP, p. 286):
> >
> > struct sembuf ops[2];
> > ops[0].sem_num = 0; /* why this? */
> > ops[0].sem_op = 0; /* why this? */
> > ops[0].sem_flg = 0; /* why this? */
> > ops[1].sem_num = 0;
> > ops[1].sem_op = 1;
> > ops[1].sem_flg = SEM_UNDO;

> > And another question: how to implement shared locks?


> > Is there some trick using semncnt/semzcnt possible or
> > should I use some variable to count the reading processes?
>
> You don't lock a semaphore. You can use semaphores to implement locks. You're correct
> in that the usual lock implementation is to initialize a sempahore to 1 and use -1 to
> lock and +1 to unlock. But in this case they're doing it a different way. They using
> semval=0 to be unlocked and semval=1 to be locked, but sem_op = +1 never blocks so they
> need to use sem_op = 0 to block until the semval goes to zero.

Okay, I understand it. sem_op = -1 or sem_op = 0 would block,
but the sem_op = +1 doesn't. So they prepend +1 with a 0.

> For shared locks you can do something like this which also has a invalid state if a
> process with exclusive access exits or terminates without releasing the lock.

Could you elaborate here please? What would be the semaphore
value if this invalid state occurs and what would happen afterwards?

> #define setop(var, n, op, flg) {\
> var.sem_num = n; \
> var.sem_op = op; \
> var.sem_flg = flg; \
> }
>
> int initialize(key_t key) {
> struct sembuf sb[1];
> int semid;
> setop(sb[0], 0, +1, 0);
>
> if ((semid=semget(key, 3, IPC_CREAT|IPC_EXCL|S_IRUSR|S_IWUSR)) >= 0) {
> if (semop(semid, sb, 1) < 0) {
> perror("semop initialize");
> exit(1);
> }
> }
> else {
> if (errno==EEXIST)
> semid=semget(key, 3, S_IRUSR|S_IWUSR);
> }
> return semid;
> }

Okay you create 3 semaphores. The first is initiated to 1 and the
others to 0. (BTW I think, you are wrong here, since Stevens writes,
that the values of the semaphores are generally not initialized
after the semget-call. You have to init them with a following semctl-
call. But it is just minor detail here).

Could you please tell, what is the function of each of the semaphores?

> int acquire_shared(int semid) {
> struct sembuf sb[4];
> setop(sb[0], 0, -1, 0);

Why don't you call above with SEM_UNDO?

> setop(sb[1], 1, +1, SEM_UNDO);
> setop(sb[2], 0, +1, 0);
> setop(sb[3], 2, 0, IPC_NOWAIT);
>
> return semop(semid, sb, 4);
> }
> int release_shared(int semid) {
> struct sembuf sb[1];
> setop(sb[0], 1, -1, SEM_UNDO);
> return semop(semid, sb, 1);
> }
> int acquire_exclusive(int semid) {
> struct sembuf sb[2];
> int rc;
> setop(sb[0], 0, -1, SEM_UNDO);
> setop(sb[1], 2, 0, IPC_NOWAIT);
> if ((rc=semop(semid, sb, 2))<0)
> return rc;

Why do you do it in two steps here and why
do you call sem_op = 0 with an IPC_NOWAIT above?

> setop(sb[0], 1, 0, 0);
> setop(sb[1], 2, +1, 0);
> if ((rc=semop(semid, sb, 2)) < 0 && errno!=EIDRM) {
> setop(sb[0], 0, +1, SEM_UNDO);
> if (semop(semid, sb, 1) < 0)
> abort();
> }
> return rc;
> }
> int release_exclusive(int semid) {
> struct sembuf sb[2];
> setop(sb[0], 0, +1, SEM_UNDO);
> setop(sb[1], 2, -1, 0);
> return semop(semid, sb, 2);
> }
> int cleanup(int semid) {
> return semctl(semid,0,IPC_RMID,0);
> }

Regards
Alex

--
http://home.t-online.de/home/Alexander.Farber/

Alexander Farber

unread,
Dec 29, 2000, 7:02:00 PM12/29/00
to
Reply all
Reply to author
Forward
0 new messages