消息队列,共享内存,信号灯

IPC对象:
内存文件 

命令

1.ipcs 
  查看所有ipc对象的信息

2.ipcrm 删除IPC对象
  ipcrm -Q/M/S  Key
  ipcrm -q/m/s  消息队列ID/共享内存ID/信号灯ID

 消息队列

    步骤

    1.创建IPC对象名称
    2.创建消息队列
    3.发送消息
    4.接收消息
    5.消息队列销毁

 函数接口

(1)ftok

 ftok 
    key_t ftok(const char *pathname, int proj_id);
    功能:
        创建一个IPC对象名称
    参数:
        pathname:文件的路径
        proj_id:项目ID(8bits)
    返回值:
        成功返回IPC对象名称
        失败返回-1

 (2)msgget

 msgget 
    int msgget(key_t key, int msgflg);
    功能:
        创建一个消息队列 
    参数:
        key:IPC对象名称 
        msgflg:消息队列属性
            IPC_CREAT:创建一个消息队列
            IPC_EXCL: 如果消息队列存在就报错
    返回值:
        成功返回消息队列ID
        失败返回 -1 

(3) msgsnd 

 msgsnd 
      int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
      功能:
        向消息队列中发送消息
      参数:
        msqid:消息队列的ID号
        msgp:发送消息的内容
        msgsz:发送消息的大小
        msgflg:消息属性 默认为0 

0:当消息队列满时,msgsnd将会阻塞,直到消息能写进消息队列

IPC_NOWAIT:当消息队列已满的时候,msgsnd函数不等待立即返回

IPC_NOERROR:若发送的消息大于size字节,则把该消息截断,截断部分将被丢弃,且不通知发送进程。

      返回值:
        成功返回0
        失败返回-1 

发送消息结构体需自己定义

 struct msgbuf {
            long mtype;       /* message type, must be > 0 */
            char mtext[1];    /* message data */
        };
 

 (4)msgrcv 

msgrcv 
      ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
      功能:
        从消息队列中接收消息
      参数:
        msqid:消息队列的ID号
        msgp:存放消息的空间首地址
        msgsz:最多接收消息的大小
        msgtyp:接收消息的类型 
        msgflg:消息属性 默认为0
      返回值:
        成功返回接收到数据的字节数 
        失败返回-1 

 (5)msgctl 

msgctl 
      int msgctl(int msqid, int cmd, struct msqid_ds *buf);
      功能:
        向消息队列发送命令
      参数:
        msqid:消息队列的ID号
        cmd:命令 

cmd取值含义
IPC_STAT获取消息队列的状态
IPC_SET设置消息队列的属性
IPC_RMID从系统中移除消息队列

返回值:
        成功返回0
        失败返回-1
      

#include "../head.h"
typedef struct msgget
{
    long mtype;
    char mtext[250];
}message_t;
int main()
{
    key_t key;
    int msgid=0;
    message_t sendmsg;
    message_t recvmsg;
    ssize_t nsize;
    int ret=0;
    key=ftok("/",'a');//创建key值
    if(key==-1)
    {
        return -1;
    }
    printf("key=%#x\n",key);
    msgid=msgget(key,IPC_CREAT|0664);//创建消息队列
    if(msgid==-1)
    {
        return -1;
    }
    printf("msgid=%d\n",msgid);

    sendmsg.mtype=100;
    strcpy(sendmsg.mtext,"how are you");
    ret=msgsnd(msgid,&sendmsg,sizeof(sendmsg)-sizeof(long),0);//发送消息,类型100
    if(ret==-1)
    {
        return -1;
    }
    printf("发送成功\n");

    sendmsg.mtype=200;
    strcpy(sendmsg.mtext,"hello world");
    ret=msgsnd(msgid,&sendmsg,sizeof(sendmsg)-sizeof(long),0);//发送消息,类型200
    if(ret==-1)
    {
        return -1;
    }
    printf("发送成功\n");
    
    nsize=msgrcv(msgid,&recvmsg,sizeof(recvmsg.mtext),200,0);
    if(nsize==-1)
    {
        return -1;
    }
    printf("接收到的字节数:%ld,内容为:%s\n",nsize,recvmsg.mtext);
    msgctl(msgid,IPC_RMID,NULL);

    //查看消息队列命令 ipcs -q
    //编译一次发送一次,编译第二次在原来的基础上继续增加
    //删除消息队列命令  ipcrm -q <msgid>
    return 0;
}

