Linux学习笔记(06-11)POSIX信号量

POSIX信号量详解
1.无名信号量

       无名信号量的创建就像声明一般的变量一样,例如:sem_t sem_id。然后再初始化该无名信号量。

       无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。

       无名信号量必须是多个进程(线程)的共享变量,要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。

        无名信号量相关函数:

        sem_destroy

        int sem_init(sem_t *sem, int pshared, unsigned int value);
        1)pshared==0 用于同一多线程的同步;

        2)若pshared>0 用于多个相关进程间的同步(即由fork产生的)


        int sem_getvalue(sem_t *sem, int *sval);
        取回信号量sem的当前值,把该值保存到sval中。
        若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
        1) 返回0

        2) 返回阻塞在该信号量上的进程或线程数目

        linux采用返回的第一种策略。


        sem_wait(或sem_trywait)相当于P操作,即申请资源。这是一个阻塞的函数,测试所指定信号量的值,它的操作是原子的。
        若sem>0,那么它减1并立即返回。

        若sem==0,则睡眠直到sem>0,此时立即减1,然后返回。


        int sem_trywait(sem_t *sem); // 非阻塞的函数

        若sem==0,不是睡眠,而是返回一个错误EAGAIN。


        sem_post相当于V操作,释放资源。
        int sem_post(sem_t *sem);
        把指定的信号量sem的值加1;
        呼醒正在等待该信号量的任意线程。

        在这些函数中,只有sem_post是信号安全的函数,它是可重入函数


        例如:(线程间同步)
        #include <pthread.h>
        #include <semaphore.h>
        #include <sys/types.h>
        #include <stdio.h>
        #include <unistd.h>
        int number; // 被保护的全局变量
        sem_t sem_id;
        void* thread_one_fun(void *arg)
        {
        sem_wait(&sem_id);
        printf("thread_one have the semaphore\n");
        number++;
        printf("number = %d\n",number);
        sem_post(&sem_id);
        }
        void* thread_two_fun(void *arg)
        {
        sem_wait(&sem_id);
        printf("thread_two have the semaphore \n");
        number--;
        printf("number = %d\n",number);
        sem_post(&sem_id);
        }
        int main(int argc,char *argv[])
        {
        number = 1;
        pthread_t id1, id2;
        sem_init(&sem_id, 0, 1);
        pthread_create(&id1,NULL,thread_one_fun, NULL);
        pthread_create(&id2,NULL,thread_two_fun, NULL);
        pthread_join(id1,NULL);
        pthread_join(id2,NULL);
        printf("main,,,\n");
        return 0;
        }
        例程:
        int number; // 被保护的全局变量
        sem_t sem_id1, sem_id2;
        void* thread_one_fun(void *arg)
        {
        sem_wait(&sem_id1);
        printf("thread_one have the semaphore\n");
        number++;
        printf("number = %d\n",number);
        sem_post(&sem_id2);
        }
        void* thread_two_fun(void *arg)
        {
        sem_wait(&sem_id2);
        printf("thread_two have the semaphore \n");
        number--;
        printf("number = %d\n",number);
        sem_post(&sem_id1);
        }
        int main(int argc,char *argv[])
        {
        number = 1;
        pthread_t id1, id2;
        sem_init(&sem_id1, 0, 1); // 空闲的
        sem_init(&sem_id2, 0, 0); // 忙的
        pthread_create(&id1,NULL,thread_one_fun, NULL);
        pthread_create(&id2,NULL,thread_two_fun, NULL);
        pthread_join(id1,NULL);
        pthread_join(id2,NULL);
        printf("main,,,\n");
        return 0;
        }
        (无名信号量在相关进程间的同步)
        在初始化mutex的时候,由pshared = 1指定了mutex处于共享内存区域,所以此时mutex变成了父子进程共享的一个变量。
        #include <semaphore.h>
        #include <stdio.h>
        #include <errno.h>
        #include <stdlib.h>
        #include <unistd.h>
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>
        #include <sys/mman.h>
        int main(int argc, char **argv)
        {
        int fd, i,count=0,nloop=10,zero=0,*ptr;
        sem_t mutex;
        //open a file and map it into memory
        fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
        write(fd,&zero,sizeof(int));
        ptr = mmap( NULL,sizeof(int),PROT_READ |
        PROT_WRITE,MAP_SHARED,fd,0 );
        close(fd);
        /* create, initialize semaphore */
        if( sem_init(&mutex,1,1) < 0) //
        {
        perror("semaphore initilization");
        exit(0);
        }
        if (fork() == 0)
        { /* child process*/
        for (i = 0; i < nloop; i++)
        {
        sem_wait(&mutex);
        printf("child: %d\n", (*ptr)++);
        sem_post(&mutex);
        }
        exit(0);
        }
        /* back to parent process */
        for (i = 0; i < nloop; i++)
        {
        sem_wait(&mutex);
        printf("parent: %d\n", (*ptr)++);
        sem_post(&mutex);
        }
        exit(0);

        }


        2.有名信号量
        有名信号量的特点是把信号量的值保存在文件中。
        这决定了它的用途非常广:既可以用于线程,也可以用于相关进程间,甚至是不相关的进程。
        (a)有名信号量能在进程间共享的原因
        由于有名信号量的值是保存在文件中的,所以对于相关进程来说,子进程是继承了父进程的文件描述符,那么子进程所继承的文件描述符所指向的文件是和父进程一样的,当然文件里面保存的有名信号量值就共享了。
        (b)有名信号量相关函数说明
        有名信号量在使用的时候,和无名信号量共享sem_wait和sem_post函数。区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。
        (1)打开一个已存在的有名信号量,或创建并初始化一个有名信号量。一个单一的调用就完成了信号量的创建、初始化和权限的设置。
        sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);
        name是文件的路径名;
        Oflag 有O_CREAT或O_CREAT|EXCL两个取值;
        mode_t控制新的信号量的访问权限;
        Value指定信号量的初始化值。
        注意:
        这里的name不能写成/tmp/aaa.sem这样的格式,因为在linux下,sem都是创建在/dev/shm目录下。你可以将name写成“/mysem”或“mysem”,创建出来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。也千万不要写“/tmp/mysem”之类的。
        当oflag = O_CREAT时,若name指定的信号量不存在时,则会创建一个,而且后面的mode和value参数必须有效。若name指定的信号量已存在,则直接打开该信号量,同时忽略mode和value参数。
        当oflag = O_CREAT|O_EXCL时,若name指定的信号量已存在,该函数会直接返回error。
        (2) 一旦你使用了一信号量,销毁它们就变得很重要。
