进程间通信

1.使用管道进行通信

 server 创建管道mypipe,并以只读方式打开.  

 client 以只写方式打开管道.   

 两者借助管道进行通信,若有一方退出,则另一方也退出。

server.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#define ERR_EXIT(m) \
do {\
     perror(m);\
     exit(EXIT_FAILURE);\
   }while(0)

int main()
{
  umask(0);
  if(mkfifo("mypipe", 0644)< 0)
  {
      ERR_EXIT("mkfifo");
  }
  int rfd=open("mypipe",O_RDONLY);
   if(rfd<0)
   {
       ERR_EXIT("open");
   }
   char buf[1024];
   while(1)
   {
       printf("Please wait....\n");
       ssize_t s=read(rfd,buf,sizeof(buf)-1);
       if(s>0)
       {
           buf[s-1]=0;
           printf("Client say# %s\n", buf);
       }
       else if(s==0)
       {
           printf("client quit,exit now!\n");
           exit(EXIT_SUCCESS);
       }
       else
       {
           ERR_EXIT("read");
       }
       
   }
   close(rfd);
   return 0;
}

client.c

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>

#define ERR_EXIT(m)\
    do{\
    perror("m");\
    exit(EXIT_FAILURE);\
    }while(0)

int main()
{
    int wfd=open("mypipe",O_WRONLY);
    if(wfd<0)
    {
        ERR_EXIT("open");
    }
    
    char buf[1024];
    while(1)
    {
        buf[0]=0;
        printf("Please enter#");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf)-1); 
        if(s>0)
        {
            buf[s]=0;
            write(wfd, buf, strlen(buf));
        }
        else
        {
            ERR_EXIT("read");
        }
    }

    close(wfd);
    return 0;
}
result

管道内置了同步互斥的机制,读写操作互斥进行,保证了通信的可靠性。



2.使用消息队列进行通信

说明:

1.server 创建消息队列,并且等待client 发送消息,接收到client发的消息后,再向client发送消息。

   client 得到消息队列, 向server发送消息,之后接受server的消息。


2. msgsnd和msgrcv系统调用函数都内置了同步互斥的机制,

  如果没有可发送的消息msgsnd就会自己阻塞。

  如果没有可接受的消息msgrcv就会自己阻塞。


3.关于msgget()中flag的说明:

server 的 flag:IPC_CREAT | IPC_EXCL | 0644

client  的 flag:IPC_CREAT

IPC_CREAT | IPC_EXCL 表示想要获得一个全新的消息队列标识符,如果对应的key已经被占用,则错误返回负数值。0644是权限设置。

IPC_CREAT表示如果对应的key已经被占用,就得到key对应的消息队列标识符,如何key未被占用就,创建新的消息队列。

由于client 中 flag 未对消息队列的权限做出设置,所以之后对消息队列的 msgsnd 和 msgrcv 操作会出现 “没有权限访问”的相关错误
所以server必须首先运行,为获得一个全新的消息队列。

comm.h

#pragma once
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#include<string.h>

#define PATHNAME "."
#define PROJ_ID 0x6666

#define SERVER_TYPE 1
#define CLIENT_TYPE 2

struct msgbufs
{
  long mtype;
  char mtext[1024];
};

int creatMsgQueue();

int getMsgQueue();

int destroyMsgQueue(int msgid);

int sendMsg(int msgid, int who ,char *msg);

int recvMsg(int msgid, int recvType, char out[]);

comm.c

#include"common.h"

//success > 0 failed==-1

static int commMsgQueue(int flags)
{
 
    key_t _key=ftok(PATHNAME,PROJ_ID);
   
    if(_key<0)
    {
       perror("ftok");
       return -1;
    }

    int msgid=msgget(_key,flags);

    if(msgid<0)
    {
       perror("msgget");
    }
   
    return msgid;

}

int creatMsgQueue()
{
    return commMsgQueue (IPC_CREAT|IPC_EXCL|0666);
}

int getMsgQueue()
{
    return commMsgQueue(IPC_CREAT);
}

int destroyMsgQueue(int msgid)
{
    if(msgctl(msgid,IPC_RMID,NULL)<0)
    {
        perror("msgctl");
        return -1;
    }
    return 0;
}

int sendMsg(int msgid,int who ,char *msg)
{
    struct msgbufs buf;
    buf.mtype=who;
    strcpy(buf.mtext,msg);

    if(msgsnd(msgid,(void*)&buf,sizeof(buf.mtext),0)<0)
    {
        perror("msgsnd");
        return -1;
    }
    return 0;
}

int recvMsg(int msgid, int recvType,char out[])
{
    struct msgbufs buf;
    if(msgrcv(msgid,(void*)&buf,sizeof(buf.mtext),recvType ,0)<0)
    {
        perror("msgrcv");
            return -1;
    }
    strcpy(out,buf.mtext);
    return 0;

}

