linux应用编程笔记(13)信号量同步编程

摘要: 总结了进程间同步的机制,如何利用同步机制处理消费者和生产者的问题,最后用实例加深了理解。


一、什么是进程间的同步

    进程间的同步,指的是一组并发进程,相互合作,相互等待,使得各自按照一定的顺序执行的过程称为进程间的同步。


二、生产者消费者的问题

    这个问题的描述如下:有一群生产者进程在生产消息,并将此 消息提供给消费者进程去消费。为使生产者进程和消费者进程能 并发进行,在他们之间设置了一个具有一个或多个缓冲区的缓冲池,生产者进程可以将它所生产的消息放入一个缓冲区中,消费者进程可以从一个缓冲区中取得一个消息消费。

    那么这个过程会出现什么问题呢?如果消费者消费者消费完了,生产者还没有生产出来,那么消费者就会一直等待。反过来,生产者一直生产,但是消费者没有去消费,那么缓冲区就会满了,生产者也会停止。

    常见的解决这个问题的方法就是信号量的同步编程。


三、不带信号量同步编程的生产者和消费者程序

    这个过程需要三个文件,生产者,消费者和缓冲区,这里缓冲区我们用一个文件。也就是producer.c consumer.c和product.txt

    producer.c做哪些事情呢?就是创建缓冲区,即product.txt,然后休息,这里的休息其实是模仿我们在实际进程当中遇到的没有生产出来,这样可以看到系统出现的故障现象,休息完了之后我们再往里写东西。

    consumer.c做的事情就很简单了,就是打开缓冲区文件,然后读走数据,也就是消费的意思了。

下面在同一个文件夹下面创建这三个文件,producer.c文件内容如下:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <error.h>
 
int main(void)
{
    int fd;
    /*打开文件产品*/
    fd=open("./product.txt",O_RDWR);
    if(fd==-1)
       {
           printf("openerror!\n");
           exit(0);
           }
    /*休息*/
    sleep(20);
    /*向文件里写入内容*/
    write(fd,"myproduct",20);
    /*关闭文件*/
    close(fd);
    return 0;
    }
    编译运行以后可以看到producer会休息20S,然后这时候我们编写消费者的程序。
consumer.c如下:
#include <stdlib.h>
#include <stdio.h>
 
int main(void)
{
    system("cp./product.txt ./ship/");
   
    return0;
 }

    这里使用了一个系统调用,可以调用命令cp把当前目录下的产品复制到当前目录下的ship目录下,如果生产者在休息,但是还是被拷贝走了,我们的产品就是不完整的,就会出错误。

    这里编译运行之后看效果,首先运行producer然后紧接着运行consumer,这时候切到ship目录下免去看拷贝的product,里面是空的,这样就说明不行了,他们之间没有一个机制保证一个生产好了,另一个采取拿,那一个拿完了,另一个紧接着生产,如此循环才是正确的生产者和消费者的关系。


四、利用信号量同步机制的生产者消费者问题

    其实这个同步机制很简单,我们在互斥的时候是先获得信号量,再释放,这里同步初始值为0,互斥初始值为1,也就是在生产者一开始先创建一个信号量,再生产者生产完毕之后,我们在释放,然后消费者那里,就是获取信号量,这样只有生产者生产完了消费者才会拿到消费产品。

    程序如下:

<span style="font-size:18px;"><strong>producer</strong>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
 
int main(void)
{
    int fd;
    key_t key;//键值
    int semid;
    int retval;
    struct sembuf sops;
    /*创建信号量*/
    key= ftok("/home/passionbird",2);//这里可以利用一个目录创建多个键值,只要他们的项目编号不一样就可以
    semid=semget(key,1,IPC_CREAT);//将键值传入,我们这里信号量集合里就只有一个信号量,因为还没有,所以需要创建,加上IPC_CREAT
   
    /*设置信号量的初始值*/
    retval=semctl(semid,0,SETVAL,0);//设置为0,此处是同步操作
    printf("theinit value is:%d\n",retval);//两处打印是为了确保初始值为0
 
    /*打开文件产品*/
    fd=open("./product.txt",O_RDWR);
    if(fd==-1)
       {
           printf("openerror!\n");
           exit(0);
           }
    /*休息*/
    sleep(20);
 
    /*向文件里写入内容*/
    write(fd,"myproduct",20);
    /*释放信号量*/
    sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0
    sops.sem_op= +1;//+1即释放了信号量,写成+1是为了便于理解
    sops.sem_flg= SEM_UNDO;//sop的第三个参数
    semop(semid,&sops,1);   
 
    retval=semctl(semid,0,GETVAL);//设置为0,此处是同步操作
    printf("theinit value is:%d\n",retval);//两处打印是为了确保初始值为0
    /*关闭文件*/
    close(fd);
     return 0;
    }</span>
<span style="font-size:18px;">
<strong>consumer</strong>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
 
int main(void)
{
    key_t key;
    int semid;
    int retval;
    struct sembuf sops;
    /*先打开信号量集合*/
    key= ftok("/home/passionbird",2);//利用相同的键值可以关联同一个信号量
    semid=semget(key,1,IPC_CREAT);//将键值传入,使得a,b打开的信号量是同一个信号量,这里已经有了不会再创建
    /*获取信号量*/
    sops.sem_num= 0;//因为只有一个信号量,所以在操作数组中的编号为0
    sops.sem_op= -1;//-1即获取走了信号量
    sops.sem_flg= SEM_UNDO;//sop的第三个参数
    semop(semid,&sops,1);    //传入返回的semid
 
    retval=semctl(semid,0,GETVAL);
    printf("theinit value is:%d\n",retval); 
    /*取走产品*/
    system("cp./product.txt ./ship/");
 
    return 0;
    }</span>

    最终查看ship目录下的文件,可以看到打印出来了我们想要的信息,也就是产品这下完整了。

    这篇帖子就总结到这里,如有不正确的地方还请指出,大家共同进步!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值