信号灯是作为进程或线程之间同步或互斥的,信号灯分为有名信号灯和基于内存的"无名"信号灯.
有名信号灯对应与sem_open,sem_wait,sem_post,sem_close,sem_unlink等操作.
而"无名"信号灯则对应于sem_init,sem_wait,sem_post,sem_destory等操作.
有名信号灯之所以叫有名信号灯,是因为,在sem_open的时候要指定一个name,这个信号灯由name参数标识,name通常指代文件系统中的某个文件.
而"无名"信号灯是基于内存的信号灯,它们由应用程序分配信号灯的内存空间,然后由系统初始化它们的值.
1.sem_open需注意事项
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
oflag参数可以是O_CREAT(创建一个信号灯)或O_CREAT|O_EXCL(如果没有指定的信号灯就创建),
如果指定了O_CREAT,那么也得指定第三个和第四个参数,
如果不指定的话,mode和value值会取默认值.如果你认为value的默认值为1,那你就错了,value的默认值为内核现在能支持的最大值.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <semaphore.h>
//#define SEM_MODE (S_IRUSR | S_IWUSR)
int main(int argc, char *argv[])
{
int value;
sem_t* psem;
psem = sem_open("test1",O_CREAT);
//psem = sem_open("test1",O_CREAT,SEM_MODE,1);
if(psem==SEM_FAILED) {
printf("sem error\n");
printf("%s\n",strerror(errno));
exit(EXIT_FAILURE);
}
sem_getvalue(psem,&value);
printf("%d\n",value);
sem_close(psem);
sem_unlink("test1");
return 0;
}
|
# gcc test.c -lrt
# ./a.out
134513772
value参数指定信号灯的初始值,通常用来指定共享资源的数量.该初始不能超过SEM_VALUE_MAX,这个常值必须低于为32767.二值信号灯的初始值通常为1
如果指定了O_CREAT(而没有指定O_EXCL),那么只有所需的信号灯尚未存在时才初始化它,
所需信号灯已存在条件下指定O_CREAT不是一个错误,该标志的意思仅仅是“如果所需信号灯尚未存在,那就创建并初始化它”。
但是所需信号灯等已存在条件下指定O_CREAT|O_EXCL却是一个错误,下面的例子会证实这一点。
2.Posix有名信号灯的值是随内核持续的.也就是说,一个进程创建了一个信号灯,这个进程结束后,这个信号灯还存在,并且信号灯的值也不会改变。
我们修改一下上面的代码,去掉sem_unlink,并把sem_open中的oflag设为O_CREAT|O_EXCL,
前面说过,如果所需信号灯等已存在条件下,sem_open指定O_CREAT|O_EXCL是一个错误.现在证实一下:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <semaphore.h>
#define SEM_MODE (S_IRUSR | S_IWUSR)
int main(int argc, char *argv[])
{
int value;
sem_t* psem;
psem = sem_open("test1",O_CREAT|O_EXCL,SEM_MODE,1);
if(psem==SEM_FAILED) {
printf("sem error\n");
printf("%s\n",strerror(errno));
exit(EXIT_FAILURE);
}
sem_getvalue(psem,&value);
printf("%d\n",value);
sem_close(psem);
// sem_unlink("test1");
return 0;
}
|
$ gcc test1.c -lrt
$ ./a.out
1
$ ./a.out
sem error
File exists
运行第二次的时候,由于第一次sem_open以后没有sem_unlink,所以信号灯还是在内核存在的,
第二次运行时由于信号灯已存在,而且sem_open指定O_CREAT|O_EXCL,所以出错.
3.当持有某个信号灯锁的进程没有释放它就终止时,内核并不给该信号灯解锁.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <semaphore.h>
#define SEM_MODE (S_IRUSR | S_IWUSR)
int main(int argc, char *argv[])
{
int i;
int ret_val;
int value;
sem_t* psem;
psem = sem_open("test3",O_CREAT,SEM_MODE,1);
if(psem==SEM_FAILED) {
printf("sem error\n");
printf("%s\n",strerror(errno));
}
printf("Begin to create process...\n");
sem_getvalue(psem,&value);
printf("%d\n",value);
if (fork() == 0 ) {//son
printf("son process\n");
sem_wait(psem);
printf("son in the critical region\n");
sem_post(psem);
sem_close(psem);
exit(1);
}
printf("fathre process\n");
sem_wait(psem);
printf("father in the critical region\n");
sem_post(psem);
printf("Now, the main process returns.\n");
sem_close(psem);
sem_unlink("test3");
return 0;
}
|
$ gcc test.c -lrt
$ ./a.out
Begin to create process...
1
son process
son in the critical region
fathre process
father in the critical region
Now, the main process returns.
可以看到上面的程序执行很好,
但是如果在子进程中把exit提前至sem_post之前
if (fork() == 0 ) {//son
printf("son process\n");
sem_wait(psem);
printf("son in the critical region\n");
exit(1);
sem_post(psem);
sem_close(psem);
}
|
$ gcc test.c -lrt
$ ./a.out
Begin to create process...
1
son process
son in the critical region
fathre process
在这里一直等待下去
可见当子进程sem_wait->exit后,父进程会一直在sem_wait处等待.
4.有名信号灯既可用于线程间的同步,又可以用于进程间的同步.
5.posix有名信号灯是通过内核持续的,一个进程创建一个信号灯,另外的进程可以通过该信号灯的外部名(创建信号灯使用的文件名)来访问它。
posix基于内存的信号灯的持续性却是不定的,
如果基于内存的信号灯是由单个进程内的各个线程共享的,那么该信号灯就是随进程持续的,当该进程终止时它也会消失。
如果某个基于内存的信号灯是在不同进程间同步的,该信号灯必须存放在共享内存区中,这时只要该共享内存区存在,该信号灯就存在。
6.基于内存的信号灯应用于进程很麻烦,而有名信号灯却很方便,
基于内存的信号灯比较适合应用于一个进程的多个线程。
reference:
Posix多线程编程学习笔记(三)—信号灯Linux 系统编程 ->进程通讯 -> 信号灯
阅读(1244) | 评论(0) | 转发(0) |