server.c

#include<unistd.h>
#include"common.h"

int main()
{
    int msgid=creatMsgQueue();
    char buf[1024];
    while(1)
    {
        buf[0]=0;
        recvMsg(msgid,CLIENT_TYPE,buf);
        printf("cilent# %s\n", buf);

        printf("Please Enter#");
        fflush(stdout);
        ssize_t s= read(0,buf,sizeof(buf));
        if(s>0)
        {
            buf[s-1]=0;
            sendMsg(msgid,SERVER_TYPE,buf);
            printf("send done,wait recv...\n");
        }
    }
    destroyMsgQueue(msgid);
    return 0;
}

client.c

#include<unistd.h>
#include"common.h"

int main()
{
    int msgid=getMsgQueue();

    char buf[1024];
    while(1)
    {
        buf[0]=0;
        printf("Please Enter#");
        fflush(stdout);
        ssize_t s=read(0,buf,sizeof(buf));
        if(s>0)
        {
            buf[s-1]=0;
            sendMsg(msgid,CLIENT_TYPE,buf);
            printf("send some.wait recv...\n");

        }
        recvMsg(msgid,SERVER_TYPE,buf);
        printf("server# %s\n",buf);
    }
    return 0;

}

result


3.共享内存通信

server创建共享内存,得到shmid标识符,并将共享内存通过shmat()函数挂接到server进程虚拟地址空间的共享内存区域。

server来打印共享内存中的内容。

client获取共享内存,得到shmid标识符,并将共享内存通过shmat()函数挂接到client进程虚拟地址空间的共享内存区域。

client向共享内存中写入数据。

此时client就和server通过共享内存进行通信了。

注意:
1.shmat后两个参数表示 在挂接过程中 共享内存区开始映射的起始地址的偏移量。
2.在程序退出之前,要调用 shmdt ()来取消进程虚拟地址空间月共享内存的映射关系。

comm.h

#pragma once

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>

#define PATHNAME "."
#define PROJ_ID 0x6666

int createShm(int size);

int destroyShm(int shmid);

int getShm(int size);

comm.c

#include"comm.h"

static int commShm(int size,int flags)
{
    key_t _key=ftok(PATHNAME, PROJ_ID);
    if(_key<0)
    {
        perror("shmget");
        return -1;
    }

    int shmid=0;

    if((shmid=shmget(_key,size,flags))<0)
    {
        perror("shmget");
        return -2;
    }

    return shmid;
}

int destroyShm(int shmid)
{
    if(shmctl(shmid,IPC_RMID,NULL)<0)
    {
        perror("shmctl");
        return -1;
    }
    return 0;

}

int createShm(int size)
{
    return commShm(size,IPC_CREAT|IPC_EXCL|0666);

}

int getShm(int size)
{
    return commShm(size,IPC_CREAT);
}

server.c

include<unistd.h>
#include"comm.h"

int main()
{
    int shmid=createShm(4096);
    char *addr=(char*)shmat(shmid,NULL,0);
    sleep(2);
    int i=0;
    while(i<26)
    {
        printf("client# %s \n",addr);
        sleep(1);
        i++;
    }
    shmdt(addr);
    sleep(2);
    destroyShm(shmid);
    return 0;
}

client.c

#include<unistd.h>
#include"comm.h"

int main()
{
    int shmid=getShm(4096);
    sleep(1);
    char *addr=shmat(shmid,NULL,0);
    sleep(2);
    int i=0;
    while(i<26)
    {
        addr[i]='A'+i;
        i++;
        addr[i]=0;
        sleep(1);

    }
    shmdt(addr);
    sleep(2);
    return 0;

}

与消息队列创建的原理相同 , server需要首先运行,创建共享内存。

result:


4.信号量的使用 与 静态库和动态库的打包。


1. 关于参数 nums 在不同函数中的意义不同:
   1.在 semget()的中的第二个参数nums表示 在这个即将创建的信号量集合中 要创建 几个信号量
  (一个信号量集合中可以创建多个信号量)------flag 为 ( IPC_CREAT | IPC_EXCL | 0666)
   2.如果semget()的 flag 为 (IPC_CREAT ),表示要获得该信号量集合的id。
    那么第二个参数  nums  必须要 小于等于 key所对应的信号量集合的nums,否则出错返回负数值。
  3.在 semctl()的第二个参数中的nums 表示要对信号量集合中第几个信号量进行操作(信号量集中的信号量标号从 0 开始)
     所以 nums 必须 小于 之前semget输入的nums 。
      例如 在semget()中 nums为1,那么在 semctl 中 nums必须为 0。