练习:
1.编写两个进程,实现利用消息队列的通信
    send.c  创建消息队列 -> 发送消息
    recv.c  创建消息队列 -> 接收消息 -> 销毁消息队列

recv.c

#include "../head.h"

typedef struct msg
{

    long mtype;
    char mtext[200];
}message_t;
int main()
{
    key_t key;
    message_t recv;
    int msgid=0;
    ssize_t nsize;
//创建key值
    key=ftok("/",'b');
    if(key==-1)
    {
        return -1;
    }
 //创建消息队列
    msgid=msgget(key,IPC_CREAT|0664);
    if(msgid==-1)
    {
        return -1;
    }
//接收消息
    nsize=msgrcv(msgid,&recv,sizeof(recv.mtext),200,0);
    if(nsize==-1)
    {
        return -1;
    }
     printf("接收到的字节数:%ld,内容为:%s\n",nsize,recv.mtext);

     msgctl(msgid,IPC_RMID,NULL);
    return 0;
}

send.c

#include "../head.h"

typedef struct msg
{

    long mtype;
    char mtext[200];
}message_t;
int main()
{
    key_t key;
    message_t send;
    int msgid=0;
    int ret=0;
//创建key值
    key=ftok("/",'b');
    if(key==-1)
    {
        return -1;
    }
 //创建消息队列
    msgid=msgget(key,IPC_CREAT|0664);
    if(msgid==-1)
    {
        return -1;
    }
//发送消息
    send.mtype=200;
    strcpy(send.mtext,"hello world");

    ret=msgsnd(msgid,&send,sizeof(send.mtext),0);
    if(ret==-1)
    {
        return -1;
    }
   
    return 0;
}

运行结果 

 

 共享内存

 进程间通信最高效的方法
    操作步骤:
       1.创建 key 值
       2.创建共享内存 
       3.映射共享内存
       4.解除映射
       5.销毁共享内存 

在Linux中,每个进程都有属于自己的进程控制块(PCB)和虚拟地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。

共享内存的通信原理示意图:

 函数接口

(1)ftok 同上

(2)shmget 

shmget 
          int shmget(key_t key, size_t size, int shmflg);
          功能:
            创建一个共享内存
          参数:
            key:IPC对象名称
            size:共享内存的大小
            shmflg:
                IPC_CREAT  创建 
                IPC_EXCL   如果存在就报错 
          返回值:
            成功返回共享内存ID号
            失败返回-1 

 (3)shmat

shmat
          void *shmat(int shmid, const void *shmaddr, int shmflg);
          功能:
            将地址映射到共享内存中
          参数:
            shmid:共享内存ID号
            shmaddr:
                NULL: 让系统选择一个合适的地址映射到共享内存中
            shmflg:
                属性,默认为0 
          返回值:
            成功返回映射到共享空间的地址
            失败返回NULL

 (4)shmdt

shmdt
          int shmdt(const void *shmaddr);
          功能:
            解除映射空间
          参数:
            shmaddr:映射到共享内存中的地址
          返回值:
            成功返回0 
            失败返回-1 

 (5)shmctl

 shmctl 
          int shmctl(int shmid, int cmd, struct shmid_ds *buf);
          功能:
            向共享内存发送命令
          参数:
            shmid:共享内存ID号
             cmd:命令
                IPC_RMID 删除
          返回值:
            成功返回0 
            失败返回-1 

#include "../head.h"
int main()
{
    int shmid;
    char *pshmadder=NULL;
    int ret=0;
    //创建key值
    key_t key;
    key=ftok("/",'c');
    if(key==-1)
    {
        return -1;
    }
    //创建共享内存

    shmid=shmget(key,4096,IPC_CREAT|0664);
    if(shmid==-1)
    {
        return -1;
    }
    //映射共享内存
    pshmadder=shmat(shmid,NULL,0);
    if(pshmadder==NULL)
    {
        return -1;
    }
    fgets(pshmadder,4096,stdin);
    pshmadder[strlen(pshmadder)-1]='\0';
    printf("共享空间:%s\n",pshmadder);
    //解除映射
    ret=shmdt(pshmadder);
    if(ret==-1)
    {
        return -1;
    }
    //销毁共享空间
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
    
}

 练习:
1.编写两个进程,实现利用共享内存的通信
    write.c  创建共享内存 -> 映射 -> 从终端接收数据写入共享内存中
    read.c  创建共享内存 -> 映射 -> 从共享内存中读取数据 -> 显示到终端

