您的位置:首页 > 运维架构 > Linux

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

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

一、什么是进程间的同步

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

二、生产者消费者的问题

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

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

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

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

这个过程需要三个文件,生产者,消费者和缓冲区,这里缓冲区我们用一个文件。也就是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目录下的文件,可以看到打印出来了我们想要的信息,也就是产品这下完整了。

这篇帖子就总结到这里,如有不正确的地方还请指出,大家共同进步!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: