网络编程(40)—— 使用信号量semaphore进行多进程间的同步

        本文主要介绍下在多进程中使用信号量semaphore的方法。我们先来看下sem_init的原型,熟悉决定进程共享或者线程共享的方法:

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);


        通过设置pshared的值来控制信号量是属于进程间共享还是线程间共享,若pshared为0表明是多线程共享,否则就是多进程间共享。接下来我们实验思路是:创建两个进程,一个进程负责读取用户在界面输入的数据,然后存入本地的test.txt文件;另一个进程负责读取该文件,然后在标准输出上显示读取的内容。为此,我们需要创建两个个支持两个进程访问的信号量sem1和sem2,读文件时需要获取sem1信号,读取结束后释放sem2信号;写文件需要获取sem2信号,写文件结束后方式sem1信号。sem2的初始值为1,sem1的初始值为0,以保证先写入再进行读取,源代码如下,稍后挑关键内容进行解释:

 

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
#include<string.h>
#include<sys/mman.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#define BUF_SIZE 30

void readfile(sem_t* psem1,sem_t* psem2)
{
    FILE* fp;
    char buf[BUF_SIZE];
    int str_len,str_seek=0;
    while(1)
    {
        sem_wait(psem1);
        fp=fopen("data.txt","r+");
        if(fp==NULL)
        return ;
        memset(buf,0,sizeof(BUF_SIZE));
        fseek(fp,str_seek,SEEK_SET);
        str_len=fread(buf,sizeof(char),BUF_SIZE-1,fp);
        buf[str_len]=0;
        str_seek+=str_len;
        fputs("output:",stdout);
        puts(buf);
        fclose(fp);
        sem_post(psem2);
    }
}
void writefile(sem_t* psem1,sem_t* psem2)
{
        FILE* fp;
        char buf[BUF_SIZE];
        while(1)
        {
            sem_wait(psem2);
            fp=fopen("data.txt","a");
            if(fp==NULL)
            return;
            memset(buf,0,BUF_SIZE);
            fputs("Input:",stdout);
            fgets(buf,BUF_SIZE,stdin);
            fwrite(buf,sizeof(char),strlen(buf),fp);
            fclose(fp);
            sem_post(psem1);
        }
}

int main()
{
    int pid;
    int fd1,fd2;
    void* pv;
    sem_t* psem1;
    sem_t* psem2;
    fd1=open("data1",O_CREAT|O_RDWR|O_TRUNC,0666);
    fd2=open("data2",O_CREAT|O_RDWR|O_TRUNC,0666);\
    ftruncate(fd1,8192);
    ftruncate(fd2,8192);
    //lseek(fd,5000,SEEK_SET);
    psem1=(sem_t*)mmap(NULL,sizeof(sem_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd1,0);
    psem2=(sem_t*)mmap(NULL,sizeof(sem_t),PROT_READ|PROT_WRITE,MAP_SHARED,fd2,0);
    sem_init(psem1,1,0);
    sem_init(psem2,1,1);
    pid=fork();
    if(pid==0)
    {
        puts("进入子进程");
        writefile(psem1,psem2);
    }
    else
    {
        puts("进入父进程");
        readfile(psem1,psem2);
    }
    sem_destroy(psem1);
    sem_destroy(psem2);
    munmap(psem1,sizeof(sem_t));
    munmap(psem2,sizeof(sem_t));
    close(fd1);
    close(fd2);
    return 0; 
}


        为了能够跨进程使用semaphore,我们引入了跨进程的技术mmap,第61、第62行分别打开了两个mmap需要映射的文件,和我们平时用的open函数不同,这里面为程序赋予了该文件的666权限。这点很重要,因为mmap需要映射的本地文件必须明确赋予其可读写的权限,否则无法通信。
        第63行和第64行分别设置两个本地映射文件的大小,以保证有充分的空间在mmap中映射并容纳我们定义的sem_t变量。这点也很重要,如果空间不够会造成总线错误。
        第66行和第67行分别利用mmap在共享内存中映射了两个sem_t类型的指针,这就是我们需要sem_init的信号量。
        第68、69行开始初始化信号量。
        70行fork了两个进程,在子进程中我们进行写操作,在主进程中我们进行读操作。读写操作的代码比较简单,在这里不再多说。
        第81到86行在使用完信号量后分别是销毁信号量、释放共享内存、关闭文件操作符。
       程序写到这里基本上完成了这个实验,可以看下执行的结果:

 

 

 

[Hyman@Hyman-PC semphare]$ ./a.out 
进入父进程
进入子进程
Input:你好
Output:你好

Input:

 


        我们可以简单总结下在多进程中使用信号量的步骤:
(1)open()用于进行mmap映射的文件,得到文件操作符fd;
(2)把映射文件用ftruncate或者fseek重新设置大小,以保证有足够的空间容纳我们需要传递的sem_t变量;
(3)利用mmap函数在共享内存中创建sen_t类型的指针。
(4)用sem_init()函数初始化第(3)步中创建的指针,也就得到了我们需要的信号量。
(5)用sem_wait()和sem_post()函数进行信号量的等待和释放。
(6)用sem_destroy()销毁信号量。

 

 

(7)用munmap()释放共享内存以及用close()函数关闭文件操作符。

 

 

Github位置:
https://github.com/HymanLiuTS/NetDevelopment
克隆本项目:
git clone git@github.com:HymanLiuTS/NetDevelopment.git
获取本文源代码:
git checkout NL40

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值