read.c

#include "../head.h"
int main()
{
    int shmid;
    key_t key;
    int ret=0;
    char *pshread=NULL;
    //创建key值
    key=ftok("/",'c');
    if(key==-1)
    {
        return -1;
    }
    //创建共享空间
    shmid=shmget(key,4096,IPC_CREAT|0664);
    if(shmid==-1)
    {
        return -1;
    }
    //映射到共享空间中
    pshread=shmat(shmid,NULL,0);
    if(pshread==NULL)
    {
        return -1;
    }
    while(1)
    {

        printf("共享空间:%s\n",pshread);
        if(strcmp(pshread,"quit")==0)
        {
            break;
        }
    }

//解除映射
    ret=shmdt(pshread);
    if(-1==ret)
    {
        perror("failed  to shmdt");
        return -1;
    }
    //销毁共享空间
    shmctl(shmid,IPC_RMID,NULL);
    return 0;
}

 write.c

#include "../head.h"
int main()
{
    int shmid;
    key_t key;
    char *pshwrite=NULL;
    int ret=0;
    //创建key值
    key=ftok("/",'c');
    if(key==-1)
    {
        return -1;
    }
    //创建共享空间
    shmid=shmget(key,4096,IPC_CREAT|0664);
    if(shmid==-1)
    {
        return -1;
    }
    //映射到共享空间中
    pshwrite=shmat(shmid,NULL,0);
    if(pshwrite==NULL)
    {
        return -1;
    }
    while(1)
    {
        fgets(pshwrite,4096,stdin);
        pshwrite[strlen(pshwrite)-1]='\0';
        if(strcmp(pshwrite,"quit")==0)
        {
            break;
        }
    }

//解除映射
     ret=shmdt(pshwrite);
    if(-1==ret)
    {
        perror("failed  to shmdt");
        return -1;
    }
    return 0;
}

 运行结果

read循环打印,当write写入内容,read循环打印新的内容

 

 

 信号灯

  有名信号量数组

函数接口

 (1)semget

semget

   int semget(key_t key, int nsems, int semflg);   
        功能:
            创建信号量数组 
        参数: 
            key:IPC对象名称
            nsems:信号量个数
            semflg:信号量属性
                IPC_CREAT:创建一个信号量数组
        返回值:    
            成功返回所创建的信号灯id
            失败返回-1 

 (2)semctl

semctl

 int semctl(int semid, int semnum, int cmd, ...);
        功能:
            向信号灯发送命令
        参数:
            IPC_RMID    删除信号灯
            SETVAL      设置第semnum-th信号量的值为arg.val 
        返回值:
            成功返回0 
            失败返回-1 

 (3)semop

semop

   int semop(int semid, struct sembuf *sops, size_t nsops);
        功能:
            对信号量完成申请和释放操作
        参数:
            semid:信号灯ID号
            sops:信号灯操作数组
                unsigned short sem_num;  //操作信号量的下标
                short          sem_op;   //对信号量的操作 +1(释放信号量) -1(申请信号量)
                short          sem_flg;  //SEM_UNDO 操作结束后,信号量的值会恢复到原来的值
            nsops:数组元素个数  
        返回值:
            成功返回0 
            失败返回-1

#include "../head.h"

union semun {
    int              val;    /* Value for SETVAL */
    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) */
};
int main()
{
    int semid=0;
    key_t key;
    int ret=0;
    union semun myun;
    struct sembuf mybuf;
   
    

    //创建IPC对象名称
    key=ftok("/",'a');
    if(key==-1)
    {
        perror("failed to ftok");
        return -1;
    }
    //创建信号量数组
    semid=semget(key,2,IPC_CREAT|0664);
    if(semid==-1)
    {
        perror("failed to semget");
        return -1;
    }

    //将下标为0的读信号量设置为0
    myun.val=0;
    ret=semctl(semid,0,SETVAL,myun);
    if(ret==-1)
    {
        perror("failed to semctl");
        return -1;
    }

    //将下标为1的写信号设置为1
     myun.val=1;
    ret=semctl(semid,1,SETVAL,myun);
    if(ret==-1)
    {
        perror("failed to semctl");
        return -1;
    }

    //申请写信号量
    mybuf.sem_num=1;
    mybuf.sem_op=-1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        perror("failed to semop");
        return -1;
    }
    printf("拿到写信号量了\n");

    //释放读信号量
     mybuf.sem_num=0;
    mybuf.sem_op=+1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        perror("failed to semop");
        return -1;
    }
    printf("释放读信号量了\n");

    //申请读信号量
     mybuf.sem_num=0;
    mybuf.sem_op=-1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        perror("failed to semop");
        return -1;
    }
    printf("释放读信号量了\n");

 #if 0  
  //再次申请读信号量阻塞
     mybuf.sem_num=0;
    mybuf.sem_op=-1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        perror("failed to semop");
        return -1;
    }
    printf("释放读信号量了\n");
