进程间通信(二)

目录

  • 命名管道
  • system v共享内存
  • 消息队列

命名管道

  • 1 .命名管道是具有标识符的管道,内存当中开辟的内存是由标识的,不同的 进程可以通过名字访问命名管道
  • 2.创建命名管道
    • 使用命令进行创建
      mkfifo [命名管道的文件名称] 文件类型是p,p代表是管道文件
    • 使用函数创建命名管道
      mkfifi(char* pathname,mode_t mode)
      mode:指定创建出来的命名管道的读写权限
  • 3.用户可以通过操作命名管道文件来对内核当中的命名管道的内存区域进行读写操作
  • 4.特性:
    • 具有标识符,可以满足不同进程之间的进程间通信
    • 生命周期是跟随进程的
    • 命名管道的其他特性和匿名管道相同

system v共享内存

创建共享内存&通信流程

  • 1.先在物理内存当中开辟一段空间

  • 2.各个进程通过页表结构将物理内存映射到自己的虚拟地址空间当中的共享区

  • 3.各个进程之间的通信是通过修改自己的虚拟地址空间当中的共享区的地址来完成的

  • 4.特性:

    • 不同的进程对共享内存区域进行读的时候并不会抹除物理内存当中的值
    • 共享内存是最快的进程间通信方式
    • 写入数据是按照覆盖的方式进行的

创建共享内存&使用共享内存的接口

  • 1.创建共享内存

    • shmget(key_t key,size_t size,int shmflag)
      key:共享内存的标识符
      size:共享内存的大小
      shmfalg:
      • IPC_CREAT:如果想获取的共享内存不存在,则创建共享内存,如果存在,则返回共享内存的操作句柄
      • IPC_EXCL|IPC_CREAT:如果想获取的共享内存存在,则报错
        按位或上权限,可以 使用8进制的数字来进行参数的按位或
    • 返回值:成功返回共享内存的操作句柄
  • 2.将进程附加到共享内存上

    • void shmat(int shmid,const voidshmaddr,int shmflag)
      shmid:共享内存的操作句柄
      shmaddr:程序员去指定映射到共享区的哪一个地址,程序员一般都不会去选择,设置为NULL,由操作系统来指定将内存映射到哪一个地址上
      shmflag:
      0:可读可写
      IPC_RDONLY:只读
      返回值:返回映射到共享区的哪一个地址上,程序员就可以操作这个地址来对物理内存区域进行读写操作
  • 3.从共享内存当中分离进程
    int shmdt(const void *shmaddr)
    shmaddr:共享区当中映射的物理内存的首地址,shmat的返回值

  • 4.共享内存的销毁

    • int shmctl(int shmid,int cmd,struct shmid_ds*buf)
      shmid:共享内存的操作句柄
      cmd:
      • 1.销毁:IPC_RMID:删除共享内存,标记共享内存为删除状态
      • 2 获取共享内存信息 :IPC_STAT:获取共享内存状态,需要搭配struct shmid_ds
    • buf:作为出参,用来获取共享内存状态信息,传入一个struct shmid_ds结构体对象的地址
  • 5.共享内存的生命周期
    共享内存的生命周期是跟随操作系统内核的

ipcs可以查看共享内存
ipcs -m
ipcrm -m [shmid]:删除共享内存

如果删除了一个有进程附加的共享内存,操作系统的做法是:

  • 1.先标记当前的共享内存状态是destory状态,并且将key设置为0x00000000,表示当前的共享内存不能再被其他进程所附加,同时释放该共享内存。
  • 2.带来的风险:导致附加到该共享内存的进程有奔溃的风险,一般静止删除有进程附加的共享内存。
  • 3.当进程退出的时候,操作系统就会将描述共享内存的结构体也释放掉

消息队列

  • 队列的特性:
    先进先出,底层的实现是链表,在内核当中创建
    在队列当中每一个元素都有自己的类型,类型之间有优先级的概念
    每一个节点最大的发送字节数量:8k cat /proc/sys/kernel/msgmax
    队列当中所有消息的长度之和:16384 cat/proc/sys/kernel/msmnb
    系统当中最大的队列数:2379 cat/proc/sys/kernel/msmni
  • 接口:
    • int msgget(key_t key,int msgflg)
      key:消息队列的标识符
      magflg: IPC_CRETA IPC_CRATE|IPC_EXCL 按位或上权限
    • int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)
      msqid:消息队列的操作句柄
      magp:发送的数据
      msgsz:数据的长度
      magflg:
      0:当队列满了则阻塞
      IPC_NOWAIT:如果说队列满了,则当前发送的操作不会进行阻塞,函数返回
    • ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
      msgtyp:
      0:表示什么类型都可以接受
      msgtyp>0:则返回队列当中消息类型为msgtyp的第一个消息
      msgtyp<0:则返回队列当中消息类型小于等于msgtye绝对值的消息
      如果这样的消息比较多,则返回类型最小的那个消息
    • int msgctl(int msqid, int cmd, struct msqid_ds *buf);
      cmd:
      IPC_STAT
      IPC_SET
      IPC_RMID
  • 特性
    • :消息队列的生命周期跟随内核
    • 消息队列可以进行双工通信
    • 克服了管道的无格式的字节流的缺点

进程间通信的代码:
writeshm.c

#include 
#include 
#include 
#define key_t 0x99999999

int main()
{
    int shmid=shmget(key_t,1024,IPC_CREAT|0664);
    if(shmid<0)
    {
        perror("shmget");
        return 0;
    }
    void *lp=shmat(shmid,NULL,0);
    if(!lp)
    {
        perror("shmat");
        return 0;
    }
int i=0;
while(1)
{
    sprintf((char*)lp,"%s-%d","lhy",i);
    i++;
}
    //shmdt(lp);
    //shmctl()
    return 0;
}

readshm.c

#include 
#include 
#include 

#define key_t 0x99999999
int main()
{
    int shmid=shmget(key_t,1024,IPC_CREAT|0664);
    if(shmid<0)
    {
        perror("shmget");
        return 0;
    }
    void *lp=shmat(shmid,NULL,0);
    if(!lp)
    {
        perror("shmat");
        return 0;
    }
    //  while(1)
    {
        printf("%s\n",(char*)lp);
    }
   // shmdt(lp);
    struct shmid_ds buf;
    shmctl(shmid,IPC_STAT,&buf);
    printf("shmid_size%ld\n",buf.shm_segsz);
  shmdt(lp);
  shmctl(shmid,IPC_RMID,NULL);
  return 0;
}

你可能感兴趣的:(Linux)