2. semop()函数中 后两个参数的使用说明:
   第二个参数 sembuf * buf 表示结构体数组的指针
   第三个参数表示结构体数组的大小因为可能对多个信号量进行操作。结构体数组中的每一个元素,表示对一个信号量进行操作。

即 :第二个参数传递数组的指针 ,第三个参数传递数组的大小。

comm.h

#pragma once

#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

#define PATHAME "."
#define PROJ_ID 0x6666

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    struct seminfo *__buf;
};

int createSemSet(int nums);

int initSem(int semid,int nums, int initVal);

int getSemSet(int nums);

int P(int semid, int Who);

int V(int semid,int Who);

int destroySemSet(int semid);

comm.c

#include"comm.h"

static int commSemSet(int nums,int flags)
{
    key_t _key=ftok(PATHAME,PROJ_ID);
  if(_key<0)
  {
      perror("ftok");
      return -1;
  }
  int semid=semget(_key,nums,flags);
     if(semid<0)
     {
         perror("semget");
         return -2;
     }
     return semid;

}

int createSemSet(int nums)
{
    return commSemSet(nums,IPC_CREAT|IPC_EXCL|0666); 
}

int getSemSet(int nums)
{
    return commSemSet(nums,IPC_CREAT);
}

int initSem(int semid ,int nums,int initVal)
{

     union semun _un;
     _un.val=initVal;

    if(semctl(semid, nums, SETVAL, _un)<0)
    {
        perror("semctl");
        return -1;
    }

    return 0;
}
static int commPV(int semid, int who ,int op)
{
    struct sembuf _sf;
    _sf.sem_num=who;
    _sf.sem_op=op;
    _sf.sem_flg=0;

    if(semop(semid, &_sf, 1)< 0)
    {
        perror("semop");
        return -1;
    }
    return 0;

}

int P(int semid, int Who)
{
    return commPV(semid,Who,-1);
}

int V(int semid,int who)
{
    return commPV(semid,who,1);

}

int destroySemSet(int semid)
{
    if(semctl(semid,0, IPC_RMID)<0)
    {
        perror("semctl");
        return -1;
    }
   return 0;
}




test_sem.c

#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdio.h>
#include"comm.h"

int main()
{
    int semid=createSemSet(1);
    initSem(semid,0,1);
    pid_t id=fork();
    if(id==0)
    {
        int _semid=getSemSet(0);
         while(1)
         {
             P(_semid,0);
             printf("A");
             fflush(stdout);
             usleep(123456);
             printf("A");
             fflush(stdout);
             usleep(321456);
             V(_semid,0);
         }
    }
    else
    {
        while(1)
        {
            P(semid,0);
            printf("B");
            fflush(stdout);
            usleep(223456);
            printf("B");
            fflush(stdout);
            usleep(121456);
            V(semid,0);
        }
        wait(NULL);
    }
    destroySemSet(semid);
     return 0;
}

打包静态库:

1. 执行命令 gcc  -c  comm.c  生成 comm.o
2. 执行命令 ar cr libmypv.a comm.o 生成静态库
3.执行命令 gcc -o test1 test_sem.c -L. -lmypv  (之后可以删除libmypv.a)
4.运行 test1

打包动态库:

1.删除之前的comm.o 。
2.执行命令 gcc -fPIC -c comm.c 生成与地址无关的 comm.o文件。
3.执行命令  gcc -shared  -o libmypv.so comm.o 生成动态库文件。
4.执行命令  mv libmypv.so /usr/lib64/  将动态库文件移入 /usr/lib64 系统索引库中。
5.执行命令 gcc -o test2 test_sem.c -L. -lmypv
6.运行test2 

result:



5.关于系统中共享 资源的查看与删除

 1.可以通过指令  ipcs  列出系统中所有 共享资源 的相关信息。
 2.可以通过指令  ipcrm  -q / -m / -s  + 资源id   分别删除对  消息队列、共享内存、信号量 共享资源进行删除。


6.对生产者与消费者模型与同步互斥的理解


  生产者:写入信息到共享的资源中。

    在写入操作之前,生产者会检查 :

   1 .共享资源是否被写满 ?如果共享资源已经被写满,生产者就阻塞的等待,直到有消费者读取了其中的资源后,再进行写入。

   2. 是否有消费者对该共享资源进行读取? 如果有就阻塞的等待直到到消费者读取完毕。如果没有就直接写入。

  

  消费者:读取信息从共享的资源中。

    再读取操作之前,消费者会检查:

   1.共享资源是否为空? 如果共享资源为空,消费者就阻塞的等待,直到有生产者对共享资源写入后,再进行读取。

   2.是否有生成者对其写入? 如果有,就阻塞的等待直到生成者写入完毕,再进行读取。

  这样的机制使得生产者和消费者在对共享资源进行操作的时由于互斥的访问机制,使得他们对内容获取具有了同步性。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值