#endif
    return 0;
}

练习:

利用信号灯,共享内存实现进程间阻塞通信

sem.c

#include "../head.h"
/*对信号量初始化*/
int init_sem(int semid,int *parray,int len)
{
    int i=0;
    union semun myun;
    int ret=0;
    for(i=0;i<len;i++)
    {
        myun.val=parray[i];
        ret=semctl(semid,i,SETVAL,myun);
        if(-1==ret)
        {
            return -1;
        }
    }
}
/*信号量申请操作*/
int sem_p(int semid,int semun)
{
    int ret=0;
    struct sembuf mybuf;
    mybuf.sem_num=semun;
    mybuf.sem_op=-1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        return -1;
    }
    return 0;
}

/*信号量释放操作*/
int sem_v(int semid,int semun)
{
    int ret=0;
    struct sembuf mybuf;
    mybuf.sem_num=semun;
    mybuf.sem_op=+1;
    mybuf.sem_flg=SEM_UNDO;
    ret=semop(semid,&mybuf,1);
    if(-1==ret)
    {
        return -1;
    }
    return 0;
}

write.c写进程

#include "../head.h"


//编译gcc write.c sem.c -o write
int main()
{
    key_t key;
    int semid=0;
    int shmid=0;
    int ret=0;
    int val[2]={0,1};
    char *pshmaddr=NULL;

    //创建key值
    key=ftok("/",'a');
    if(key==-1)
    {
        return -1;
    }

    //创建共享内存
    shmid=shmget(key,4096,IPC_CREAT|0664);
    if(shmid==-1)
    {
        perror("failed to shmget");
        return -1;
    }
    //创建信号量数组
    semid=semget(key,2,IPC_CREAT|0664);
    if(shmid==-1)
    {
        perror("failed to semget");
        return -1;
    }

    //对信号量数组初始化
    init_sem(semid,val,2);

    //映射共享内存
    pshmaddr=shmat(shmid,NULL,0);
    if(NULL==pshmaddr)
    {
        perror("failed to shmat");
        return -1;
    }
    while(1)
    {
        //申请写资源
        sem_p(semid,1);
        gets(pshmaddr);
        
        //释放读资源
        sem_v(semid,0);
        if(!strcmp(pshmaddr,"quit"))
        {
            break;
        }
    }

     shmdt(pshmaddr);
    
    shmctl(shmid, IPC_RMID, NULL);

    semctl(semid, 0, IPC_RMID);
    return 0;
}

read.c读进程

#include "../head.h"

//编译gcc read.c sem.c -o read
int main()
{
    key_t key;
    int semid=0;
    int shmid=0;
    int ret=0;
    int val[2]={0,1};
    char *pshmaddr=NULL;

    //创建key值
    key=ftok("/",'a');
    if(key==-1)
    {
        return -1;
    }

    //创建共享内存
    shmid=shmget(key,4096,IPC_CREAT|0664);
    if(shmid==-1)
    {
        perror("failed to shmget");
        return -1;
    }
    //创建信号量数组
    semid=semget(key,2,IPC_CREAT|0664);
    if(shmid==-1)
    {
        perror("failed to semget");
        return -1;
    }

    //对信号量数组初始化
    init_sem(semid,val,2);

    //映射共享内存
    pshmaddr=shmat(shmid,NULL,0);
    if(NULL==pshmaddr)
    {
        perror("failed to shmat");
        return -1;
    }
    while(1)
    {
        //申请读资源
        sem_p(semid,0);
        printf("内容:%s\n",pshmaddr);
        if(!strcmp(pshmaddr,"quit"))
        {
            break;
        }
        //释放写资源
        sem_v(semid,1);
    }

     shmdt(pshmaddr);
    
    shmctl(shmid, IPC_RMID, NULL);

    semctl(semid, 0, IPC_RMID);
}

运行结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值