IO进程——共享内存、信号灯集、消息队列

一、共享内存

1、概念

        共享内存是指操作系统在物理内存中申请一块空间,应用程序可以映射到这块空间,从而直接进行读写操作。

2、特点

        1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

        2)为了能够在多个进程之间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将其映射到自己的私有地址空间

        3)进程可以直接读写这个内存区而不需要进行数据拷贝,从而大大提升效率

        4)由于多个进程共享一段内存,因此也需要依靠某种同步机制,例如:互斥锁和信号量

3、步骤

        1)创建一个唯一key值

        2)创建或打开共享内存 //ftok

        3)映射共享内存

        4)取消映射

        5)删除共享内存

4、函数接口

1)创建key值

#include<sys/types.h>
#inclued<sys/pic.h>
key_t ftok(const char *pathname, int proj_id);
功能:创建key值
参数:pathname:文件名
       proj_id:取整型数的低8位数值  //一般为'a'
返回值:成功:key值
       失败:-1

示例:

#include<sys/pci.h>
#include<sys/types.h>
#include<stdio.h>
int main()
{
    key_t key;  //定义变量
    key=ftok("./aa",'a');//创建key值
    if(key<0)//若打开失败
    {
        perror("ftok err\n");
        return-1;
    }
    printf("%#x\n",key);//输出key值
    return 0;
}

2)创建共享内存

int shmget(key_t key, size_t size, int shmflg);
功能:创建或打开共享内存
参数:
    key  键值
    size   共享内存的大小
                创建       检测错误
    shmflg   IPC_CREAT|IPC_EXCL|0777  创建共享内存的时候的权限
返回值:成功   shmid  共享内存的id
      出错    -1

查看创建的共享内存命令:ipcs -m

示例:

#include <sys/ipc.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/shm.h>
#include <errno.h>
int main()
{
    key_t key;
    key = ftok("./aa", 'a');
    if (key < 0)
    {
        perror("ftok err\n");
        return -1;
    }
    printf("%#x\n", key);
    int shmid;
    shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 0666); // 创建或打开共享内存
    if (errno == EEXIST)                                  // 文件已存在
    {
        printf("file exists\n");
        shmid = shmget(key, 64, 0666); // 重新打开共享内存
    }
    else
    {
        perror("shmget err");
        return -1;
    }
    printf("%d\n",shmid);
    return 0;
}

4)映射共享内存

void  *shmat(int  shmid,const  void  *shmaddr,int  shmflg);
功能:映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
参数:
    shmid   共享内存的id号
    shmaddr   一般为NULL,表示由系统自动完成映射
              如果不为NULL,那么有用户指定
    shmflg:SHM_RDONLY就是对该共享内存只进行读操作
                0     可读可写
返回值:成功:完成映射后的地址,
      出错:(void *)-1的地址

示例:

#include <sys/ipc.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/shm.h>
#include <errno.h>
#include<string.h>
int main()
{
    key_t key;
    key = ftok("./aa", 'a');
    if (key < 0)
    {
        perror("ftok err\n");
        return -1;
    }
    printf("%#x\n", key);
    int shmid;
    shmid = shmget(key, 64, IPC_CREAT | IPC_EXCL | 0666); // 创建或打开共享内存
    if (errno == EEXIST)                                  // 文件已存在
    {
        printf("file exists\n");
        shmid = shmget(key, 64, 0666); // 重新打开共享内存
    }
    else
    {
        perror("shmget err");
        return -1;
    }
    printf("%d\n",shmid);//打印共享内存id号
    char *p=shmat(shmid,NULL,0);//映射共享内存
    if(p==(void*)-1)
    {
        perror("shmat err");
        return-1;
    }
    strcpy(p,"jijijiji");//向内存中存放数据
    printf("%s\n",p);
    return 0;
}

4)取消映射

int shmdt(const void *shmaddr);
功能:取消映射
参数:要取消的地址
返回值:成功0  
      失败的-1

示例:

  shmdt(p);//取消映射
    getchar();//阻塞
    return 0;

5)删除共享内存

int  shmctl(int  shmid,int  cmd,struct  shmid_ds   *buf);
功能:(删除共享内存),对共享内存进行各种操作
参数:
    shmid   共享内存的id号
    cmd     IPC_STAT 获得shmid属性信息,存放在第三参数
            IPC_SET 设置shmid属性信息,要设置的属性放在第三参数
            IPC_RMID:删除共享内存,此时第三个参数为NULL即可
 struct  shmid_ds   *buf:是一个结构体指针,但我们是删除共享内存,所以并没有意义,我们直接设置为NULL就可以了
返回:成功0 
     失败-1

5、操作命令

ipcs -m:查看系统中国创建的共享内存

ipcrm -m [shmid]:删除创建的共享内存

二、信号灯集

1、概念

        信号灯也被称为信号量。它是不同进程之间或者给定进程内部不同的线程间同步的机制。

        System V 信号灯集是一个或者多个信号灯的集合。其中每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。

        通过信号灯可以实现共享内存的同步操作。

2、操作步骤

1)创建key值  //同上

2)创建或打开信号灯集

3)初始化信号灯集

4)pv操作

5)删除信号灯集

3、函数接口

1)创建或打开信号灯集

#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:创建/打开信号灯
参数:key:ftok产生的key值
    nsems:信号灯集中包含的信号灯数目
    semflg:信号灯集的访问权限,通常为IPC_CREAT |0666
返回值:成功:信号灯集ID
       失败:-1

示例:

#include<stdio.h>
#include<sys/types.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<errno.h>
int main()
{
    key_t key;
    int semid;
    key=ftok("./aa",'a');//创建key值
    if(key<0)
    {
        perror("ftok err\n");
        return -1;
    }
    semid=semget(key,2,IPC_CREAT|0666);//创建信号灯集,数目为2
    if(semid<0)
    {
        perror("semget err \n");
        return -1;
    }
    printf("%d\n",semid);//返回信号灯集ID
}

2)初始化或者删除信号灯集

int semctl ( int semid, int semnum,int cmd,…/*union semun arg*/);
功能:信号灯集合的控制(初始化/删除)
参数:semid:信号灯集ID
    semnum: 要操作的集合中的信号灯编号
     cmd:
        GETVAL:获取信号灯的值,返回值是获得值
        SETVAL:设置信号灯的值,
        IPC_RMID:从系统中删除信号灯集合
    ...:当cmd为SETVAL时,需要传递共用体
返回值:成功 0
      失败 -1
共用体格式:
    union semun {
       int              val;    /* 信号量的初值 */
       struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
       unsigned short  *array;  /* Array for GETALL, SETALL */
       struct seminfo  *__buf;  /* Buffer for IPC_INFO
                                           (Linux-specific) */
    };

示例:

ys/sem.h>
#include <sys/ipc.h>
#include <errno.h>
union semun
{
    int val;
};
int main()
{
    key_t key;
    int semid;
    key = ftok("./aa", 'a');
    if (key < 0)
    {
        perror("ftok err\n");
        return -1;
    }
    semid = semget(key, 2, IPC_CREAT | 0666);
    if (semid < 0)
    {
        perror("semget err \n");
        return -1;
    }
    else
    {//初始化只需要在创建时进行初始化即可,避免重复初始化
        union semun sem;
        sem.val = 10;
        semctl(semid, 0, SETVAL, sem);//初始化第一个信号灯
        sem.val = 0;
        semctl(semid, 1, SETVAL, sem);//第二个
    }
    printf("%d\n", semctl(semid, 0, GETVAL));
    printf("%d\n", semctl(semid, 1, GETVAL));
}

补充:当cmd为SETVAL时,需要传递第四个参数,类型为共用体

        用法:

union semun {
    int val;
};
union semun sem;
sem.val = 10;
semctl(semid, 0, SETVAL, sem); //对编号为0的信号灯设置初值为10

3)pv操作

int semop ( int semid, struct sembuf  *opsptr,  size_t  nops);
功能:对信号灯集合中的信号量进行PV操作
参数:semid:信号灯集ID
     opsptr:操作方式
     nops:  要操作的信号灯的个数 1个
返回值:成功 :0
      失败:-1
struct sembuf {
   short  sem_num; // 要操作的信号灯的编号
   short  sem_op;  //    0 :  等待,直到信号灯的值变成0
                   //   1  :  释放资源,V操作
                   //   -1 :  分配资源,P操作                    
    short  sem_flg; // 0(阻塞),IPC_NOWAIT, SEM_UNDO
};

示例:

使用:

申请资源 P操作:

mysembuf.sem_num = 0;

mysembuf.sem_op = -1;

mysembuf.sem_flg = 0;

semop(semid, &mysembuf, 1);

释放资源 V操作:

mysembuf.sem_num = 0;

mysembuf.sem_op = 1;

mysembuf.sem_flg = 0;

semop(semid, &mysembuf, 1);

封装函数:使代码更加美观

4、操作命令

ipcs -s :查看创建的信号灯集

ipcrm -s [semid]:删除信号灯集

三、消息队列

1、特点

1)消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。

2)消息队列可以按照类型来发送/接收消息

3)在linux下消息队列的大小有限制。

  • 消息队列个数最多为16个;
  • 消息队列总容量最多为16384字节;
  • 每个消息内容最多为8192字节。

2、操作步骤

1)创建key值

2)创建或打开消息队列

3)添加消息

4)读取消息

5)删除消息队列

3、函数接口

1)创建消息队列

#include <sys/msg.h>

int msgget(key_t key, int flag);
功能:创建或打开一个消息队列
参数:  key值
       flag:创建消息队列的权限IPC_CREAT|IPC_EXCL|0666
返回值:成功:msgid
       失败:-1

示例:

2)添加消息

int msgsnd(int msqid, const void *msgp, size_t size, int flag); 
功能:添加消息
参数:msqid:消息队列的ID
      msgp:指向消息的指针。常用消息结构msgbuf如下:
          struct msgbuf{
            long mtype;        //消息类型
            char mtext[N]};   //消息正文
          }
   size:发送的消息正文的字节数
   flag:IPC_NOWAIT消息没有发送完成函数也会立即返回    
         0:直到发送完成函数才返回  (阻塞)
返回值:成功:0
      失败:-1

示例:

3)读取消息

int msgrcv(int msgid,  void* msgp,  size_t  size,  long msgtype,  int  flag);
功能:读取消息
参数:msgid:消息队列的ID
     msgp:存放读取消息的空间
     size:接受的消息正文的字节数
    msgtype:0:接收消息队列中第一个消息。
            大于0:接收消息队列中第一个类型为msgtyp的消息.
            小于0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息。
     flag:0:若无消息函数会一直阻塞
        IPC_NOWAIT:若没有消息,进程会立即返回ENOMSG
返回值:成功:接收到的消息的长度
      失败:-1

示例:

4)删除消息队列

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );
功能:对消息队列的操作,删除消息队列
参数:msqid:消息队列的队列ID
     cmd:
        IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
        IPC_SET:设置消息队列的属性。这个值取自buf参数。
        IPC_RMID:从系统中删除消息队列。
     buf:消息队列缓冲区
返回值:成功:0
      失败:-1

4、操作命令

ipcs -q:查看创建的消息队列

ipcrm -q [msgid]:删除消息队列

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值