Linux进程间通信(五) - 信号灯(史上最全)及其经典应用案例
信号灯概述
什么是信号灯
信号灯用来实现同步,用于多线程,多进程之间同步共享资源(临界资源)。
PV原语:信号灯使用PV原语
P原语操作的动作是:
u sem减1。
u sem减1后仍大于或等于零,则进程继续执行。
u 若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的队列中,然后转进程调度。
V原语操作的动作是:
u sem加1。
u 若相加结果大于零,则进程继续执行。
u 若相加结果小于或等于零,则从该信号的等待队列中唤醒一等待进程,然后再返回原进程继续执行或转进程调度。
信号灯分类
按信号灯实现原理,信号灯分两种,一种是有名信号灯,一种是基于内存的信号灯。
有名信号灯,是根据外部名字标识,通常指代文件系统中的某个文件。而基于内存的信号灯,它主要是把信号灯放入内存的。
基于内存的信号灯,同步多线程时,可以放到该多线程所属进程空间里;如果是同步多进程,那就需要把信号灯放入到共享内存中(方便多个进程访问)。
按实现方式,信号灯分为POSIX信号灯和System V信号灯,System V信号灯是由内核维护的,Posix信号灯是由文件系统中的路径名对应的名字来标识的。在目前的Linux中,System V使用更为广泛,POSIX一般是在更老的系统中使用。
信号灯操作
进程在信号灯上的几种操作:
1) 创建一个信号灯。还要求调用者指定初始值,对二值来说通常是1。
2) 等待一个信号灯。测试信号灯的值,如果<=0则等待,否则将其减1。注:测试其值并减1必须作为一个原子操作。
3) 挂出一个信号灯。将信号灯的值加1。挂出操作也必须是原子的。
4)获取信号灯状态。
问题:如何将等待某个信号灯的所有进程排队,如何唤醒这些可能很多的进程中的一个,所幸这些都是由实现来处理的。
二值信号灯可用于互斥目的。除了可以象互斥锁那样使用外,信号灯还有一个互斥锁没有提供的特性:互斥锁必须总是由锁住他的线程解锁,信号灯的挂出却不必由执行过它的等待操作的同一线程执行。比如生产者消费者问题是生产者和消费者互相唤醒的。
共享内存信号灯同时属于两个进程的地址空间。
信号灯有一个与之关联的值,挂出一个信号即使当前没有线程在等待该信号也没关系,与之相反的是,pthread_cond_signal如果当时没有任何线程阻塞在pthread_cond_wait中,则信号丢失。
POSIX信号灯
图1:POSIX有名信号灯和基于内存信号灯系统调用关系
POSIX有名信号灯
函数说明
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
// 用来打开已经存在的信号灯
sem_t *sem_open(const char *name, int oflag);
// 用来创建信号灯
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
// 获得信号灯,得不到就阻塞;如果获得信号灯,信号灯数量减1
int sem_wait(sem_t *sem);
// 尝试获得,得不到返回失败,errno设置为EAGAIN
int sem_trywait(sem_t *sem);
// sem_getvalue返回指定信号灯的当前值,如果该信号灯已上锁,那么返回或为0,或为某个负数,其绝对值就是等待该信号灯解锁的线程数。
int sem_getvalue(sem_t *sem, int *sval);
// 释放信号灯,信号数量加1
int sem_post(sem_t *sem);
// 删除以name命名的信号灯,只有当系统中所有使用该信号灯的进程都释放,才会真的
删除
int sem_unlink(const char *name);
创建信号灯
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include <string.h> sem_t* namedSem; #define FILENAME "/tmp/count_named_sem.txt" #define MUTEXNAME "my_named_sem" int main(int argc, char** argv) { int fd, inum = 0; namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1); if (SEM_FAILED == namedSem) { if (errno != EEXIST) { printf("sem_open error : %s\n", strerror(errno)); return -1; } printf("sem_open %s exist! so open\n", MUTEXNAME); namedSem = sem_open(MUTEXNAME, O_RDWR); } printf("sem_open succ\n", MUTEXNAME); fd = open(FILENAME, O_RDWR|O_CREAT|O_TRUNC, 0777); write(fd, &inum, sizeof(int)); return 0; }
结果说明
[root@rocket ipc]# g++ -g -o ipc_posix_named_sem_create ipc_posix_named_sem_create.cpp –lrt
[root@rocket ipc]# ./ipc_posix_named_sem_create
sem_open succ
[root@rocket ipc]# ./ipc_posix_named_sem_create
sem_open my_named_sem exist! so open
sem_open succ
删除信号灯
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include <string.h> sem_t* namedSem; #define FILENAME "/tmp/count_named_sem.txt" #define MUTEXNAME "my_named_sem" int main(int argc, char** argv) { namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1); if (SEM_FAILED == namedSem) { if (errno != EEXIST) { printf("sem_open error : %s\n", strerror(errno)); return -1; } printf("sem_open %s exist! so open\n", MUTEXNAME); namedSem = sem_open(MUTEXNAME, O_RDWR); } int ret = sem_unlink(MUTEXNAME); if (-1 == ret) { printf("sem_unlink error: %s\n", strerror(errno)); return -1; } printf("sem_unlink %s succ!\n", MUTEXNAME); return 0; }
结果说明
[root@rocket ipc]# g++ -g -o ipc_posix_named_sem_unlink ipc_posix_named_sem_unlink.cpp -lrt
[root@rocket ipc]# ./ipc_posix_named_sem_unlink
sem_open my_named_sem exist! so open
sem_unlink my_named_sem succ!
[root@rocket ipc]# ./ipc_posix_named_sem_unlink
sem_unlink my_named_sem succ!
案例设计:使用信号灯加锁更新文件
u 生成一个文件,我们在里面写一个int,值为0,并初始化一个信号灯,信号数量为1
u 用一个使用信号灯加锁的进程,启动多份更新
u 用一个未使用信号灯加锁的进程,启动多份更新
u 检查加锁和不加锁更新的结果是否符合预期
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> #include <string.h> sem_t* namedSem; #define FILENAME "/tmp/count_named_sem.txt" #define MUTEXNAME "my_named_sem" int main(int argc, char** argv) { int fd, inum, iloop; if (argc != 2) { printf("usage: ./ipc_posix_sem_countlock <loopnum>\n"); return 0; } namedSem = sem_open(MUTEXNAME, O_CREAT|O_EXCL, 0644, 1); if (SEM_FAILED == namedSem) { if (errno != EEXIST) { printf("sem_open error : %s\n", strerror(errno)); return -1; } printf("sem_open %s exist! so open\n", MUTEXNAME); namedSem = sem_open(MUTEXNAME, O_RDWR); } printf("sem_open succ\n", MUTEXNAME); iloop = atoi(argv[1]); fd = open(FILENAME, O_RDWR); for (int i = 0; i < iloop; ++i) { sem_wait(namedSem); lseek(fd, 0, SEEK_SET); read(fd, &inum, sizeof(int)); usleep(1000); inum++; lseek(fd, 0, SEEK_SET); write(fd, &inum, sizeof(int)); sem_post(namedSem); } printf("pid %d countlock down\n", getpid()); return 0; }
结果说明
[root@rocket ipc]# od -i /tmp/count_named_sem.txt
0000000 0
0000004
[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &
[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &
[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &
[root@rocket ipc]# ./ipc_posix_named_sem_countlock 1000 &
pid 115794 countlock down
pid 115795 countlock down
pid 115796 countlock down
pid 115797 countlock down
[root@rocket ipc]# od -i /tmp/count_named_sem.txt
0000000 4000
0000004
运行不加锁版本(去掉上面代码中的sem_wait和sem_post)
[root@rocket ipc]# od -i /tmp/count_named_sem.txt
0000000 0
0000004
[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &
[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &
[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &
[root@rocket ipc]# ./ipc_countlock_without_sem 1000 &
pid 116351 countlock down
pid 116352 countlock down
pid 116353 countlock down
pid 116354 countlock down
[root@rocket ipc]# od -i /tmp/count_named_sem.txt
0000000 2245 没加锁,这里的结果是不正确的
0000004
POSIX基于内存的信号灯
POSIX基于内存的信号灯的sem_wait和sem_post和POSIX有名信号灯是同一个实现,唯一不同在于构造和析构是在内存中进行的,而不是基于文件系统的某个路径名。
在sem_init函数中,如果shared为0,那么待初始化的信号灯是在同一进程的各个线程间共享的,否则该信号灯是在进程间共享的。当shared非0时,该信号灯必须存放在即将使用他的所有进程都能访问的某种类型的共享内存区中。
基于内存的信号灯的持续性由它所在的内存持续性决定。
函数说明
// 初始化一个信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
// 释放信号量
int sem_destory(sem_t *sem)
基于内存的信号灯的使用(线程之间进行同步)
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> sem_t binSem; void* helloWorld(void* arg) { while(1) { // Wait semaphore sem_wait(&binSem); printf("Hello World\n"); } } int main(int argc, char** argv) { // Result for System call int res = 0; // Initialize semaphore sem_init(&binSem, 0, 0); // Create thread pthread_t thdHelloWorld; pthread_create(&thdHelloWorld, NULL, helloWorld, NULL); while(1) { // Post semaphore sem_post(&binSem); printf("In main, sleep several seconds.\n"); sleep(1); } // Wait for thread synchronization void *threadResult; pthread_join(thdHelloWorld, &threadResult); return 0; }
结果说明
[root@rocket ipc]# g++ -g -o ipc_posix_sem_thread ipc_posix_sem_thread.cpp -lrt
[root@rocket ipc]# ./ipc_posix_sem_thread
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
Hello World
In main, sleep several seconds.
基于内存的信号灯的使用(进程之间进行同步,使用共享内存存放信号灯)
# 创建
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <semaphore.h> int main(int argc, char** argv) { sem_t* shm_sem; const char* name = "/dev/shm/my_systemv_shm1"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return -1; } shm_sem=(sem_t*)shmat(shm_id,NULL,0); // Initialize semaphore sem_init(shm_sem, 1, 0); // pshared = 1 return 0; }
结果说明
[root@rocket ipc]# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 gdm 600 393216 2 dest
0x00000000 32769 gdm 600 393216 2 dest
0x00000000 65538 gdm 600 393216 2 dest
0x00000000 98307 gdm 600 393216 2 dest
[root@rocket ipc]# g++ -g -o ipc_posix_sem_mmap_create ipc_posix_sem_mmap_create.cpp –lrt
[root@rocket ipc]# ./ipc_posix_sem_mmap_create
[root@rocket ipc]# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 gdm 600 393216 2 dest
0x00000000 32769 gdm 600 393216 2 dest
0x00000000 65538 gdm 600 393216 2 dest
0x00000000 98307 gdm 600 393216 2 dest
0x00108d43 229380 root 0 32 0
这里看到已经创建成功共享内存并初始化了信号灯。
# 信号灯V操作
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <semaphore.h> int main(int argc, char** argv) { sem_t* shm_sem; const char* name = "/dev/shm/my_systemv_shm1"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return -1; } shm_sem=(sem_t*)shmat(shm_id,NULL,0); while(1) { // Post semaphore sem_post(shm_sem); printf("In main, sleep several seconds.\n"); sleep(1); } return 0; }
# 信号灯P操作
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <semaphore.h> int main(int argc, char** argv) { sem_t* shm_sem; const char* name = "/dev/shm/my_systemv_shm1"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int shm_id=shmget(key, sizeof(sem_t), IPC_CREAT); if(shm_id == -1) { perror("shmget error"); return -1; } shm_sem=(sem_t*)shmat(shm_id,NULL,0); int semvalue; sem_getvalue(shm_sem, &semvalue); printf("current sem value = %d\n", semvalue); while(1) { // Wait semaphore sem_wait(shm_sem); printf("Hello World\n"); } return 0; }
结果说明
writer先跑起来
[root@rocket ipc]# ./ipc_posix_sem_mmap_writer
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
在另一个终端启动reader
[root@rocket ipc]# ./ipc_posix_sem_mmap_reader
current sem value = 7
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
System V信号灯
函数说明
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
// 创建一个新的信号量或是获得一个已存在的信号量键值
int semget(key_t key, int nsems, int semflg);
key:所创建或打开信号量集的键值。需要是唯一的非零整数。
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。
flag:调用函数的操作类型,也可用于设置信号量集的访问权限。
// 用来改变信号量的值(包含P操作和V操作)
struct sembuf{
unsigned short sem_num; /* semaphore number *//*信号灯在信号灯集中的编号*/
short sem_op; /* semaphore operation *//*P操作或者V操作*/
short sem_flg; /* operation flags */
};
int semop(int semid, struct sembuf *sops, unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
sem_num:是相对应的信号量集中的某一个资源,所以其值是一个从0到相应的信号量集的资源总数(ipc_perm.sem_nsems)之间的整数。除非使用一组信号灯了,否则它的取值一般为0。
sem_op:是信号量在一次操作中需要改变的数值。通常只会用到两个值:-1---P操作,1---V操作。
sem_flg:说明函数semop的行为。通常被设置为SEM_UNDO。它将使得操作系统跟着当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。用一个通俗的说法:IPC_UNDO标志保证进程终止后,它对信号量的修改都撤销,好像它从来没有操作过信号量一样。这个标志要特别注意,使用不当容易造成一些诡异的问题。
这里需要强调的是semop同时操作多个信号灯,在实际应用中,对应多种资源的申请或释放。semop保证操作的原子性,这一点尤为重要。尤其对于多种资源的申请来说,要么一次性获得所有资源,要么放弃申请,要么在不占有任何资源情况下继续等待,这样,一方面避免了资源的浪费;另一方面,避免了进程之间由于申请共享资源造成死锁。关于这一点,可以参考http://www.cnblogs.com/linuxbug/p/4840148.html里面的银行家算法,semop就是银行家算法的一个实现。
也许从实际含义上更好理解这些操作:信号灯的当前值记录相应资源目前可用数目;sem_op > 0对应相应进程要释放sem_op数目的共享资源;sem_op=0可以用于对共享资源是否已用完的测试;sem_op<0相当于进程要申请-sem_op个共享资源。再联想操作的原子性,更不难理解该系统调用何时正常返回,何时睡眠等待。
// 允许信号量信息的直接控制(包含初始化信号灯和删除信号灯)
// 这个联合体需要在程序声明,用于semctl函数的SETVAL选项的传值,作为第四个参数
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
}
int semctl(int semid, int semnum, int cmd, …/*union semun arg*/);
IPC_STAT |
获取信号灯信息,信息由arg.buf返回; |
IPC_SET |
设置信号灯信息,待设置信息保存在arg.buf中(在manpage中给出了可以设置哪些信息); |
GETALL |
返回所有信号灯的值,结果保存在arg.array中,参数sennum被忽略; |
GETNCNT |
返回等待semnum所代表信号灯的值增加的进程数,相当于目前有多少进程在等待semnum代表的信号灯所代表的共享资源; |
GETPID |
返回最后一个对semnum所代表信号灯执行semop操作的进程ID; |
GETVAL |
返回semnum所代表信号灯的值; |
GETZCNT |
返回等待semnum所代表信号灯的值变成0的进程数; |
SETALL |
通过arg.array更新所有信号灯的值;同时,更新与本信号集相关的semid_ds结构的sem_ctime成员; |
SETVAL |
设置semnum所代表信号灯的值为arg.val; |
信号灯创建并获取状态
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <sys/sem.h> union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; }; int main(int argc, char** argv) { const char* name = "/dev/shm/my_systemv_shm2"; union semun un; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int semid = semget(key, 1, 0666|IPC_CREAT|IPC_EXCL); if (-1 == semid) { if (errno != EEXIST) { printf("semget error: %s\n", strerror(errno)); return -1; } semid = semget(key, 0, 0666); printf("semget get succ\n"); } else { printf("semget create succ\n"); un.val = 1; if (semctl(semid, 0, SETVAL, un) == -1) { printf("semctl error: %s\n", strerror(errno)); return -1; } } struct semid_ds buf; un.buf = &buf; int ret = semctl(semid, 0, IPC_STAT, un); if (-1 == ret) { printf("semctl error: %s\n", strerror(errno)); return -1; } printf("semid = %d, semvalue = %d\n", semid, un.buf->sem_nsems); return 0; }
结果说明
[root@rocket ipc]# g++ -g -o ipc_systemv_sem_mmap_create ipc_systemv_sem_mmap_create.cpp
[root@rocket ipc]# ./ipc_systemv_sem_mmap_create
semget create succ
semid = 131073, semvalue = 1
[root@rocket ipc]# ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
0x00000000 0 gdm 600 393216 2 dest
0x00000000 32769 gdm 600 393216 2 dest
0x00000000 65538 gdm 600 393216 2 dest
0x00000000 98307 gdm 600 393216 2 dest
------ Semaphore Arrays --------
key semid owner perms nsems
0x00000000 0 root 600 1
0x00108f11 131073 root 666 1
信号灯删除
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <sys/sem.h> union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; }; int main(int argc, char** argv) { const char* name = "/dev/shm/my_systemv_shm2"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int semid = semget(key, 1, 0666|IPC_CREAT|IPC_EXCL); if (-1 == semid) { if (errno != EEXIST) { printf("semget error: %s\n", strerror(errno)); return -1; } semid = semget(key, 0, 0666); printf("semget get succ\n"); } else { printf("semget create succ\n"); } int ret = semctl(semid, 0, IPC_RMID); if (-1 == ret) { printf("semctl error: %s\n", strerror(errno)); return -1; } printf("semid %d delete succ\n", semid); return 0; }
结果说明
[root@rocket ipc]# g++ -g -o ipc_systemv_sem_mmap_delete ipc_systemv_sem_mmap_delete.cpp
[root@rocket ipc]# ipcs
------ Semaphore Arrays --------
key semid owner perms nsems
0x00000000 0 root 600 1
0x00108f11 131073 root 666 1
[root@rocket ipc]# ./ipc_systemv_sem_mmap_delete
semget get succ
semid 131073 delete succ
[root@rocket ipc]# ipcs
------ Semaphore Arrays --------
key semid owner perms nsems
0x00000000 0 root 600 1
可以看到,这里已经成功删除semid为131073的信号灯。
也可以使用ipcrm -s命令删除,ipcsrm -s semid。
信号灯V操作
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <sys/sem.h> union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; }; int sem_p(int sem_id) { struct sembuf sem_buf; sem_buf.sem_num = 0; // 信号量编号 sem_buf.sem_op = -1; // P操作 sem_buf.sem_flg = 0; //sem_buf.sem_flg = SEM_UNDO; // 系统退出前未释放信号量,系统自动释放 if (semop(sem_id, &sem_buf, 1) == -1) { perror("Sem P operation"); exit(1); } return 0; } int sem_v(int sem_id) { struct sembuf sem_buf; sem_buf.sem_num = 0; sem_buf.sem_op = 1; // V操作 sem_buf.sem_flg = 0; // sem_buf.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_buf, 1) == -1) { perror("Sem V operation"); exit(1); } return 0; } int main(int argc, char** argv) { const char* name = "/dev/shm/my_systemv_shm2"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int semid = semget(key, 1, 0666); if (-1 == semid) { printf("semget get error: %s\n", strerror(errno)); } else { printf("semget create succ\n"); } union semun un; printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0)); while(1) { // Post semaphore sem_v(semid); printf("In main, sleep several seconds.\n"); sleep(1); } return 0; }
结果说明
[root@rocket ipc]# ./ipc_systemv_sem_mmap_writer
semget create succ
semid = 294913, semvalue = 0
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
In main, sleep several seconds.
信号灯P操作
#include <sys/mman.h> #include <fcntl.h> #include <sys/mman.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/ipc.h> #include <sys/shm.h> #include <pthread.h> #include <sys/sem.h> union semun { int val; /* Value for SETVAL */ struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */ unsigned short *array; /* Array for GETALL, SETALL */ struct seminfo *__buf; }; int sem_p(int sem_id) { struct sembuf sem_buf; sem_buf.sem_num = 0; // 信号量编号 sem_buf.sem_op = -1; // P操作 sem_buf.sem_flg = 0; //sem_buf.sem_flg = SEM_UNDO; // 系统退出前未释放信号量,系统自动释放 if (semop(sem_id, &sem_buf, 1) == -1) { perror("Sem P operation"); exit(1); } return 0; } int sem_v(int sem_id) { struct sembuf sem_buf; sem_buf.sem_num = 0; sem_buf.sem_op = 1; // V操作 sem_buf.sem_flg = 0; //sem_buf.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_buf, 1) == -1) { perror("Sem V operation"); exit(1); } return 0; } int main(int argc, char** argv) { const char* name = "/dev/shm/my_systemv_shm2"; key_t key = ftok(name,0); if (key == -1) { perror("ftok error"); return -1; } int semid = semget(key, 1, 0666); if (-1 == semid) { printf("semget get error: %s\n", strerror(errno)); } else { printf("semget create succ\n"); } union semun un; printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0)); while(1) { // Wait semaphore sem_p(semid); printf("Hello World\n"); printf("semid = %d, semvalue = %d\n", semid, semctl(semid, 0, GETVAL, 0)); } return 0; }
结果说明
[root@rocket ipc]# ./ipc_systemv_sem_mmap_reader
semget create succ
semid = 294913, semvalue = 3
Hello World
semid = 294913, semvalue = 2
Hello World
semid = 294913, semvalue = 1
Hello World
semid = 294913, semvalue = 0
semvalue为0的时候,semop就会阻塞,直到另一个进程调用sem_v函数。