在做这个之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量,注意如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作用。也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。
        (c)有名信号量在无相关进程间的同步

        前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于共享内存区,只有这样才能被无相关的进程所共享。

        在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。

<u>File1: server.c </u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize semaphore
mutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if(mutex == SEM_FAILED)
{
perror("unable to create semaphore");
sem_unlink(SEM_NAME);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,IPC_CREAT|0666);
if(shmid<0)
{
perror("failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start writing into memory
s = shm;
for(ch='A';ch<='Z';ch++)
{
sem_wait(mutex);
*s++ = ch;
sem_post(mutex);
}
//the below loop could be replaced by binary semaphore
while(*shm != '*')
{
sleep(1);
}
sem_close(mutex);
sem_unlink(SEM_NAME);
shmctl(shmid, IPC_RMID, 0);
exit(0);
}
<u>File 2: client.c</u>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[]= "vik";
int main()
{
char ch;
int shmid;
key_t key;
char *shm,*s;
sem_t *mutex;
//name the shared memory segment
key = 1000;
//create & initialize existing semaphore
mutex = sem_open(SEM_NAME,0,0644,0);
if(mutex == SEM_FAILED)
{
perror("reader:unable to execute semaphore");
sem_close(mutex);
exit(-1);
}
//create the shared memory segment with this key
shmid = shmget(key,SHMSZ,0666);
if(shmid<0)
{
perror("reader:failure in shmget");
exit(-1);
}
//attach this segment to virtual memory
shm = shmat(shmid,NULL,0);
//start reading
s = shm;
for(s=shm;*s!=NULL;s++)
{
sem_wait(mutex);
putchar(*s);
sem_post(mutex);
}
//once done signal exiting of reader:This can be replaced by
another semaphore
*shm = '*';
sem_close(mutex);
shmctl(shmid, IPC_RMID, 0);
exit(0);

}

转自:http://blog.csdn.net/qinxiongxu/article/details/7830537


Linux的信号量机制
Linux提供了一组精心设计的信号量接口来对信号进行操作,它们不只是针对二进制信号量,下面将会对这些函数进行介绍,但请注意,这些函数都是用来对成组的信号量值进行操作的。它们声明在头文件sys/sem.h中。


1、semget函数
它的作用是创建一个新信号量或取得一个已有信号量,原型为:
[cpp] view plaincopyprint?
int semget(key_t key, int num_sems, int sem_flags);  
第一个参数key是整数值(唯一非零),不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。


第二个参数num_sems指定需要的信号量数目,它的值几乎总是1。


第三个参数sem_flags是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。


semget函数成功返回一个相应信号标识符(非零),失败返回-1.


2、semop函数
它的作用是改变信号量的值,原型为:
[cpp] view plaincopyprint?
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);  
sem_id是由semget返回的信号量标识符,sembuf结构的定义如下:
[cpp] view plaincopyprint?
struct sembuf{  
    short sem_num;//除非使用一组信号量,否则它为0  
    short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,  
                    //一个是+1,即V(发送信号)操作。  
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
                    //并在进程没有释放该信号量而终止时,操作系统释放信号量  
};  
3、semctl函数
该函数用来直接控制信号量信息,它的原型为:
[cpp] view plaincopyprint?
int semctl(int sem_id, int sem_num, int command, ...);  
如果有第四个参数,它通常是一个union semum结构,定义如下:
[cpp] view plaincopyprint?
union semun{  
    int val;  
    struct semid_ds *buf;  
    unsigned short *arry;  
};  
前两个参数与前面一个函数中的一样,command通常是下面两个值中的其中一个
SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值