Chinaunix首页 | 论坛 | 博客
  • 博客访问: 259463
  • 博文数量: 74
  • 博客积分: 1470
  • 博客等级: 上尉
  • 技术积分: 793
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-25 21:01
文章分类

全部博文(74)

文章存档

2011年(1)

2010年(32)

2009年(32)

2008年(9)

我的朋友

分类: LINUX

2009-12-22 16:36:25

信号灯是作为进程或线程之间同步或互斥的,信号灯分为有名信号灯和基于内存的"无名"信号灯.
有名信号灯对应与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 系统编程 ->进程通讯 -> 信号灯





阅读(1214) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~