Semaphores
Linux also has two different semaphores implementation, XSI semaphore, POSIX semaphore.
The interface of XSI semaphores.
int semget(key_t key, int nsems, int flag);
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */ );
int semop(int semid, struct sembuf semoparray[], size_t nops);
The cmd
The simplest demo of create a semaphore
#include "apue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
void
sem_status() {
int semid;
struct semid_ds sem_buf;
semid = semget(IPC_PRIVATE, 1, IPC_CREAT|0600);
//Create private semaphore, the key of semaphore will be 0x00000000
if (semid < 0) {
err_sys("semget failed");
}
if (semctl(semid, 0, IPC_STAT, &sem_buf) == -1) {
err_sys("semctl failed");
}
printf("semid: %d\n", semid);
printf("sem_perm.uid: %d\n", sem_buf.sem_perm.uid);
printf("sem_perm.gid: %d\n", sem_buf.sem_perm.gid);
printf("sem_otime: %d\n", sem_buf.sem_otime);
printf("sem_ctime: %d\n", sem_buf.sem_ctime);
printf("sem_ctime: %d\n", sem_buf.sem_ctime);
printf("sem_nsems;: %d\n", sem_buf.sem_nsems);
}
int
main(void) {
sem_status();
exit(0);
}
We can utilize semaphore implementation a mutex lock between independent process.
#include "apue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define PATH "/home/keqiang/workspace/c/apue/mq_x"
#define ID 1234
void
sem_status(int semid) {
struct semid_ds sem_buf;
if (semctl(semid, 0, IPC_STAT, &sem_buf) == -1) {
err_sys("semctl failed");
}
printf("semid: %d\n", semid);
printf("sem_perm.uid: %d\n", sem_buf.sem_perm.uid);
printf("sem_perm.gid: %d\n", sem_buf.sem_perm.gid);
printf("sem_otime: %d\n", sem_buf.sem_otime);
printf("sem_ctime: %d\n", sem_buf.sem_ctime);
printf("sem_ctime: %d\n", sem_buf.sem_ctime);
printf("sem_nsems;: %d\n", sem_buf.sem_nsems);
}
int
semlock_alloc() {
int semid;
key_t key;
struct semid_ds sem_buf;
key = ftok(PATH, ID);
if (key == -1) {
err_sys("ftok failed");
}
semid = semget(key, 1, IPC_CREAT|0600);
if (semid < 0) {
err_sys("semget failed");
}
//initial lock
if (semctl(semid, 0, SETVAL, 1) == -1) {
err_sys("semctl failed");
}
return semid;
}
void
semlock_free(int lockid) {
semctl(lockid, 0, IPC_RMID);
}
int
semlock(int lockid) {
struct sembuf sbuf;
sbuf.sem_num = 0; /* member # in set (0, 1, ..., nsems-1) */
sbuf.sem_op = -1; /* operation (negative, 0, or positive) */
sbuf.sem_flg = SEM_UNDO; /* IPC_NOWAIT, SEM_UNDO */
if (semop(lockid, &sbuf, 1) < 0) {
printf("semop failed\n");
return -1;
}
return 0;
}
int
semunlock(int lockid) {
struct sembuf sbuf;
sbuf.sem_num = 0;
sbuf.sem_op = 1;
sbuf.sem_flg = 0;
if (semop(lockid, &sbuf, 1) < 0) {
printf("semop failed\n");
return -1;
}
return 0;
}
int
main(void) {
int lockid;
int pid;
lockid = semlock_alloc();
semlock(lockid);
if ((pid = fork()) < 0) {
err_sys("fork failed");
} else if (pid == 0) {
//child
semlock(lockid);
}
sem_status(lockid);
while (1) {
}
exit(0);
}
//only parent process will execute sem_status(), child be blocked at mylock() line.
Why the semaphore only work fine in parent-child process and not in two independent process?
POSIX Semaphore
We should prior to considering use POSIX semaphore
The POSIX semaphore mechanism is one of three IPC mechanisms that originated with the real-time extensions to POSIX.1. The Single UNIX Specification placed the three mechanisms (message queues, semaphores, and shared memory) in option classes.
Prior to SUSv4, the POSIX semaphore interfaces were included in the semaphores option. In SUSv4, these interfaces were moved to the base specification, but the message queue and shared memory interfaces remained optional. The POSIX semaphore interfaces were meant to address several deficiencies with the XSI semaphore interfaces:
- The POSIX semaphore interfaces allow for higher-performance implementation compared to XSI semaphores.
- The POSIX semaphore interfaces are simpler to use: there are no semaphore sets, and several of the interfaces are patterned after familiar file system operations. Although there is no requirement that they be implemented in the file system, some systems do take this approach.
- The POSIX semaphores behave more gracefully when removed. Recall that when an XSI semaphore is removed, operations using the same semaphore identifier fail with errno set to EIDRM. With POSIX semaphores, operations
The process lock based on POSIX semaphore.
#include "apue.h"
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#define NAME "/sem_x"
sem_t *
semlock_alloc(void) {
sem_t *sem;
sem = sem_open(NAME, O_CREAT, S_IRWXU, 1);
if (sem == SEM_FAILED) {
return NULL;
}
return sem;
}
void
semlock_free(sem_t *sem) {
sem_unlink(NAME);
sem_close(sem);
}
void
semlock_print(sem_t *sem) {
int num;
if (sem_getvalue(sem, &num) == -1) {
err_sys("sem_getvalue failed");
}
printf("[posix semaphore] %s:%d\n", NAME, num);
}
int
semlock(sem_t *sem) {
return sem_wait(sem);
}
int
semunlock(sem_t *sem) {
return sem_post(sem);
}
int
main(void) {
sem_t *sem;
sem = semlock_alloc();
if (sem == NULL) {
err_sys("alloc semlock failed");
}
semlock_print(sem);
semlock(sem);
semlock_print(sem);
semlock_free(sem);
exit(0);
}