目录

一、linuxIPC对象 (Inter Process Communicate)

1.什么是IPC对象

1)想要操作文件(设备文件/dev):

2)想要操作消息队列:

2.查看系统中所有的IPC对象

1)查看IPC对象: ipcs -a

2)删除IPC对象

3.使用IPC对象之前,要申请key值,那么这个key值是怎么来的??? ---》ftok  --> man 3 ftok

二、进程之间通信方式之一----消息队列

1.消息队列是属于IPC对象,所以使用之前一定要先申请key值。

2.管道通信跟消息队列非常相似,它们之间的区别???

1)管道通信:

2)消息队列:

3.消息队列机制:

4.消息队列作用的范围: linux下任意两个进程。

三、使用消息队列来通信的步骤

1.申请消息队列的key值

2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget

3.发送 /写入数据

4.从消息队列中读取数据(接收数据) ---》man 2 msgrcv

5.删除消息队  msgctl=message contol

四、进程之间的通信方式 --- 共享内存。share-memory

1.共享内存也是属于IPC对象,所以使用之前必须申请key值。2.共享内存作用范围以及机制是如何?

1)作用范围:在linux下任意两个进程。2)机制:任意两个进程通过申请key值,ID号,共享内存得到一片内存空间,那么这两个进程就可以将数据写入到共享内存/读取共享内存上的数据进行数据的交换。

3.实现共享内存步骤:

1)先申请key值。

2)根据申请到的key值去申请共享内存的ID号。  -> shmget()  -> man 2 shmget   (share memory get)

#include #include int shmget(key_t key, size_t size, int shmflg); //shm --->share memory参数:    key: key值    size: 共享内存的总字节数,必须是PAGE_SIZE的倍数   #define PAGE_SIZE 1024    shmflg:IPC_CREAT  -> 不存在则创建        IPC_CREAT|0666返回值:    成功:共享内存ID号    失败:-1    3)根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域---映射

4)当不再使用时,解除映射关系

5)当没有进程再需要使用这块共享内存时,删除它-> shmctl()  -> man 2 shmctl

4.例子:通过共享内存实现两个进程的通信

1)写端:

2)读端:

五、处理进程之间通信--共享内存的同步互斥问题  -> 信号量。

1.什么是信号量?

2.信号量的函数接口?--semaphore

1)由于信号量属于IPC对象,所以要申请key值。

2)根据key值申请信号量ID号。 -> semget()  -> man 2 semget

3)控制/设置信号量值参数。  -> semctl()  -> man 2 semctl

4)如何实现信号量的P/V操作?  (P操作: 1->0  V操作: 0->1)

作业


一、linuxIPC对象 (Inter Process Communicate)

1.什么是IPC对象

在linux下,IPC对象指的是 消息队列、共享内存、信号量。如果用户需要使用IPC对象来进行进程之间的通信,首先必须为IPC对象申请对应的资源
比如,如果想要使用消息队列来通信,那么就必须先申请消息队列对应的key值和ID号。

1)想要操作文件(设备文件/dev):

需要获得的资源:
>文件的路径名
>文件的文件描述符

2)想要操作消息队列:

需要获得的资源:
>需要获得key值------文件的路径名
>ID号  -----文件的文件描述符

2.查看系统中所有的IPC对象

1)查看IPC对象: ipcs -a

gec@ubuntu:/mnt/hgfs/gz2166/07-系统编程/03/1-code$ ipcs -a
------ Shared Memory Segments -------- //共享内存
key值      ID号
key        shmid      owner      perms      bytes      nattch     status
------ Semaphore Arrays -------- //信号量
key        semid      owner      perms      nsems
------ Message Queues -------- //消息队列
key        msqid      owner      perms      used-bytes   messages
key值: 类似于 文件的路径名
ID号: 类似于文件描述符

2)删除IPC对象

想删除消息队列: ipcrm  -q   消息队列的key值  /  ipcrm  -q   消息队列的ID值   (ipc remove  -queue)
想删除共享内存: ipcrm  -m   共享内存的key值  /  ipcrm  -m   共享内存的ID值 (ipc remove -memory)
想删除信号量: ipcrm  -s   信号量的key值  /  ipcrm  -s   信号量的ID值  (ipc remove sem)

3.使用IPC对象之前,要申请key值,那么这个key值是怎么来的??? ---》ftok  --> man 3 ftok

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

key_t ftok(const char *pathname, int proj_id);
函数作用: 获得一个key值
参数:
    pathname: 一个合法的路径。  常用 "."
    proj_id: 非0整数。   常用 10
返回值:
    成功 key值
    失败 -1
 The resulting value is the same for all pathnames that  name  the  same file,  when  the  same  value  of  proj_id is used.  
 当文件路径pathname 和 proj_id 是一样的时候,两个ftok函数的返回值---key是一样的。
 
 The value returned should be different when the (simultaneously  existing)  files  or  the project IDs differ.
  只要文件路径pathname 或者 proj_id 有一个不一样,返回的key值 就是不一样的。
key_t key = ftok(".",10);
key_t key = ftok(".",10);
例子: 验证key值。

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>int main()
{key_t key;//获取key值key = ftok(".", 10);printf("key:%x\n",key);//获取key值key = ftok("..", 10);printf("key:%x\n",key);//获取key值key = ftok(".", 20);printf("key:%x\n",key);//获取key值key = ftok(".", 10);printf("key:%x\n",key);return 0;
}

结论:如果想要使用IPC对象实现两个进程之间的通信,那么两个进程的IPC对象的key值必须是一样的。

验证:两个进程之间当ftok的两个参数一样的时候,获取的key值是一样的
进程1: key_t key = ftok(".",10);
进程2: key_t key = ftok(".",10);

二、进程之间通信方式之一----消息队列

1.消息队列是属于IPC对象,所以使用之前一定要先申请key值。

2.管道通信跟消息队列非常相似,它们之间的区别???

1)管道通信:

不能读取指定的数据,只要管道中有数据,就一定要读取出来,操作的时候使用
open /  write  read -->文件描述符(系统IO来操作 write read)

2)消息队列:

消息队列是一种带有数据标识的特殊管道,消息队列可以读取指定的数据,如果里面有多个数据,但是不符合我的类型,我可以不读取,
操作时候使用消息队列中独有的函数接口:msgsnd / msgrcv

3.消息队列机制:

进程1往消息队列中写入数据时,“类型”+ “数据正文”  //类型 ---数据的编号
进程2从消息队列中读取数据时,只需要提供数据的编号就可以读取到指定的数据了。

4.消息队列作用的范围: linux下任意两个进程。

三、使用消息队列来通信的步骤

1.申请消息队列的key值

key_t key = ftok(".",10);

2.根据消息队列的key值获取ID号 ,如果该key值对应的消息不存在,会创建 -->man 2 msgget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg); // 类似 之前的open("./1.txt",O_CREAT,0666)
函数作用
    得到一个消息队列的ID号
参数:
    key:消息队列的key值
    msgflg: IPC_CREAT|0666  如果不存在则创建。并且给权限
返回值:
    成功返回 消息队列的ID号
    失败返回 -1
例子:创建一条消息队列,并且把key值和id打印出来
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

//进程1 :  往消息队列中发送数据

//自己定义的消息队列的数据结构体
struct msgbuf{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//3、往文件中写入数据 --------往消息队列中 发送数据struct msgbuf data;memset(&data,0,sizeof(data));strcpy(data.mtext,"nihao");data.mtype = 10;int ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}return 0;
}

3.发送 /写入数据

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
函数作用:
    往消息队列中写入数据
参数:
    msqid:消息队列的ID号
    msgp:你要写入的数据,注意传递的是 入的数据结构体的地址
    msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
    msgflg:一般属性,默认为0
返回值:
    成功返回 0
    失败返回 -1

电子相册:
struct node{
    char bmpName[256]; //数据的正文
    int index;//数据的编号
}
消息队列:
10 + "数据"
//写入结构体 是由 用户 自己定义的 ,也就是该结构体的数据类型是我们自己设计
struct msgbuf {
long mtype;       /* 消息类型/数据的编号  而且是>0* /
char mtext[1024];    /* 数据的正文 */
};

4.从消息队列中读取数据(接收数据) ---》man 2 msgrcv

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
参数:
    msqid:消息队列的ID号
    msgp:读取的数据存储到这里,注意 传递的是 读取的数据结构体的地址
    msgsz:数据正文的大小,也就是 char mtext[1024] 的大小,(注意不是整个结构体的大小)
    msgtyp:读取的数据的类型或者说数据的编号
    msgflg:一般属性,默认为0
返回值:
    成功返回  读取的字节数
    失败 返回 -1
练习1:
发送端先发10+“hai”再发20+"nihao"
接收端选择性接收10或者20的内容

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//自己定义的消息队列的数据结构体
struct msgbuf
{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//通过消息队列发送//3、往文件中写入数据 --------往消息队列中 发送数据struct msgbuf data;memset(&data,0,sizeof(data));//给char mtext[1024]赋值的时候要用strcpydata.mtype = 10; //数据编号是10strcpy(data.mtext,"hai");//msgsendint ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}  return 0;
}
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//自己定义的消息队列的数据结构体
struct msgbuf
{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//3、通过消息队列接收struct msgbuf data;memset(&data,0,sizeof(data)); //清空结构体//msgrcvint ret;ret = msgrcv(msgid, &data, sizeof(data.mtext),10,0);printf("msgrcv ret :%d data:%s\n",ret,data.mtext);//4、最后不需要 使用到这条消息队列的时候,删除即可msgctl(msgid, IPC_RMID, NULL);return 0;
}

5.删除消息队  msgctl=message contol

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //cmd --->command
参数:
    msqid:你要删除哪条消息队列,将消息队列的ID号传递过来
    cmd: 操作的命令
        IPC_STAT -->获取消息队列的状态  ---》最后一个参数要填
        IPC_RMID---》删除消息队列 ---》删除不需要第三个参数,但是要填NULL
    buf: 获取的那些数据 存储到这个结构体里面
返回值:
    成功返回 0
    失败返回 -1

比如删除消息队列:
msgctl(msgid,IPC_RMID,NULL);(msg的通常用法)

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

//进程2 : 从消息队列中 获取数据

//自己定义的消息队列的数据结构体
struct msgbuf{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//3、往文件中写入数据 --------往消息队列中 发送数据struct msgbuf data;memset(&data,0,sizeof(data));int ret = msgrcv(msgid, &data, sizeof(data.mtext),10,0);printf("msgrcv ret :%d data:%s\n",ret,data.mtext);//4、最后不需要 使用到这条消息队列的时候,删除即可msgctl(msgid, IPC_RMID, NULL);return 0;
}

练习2:进程1可以不断地发送数据给进程2 ,进程2接收到数据 然后打印出来
管道:open write read close
消息队列:ftok msgget msgsnd msgrcv msgctl

#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>//进程1 :  往消息队列中发送数据//自己定义的消息队列的数据结构体
struct msgbuf{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main(int argc,char *argv[])
{
/*  1、确定 文件路径名 ---获取消息队列的key值*/ key_t key = ftok(".",100);
/*  2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建*/   int msgid = msgget(100,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x/*3、往文件中写入数据 --------往消息队列中 发送数据*/struct msgbuf data;memset(&data,0,sizeof(data));//清空结构体while(1){printf("input mtext内容\n");scanf("%s",data.mtext);printf("input mtype编号\n");scanf("%ld",&data.mtype);strcpy(data.mtext,data.mtext);   int ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}if(strcmp(data.mtext,"quit") == 0){break;}printf("%s\n",data.mtext);printf("%ld\n",data.mtype);}return 0;
}
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>//进程1 :  往消息队列中发送数据//自己定义的消息队列的数据结构体
struct msgbuf{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};int main(int argc,char *argv[])
{
/*  1、确定 文件路径名 ---获取消息队列的key值*/ key_t key = ftok(".",100);
/*  2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建*/   int msgid = msgget(100,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x/*3、往文件中读取数据 --------往消息队列中 读取数据*/struct msgbuf data;memset(&data,0,sizeof(data));//清空结构体while(1){int ret = msgrcv(msgid, &data,sizeof(data.mtext),10,0);if(ret == -1){perror("msgrecv error");return -1;}if(strcmp(data.mtext,"quit") == 0){break;}printf("%s\n",data.mtext);  printf("%ld\n",data.mtype);           }/*4、最后不需要 使用到这条消息队列的时候,删除即可*/int del = msgctl(msgid, IPC_RMID, NULL);if(del == -1){perror("msgctl error\n");return -1;}else{printf("msgctl success\n");}return 0;
}

四、进程之间的通信方式 --- 共享内存。share-memory

1.共享内存也是属于IPC对象,所以使用之前必须申请key值。
2.共享内存作用范围以及机制是如何?

1)作用范围:在linux下任意两个进程。
2)机制:任意两个进程通过申请key值,ID号,共享内存得到一片内存空间,那么这两个进程就可以将数据写入到共享内存/读取共享内存上的数据进行数据的交换。

3.实现共享内存步骤:

1)先申请key值。

key_t key = ftok(".",10);

2)根据申请到的key值去申请共享内存的ID号。  -> shmget()  -> man 2 shmget   (share memory get)

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg); //shm --->share memory
参数:
    key: key值
    size: 共享内存的总字节数,必须是PAGE_SIZE的倍数   #define PAGE_SIZE 1024
    shmflg:IPC_CREAT  -> 不存在则创建
        IPC_CREAT|0666
返回值:
    成功:共享内存ID号
    失败:-1
    
3)根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域---映射

#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数:
    shmid: 共享内存ID号
    shmaddr:
        NULL  -> 系统自动分配地址给你  99.999%
        不为NULL -> 用户自己选择地址  0.001%
    shmflg:普通属性,填0
返回值:
    成功:共享内存的起始地址
    失败:(void *)-1
通常用法:shmat(shmid,NULL,0)

4)当不再使用时,解除映射关系

int shmdt(const void *shmaddr);
参数:
    shmaddr :你需要解除内存映射的地址
返回值:
    成功:0
    失败:-1

5)当没有进程再需要使用这块共享内存时,删除它-> shmctl()  -> man 2 shmctl

#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
    shmid:共享内存ID号
    cmd:操作命令
        IPC_STAT --->获取共享内存的属性 --》必须填第三个参数
        IPC_RMID --->删除共享内存  --》填 NULL
    buf:看cmd
返回值:
    成功:0
    失败:-1

通常用法:
    shmctl(id,IPC_RMID,NULL);//删除共享内存

说明:
#include <string.h>
char *strcpy(char *dest, const char *src); //字符串拷贝-->使用于字符串操作
void *memcpy(void *dest, const void *src, size_t n); //内存拷贝-->适用于内存操作

4.例子:通过共享内存实现两个进程的通信

1)写端:

#include<stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//进程1
int main()
{//1、申请key值 key_t key = ftok(".",10);//2、根据key值, 得到物理共享内存的ID号,如果该物理内存不存在,则创建  ---openint shmid = shmget(key,1024,IPC_CREAT|0666);printf("共享内存 key:%#x shmid:%d\n",key,shmid);//3、将物理内存 映射到 用户的虚拟内存空间中的某一块区域char*shm_p = shmat(shmid,NULL,0);if(shm_p == (void*)-1){perror("shmat error");return -1;}//4、直接往这块内存进行赋值(写入数据)//可以不断从键盘上获取数据,当输入exit的时候退出char sendbuf[1024] = "hello world";memcpy(shm_p,sendbuf, strlen(sendbuf));while(1);//5、最后不用的时候解除 映射 shmdt(shm_p);return 0;
}

2)读端:

#include<stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
//进程2 int main()
{//1、申请key值 key_t key = ftok(".",10);//2、根据key值, 得到物理共享内存的ID号,如果该物理内存不存在,则创建  ---openint shmid = shmget(key,1024,IPC_CREAT|0666);printf("共享内存 key:%#x shmid:%d\n",key,shmid);//3、将物理内存映射到用户的虚拟内存空间中的某一块区域char*shm_p = shmat(shmid,NULL,0);if(shm_p == (void*)-1){perror("shmat error");return -1;}//4、直接往这块内存读取数据//不断地读取数据,当获取到exit的时候退出printf("%s\n",shm_p);while(1);//5、最后不用的时候解除 映射 shmdt(shm_p);//6、删除共享内存shmctl(shmid,IPC_RMID,NULL);return 0;
}

结果:出现数据践踏,我们写了一次数据进去,那么就无限打印。
我们想要的是,更新一次数据,就打印一次就行。-----(用信号量来解决数据践踏)

五、处理进程之间通信--共享内存的同步互斥问题  -> 信号量。

1.什么是信号量?

信号量也是属于IPC对象,所以使用信号量之前必须先申请key值。信号量不是用于进程之间的通信,
但是作用于进程之间的通信。(信号量+共享内存,信号量是一种防护机制)

2.信号量的函数接口?--semaphore

1)由于信号量属于IPC对象,所以要申请key值。

key = ftok(".",10);

2)根据key值申请信号量ID号。 -> semget()  -> man 2 semget

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
参数:
    key:信号量的key值
    nsems:  信号量元素的个数。例如: 空间+数据  -> 2
    semflg:  IPC_CREAT|0666  -> 不存在则创建
返回值:
    成功: 信号量ID
    失败: -1

3)控制/设置信号量值参数。  -> semctl()  -> man 2 semctl

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
参数:
    semid:信号量ID
    semnum:需要操作的成员的下标   空间:0  数据:1
    cmd:    SETVAL  -> 用于设置信号量的起始值
            IPC_RMID -> 删除信号量的ID
    ...:    空间/数据的起始值
    例如: 想设置空间的起始值为1,数据的起始值为0
    semctl(semid,0,SETVAL,1);//设置空间的起始值为1 (有车位)
    semctl(semid,1,SETVAL,0);//数据的起始值为0 (没车)
返回值:
    成功:0
    失败:-1

4)如何实现信号量的P/V操作?  (P操作: 1->0  V操作: 0->1)

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
函数参数:
    semid:  信号量ID号
    sops:进行P/V操作结构体
    nsops: 信号量操作结构体的个数 -> 1
        struct sembuf{
        unsigned short sem_num;   //需要操作的成员的下标  空间:0  数据:1
        short          sem_op;    //P操作/V操作           P: -1  V: 1
        short          sem_flg;   //普通属性,填0.
        }

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

例题1: 将信号量加入到今天上午的代码中。参考信号量.jpg。

03共享内存+信号量实现通信write.c

#include<stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main()
{//1、获取key值key_t key = ftok(".",10);//2、根据key值 获取共享内存的ID号int shmid = shmget(key,1024,IPC_CREAT|0666);//3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域char*shm_p = shmat(shmid,NULL,0);if(shm_p == (void*)-1){perror("shmat error");return -1;}//4、获取信号量的key值key_t key1 = ftok(".",20);//5、根据key值申请信号量ID号int semid = semget(key1,2,IPC_CREAT|0666);//6、初始化信号量起始值semctl(semid,0,SETVAL,1);//设置空间的起始值为1 //有空间semctl(semid,1,SETVAL,0);//设置数据的起始值为0 //无数据//空间结构体struct sembuf space;space.sem_num = 0;//空间space.sem_op = -1;//P操作space.sem_flg = 0;//普通属性//数据结构体struct sembuf data;data.sem_num = 1;//数据data.sem_op = 1;//V操作data.sem_flg = 0;//普通属性//此时映射出来的shm_p 就是两个进程的共享内存while(1){//空间:1  数据:0//开车进去之前,空间 -1 --P操作semop(semid, &space, 1);//请问空间能不能P操作?//能   -> 有车位  -> 函数返回//不能 -> 没车位  -> 函数阻塞//开车进去//从键盘上获取数据,存储到共享内存shm_pscanf("%s",shm_p);//开车进去之后,数据+1 --V操作semop(semid, &data, 1); //数据自动+1//退出条件,这里要注意 应该使用strncmp 指定字节数if(strncmp(shm_p,"exit",4) == 0)break;}//4、当不再使用时,解除映射关系shmdt(shm_p);//5、当没有进程再需要使用这块共享内存时,删除它shmctl(shmid,  IPC_RMID, NULL);semctl(semid,0,IPC_RMID);return 0;
}

04共享内存+信号量实现通信read.c

#include<stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>int main()
{//1、获取key值key_t key = ftok(".",10);//2、根据key值 获取共享内存的ID号int shmid = shmget(key,1024,IPC_CREAT|0666);//3、根据ID号 将共享内存映射至本进程虚拟内存空间的某个区域char*shm_p = shmat(shmid,NULL,0);if(shm_p == (void*)-1){perror("shmat error");return -1;}//4、获取信号量的key值key_t key1 = ftok(".",20);//5、根据key值申请信号量ID号int semid = semget(key1,2,IPC_CREAT|0666);//6、初始化信号量起始值semctl(semid,0,SETVAL,1);//设置空间的起始值为1semctl(semid,1,SETVAL,0);//设置数据的起始值为0//空间结构体struct sembuf space;space.sem_num = 0;space.sem_op = 1; //V操作space.sem_flg = 0;//数据结构体struct sembuf data;data.sem_num = 1;data.sem_op = -1;//P操作data.sem_flg = 0;//此时映射出来的shm_p 就是两个进程的共享内存while(1){//空间:0  数据:1//把车开出来之前,请问数据能不能-1?semop(semid, &data, 1);//能  -> 里面有车  -> 函数返回//不能 -> 里面没车 -> 函数阻塞//从车库里面把车开出来//从共享内存中读取数据printf("recv:%s\n",shm_p);//sleep(1);//把车开出来之后,空间+1semop(semid, &space, 1);//空间:1  数据:0//退出条件,这里要注意 应该使用strncmp 指定字节数if(strncmp(shm_p,"exit",4) == 0)break;}return 0;
}

作业

使用消息队列实现互聊(当一方发送"byebye",所有进程结束要借助 发送信号)

#include<stdio.h>
#include <string.h>
#include <unistd.h>/*fork exit*/
#include <sys/types.h>/*msgget*/
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>/*signal*/
#include <stdlib.h>
#include <wait.h>//自己定义的消息队列的数据结构体
struct msgbuf
{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};void handle(int arg)
{printf("收到%d信号退出\n",arg);exit(0);
} int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//父进程和子进程都能处理的信号signal(SIGUSR1,handle);pid_t id;id = fork();if(id == -1){perror("fork fail");return -1;}else if(id > 0){    //往消息队列中 发送数据struct msgbuf data;memset(&data,0,sizeof(data));//给char mtext[1024]赋值的时候要用strcpydata.mtype = 10; //数据编号是10while(1){   //strcpy(data.mtext,"hai");scanf("%s",data.mtext);//msgsendint ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}if(strcmp(data.mtext,"byebye") == 0){kill(id,SIGUSR1); //给子进程发送一个退出信号//exit(0);break;}} wait(NULL);}else if(id == 0){//3、通过消息队列接收struct msgbuf data;while(1){ memset(&data,0,sizeof(data)); //清空结构体int ret;ret = msgrcv(msgid, &data, sizeof(data.mtext),20,0);printf("msgrcv ret :%d data:%s\n",ret,data.mtext);  if(strcmp(data.mtext,"byebye") == 0){kill(getppid(),SIGUSR1); //给父进程发送一个退出信号exit(0);}}  }}
#include<stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <signal.h>
#include <wait.h>//自己定义的消息队列的数据结构体
struct msgbuf
{long mtype;//数据的编号/类型char mtext[1024]; //数据的正文
};void handle(int arg)
{printf("收到%d信号退出\n",arg);exit(0);
} int main()
{//1、确定 文件路径名 ---获取消息队列的key值key_t key = ftok(".", 100);//2、根据文件路径名,打开文件 文件不存在则创建 得到fd ---根据key值 获取消息队列的ID,如果消息队列不存在则创建int msgid = msgget(key,IPC_CREAT|0666);if(msgid == -1){perror("msgget error");return -1;}printf("消息队列 key:%#x msgid:%d\n",key,msgid); //0x//父进程和子进程都能处理的信号signal(SIGUSR1,handle);pid_t id;id = fork();if(id == -1){perror("fork fail");return -1;}else if(id > 0){//往消息队列中 发送数据struct msgbuf data;memset(&data,0,sizeof(data));//给char mtext[1024]赋值的时候要用strcpydata.mtype = 20; //数据编号是10while(1){   //strcpy(data.mtext,"hai");scanf("%s",data.mtext);//msgsendint ret = msgsnd(msgid, &data,strlen(data.mtext),0);if(ret == -1){perror("msgsnd error");return -1;}if(strcmp(data.mtext,"byebye") == 0){kill(id,SIGUSR1); //给子进程发送一个退出信号//exit(0);break;}} wait(NULL);}else if(id == 0){//3、通过消息队列接收struct msgbuf data;while(1){ memset(&data,0,sizeof(data)); //清空结构体int ret;ret = msgrcv(msgid, &data, sizeof(data.mtext),10,0);printf("msgrcv ret :%d data:%s\n",ret,data.mtext);  if(strcmp(data.mtext,"byebye") == 0){kill(getppid(),SIGUSR1); //给父进程发送一个退出信号exit(0);}}  }//4、最后不需要 使用到这条消息队列的时候,删除即可msgctl(msgid, IPC_RMID, NULL);return 0;
}

系统编程04-消息队列(ftok、msgget、msgsnd、msgrcv、msgctl、shmget、shmat、shmdt、shmctl、semget、semctl、semop)相关推荐

  1. msgget();msgsnd();msgrcv();msgctl(); 消息队列 Linux进程间的通信方式之消息队列

    Linux进程间的通信方式 ----消息队列. 消息队列和共享内存类似 消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息. 消息队列存在于系统内核中,消息的数量受系统限制. 我们来看 ...

  2. python队列线程池_实例详解:python高级编程之消息队列(Queue)与进程池(Pool)

    今天为大家带来的内容是:python高级编程之消息队列(Queue)与进程池(Pool),结合了实例的形式详细分析了Python消息队列与进程池的相关原理.使用技巧与操作注意事项!!! Queue消息 ...

  3. PHP高级编程之消息队列

    1. 什么是消息队列 消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式 2. 为什么使用消息队列 消息队列技术是分布式应用间交换信息的一种技术.消息队列可驻 ...

  4. win7系统中的消息队列服务器,高手分析win7系统安装消息队列的详细

    win7系统是计算机学员最喜欢使用的电脑系统,却有一些学员在操作过程中应该会面临对win7系统安装消息队列进行布置的情景.几乎所有的初学者计算机知识还很薄弱,关于win7系统安装消息队列的情况,仍旧一 ...

  5. HarmonyOS IoT设备内核编程接口-----消息队列

    一.概念 消息队列,是一种常用于任务间通信的数据结构,实现了接收来自任务或中断的不固定长度的消息,并根据不同的接口选择传递消息是否存放在自己空间.任务能够从队列里面读取消息,当队列中的消息是空时,挂起 ...

  6. Linux共享内存二维数组,linux系统编程--shmget shmat shmdt

    要使用共享内存,应该有如下步骤: 1.开辟一块共享内存 shmget() 2.允许本进程使用共某块共享内存 shmat() 3.写入/读出 4.禁止本进程使用这块共享内存 shmdt() 5.删除这块 ...

  7. linux shmget ftok shmat shmdt shmctl函数

    这里写目录标题 一 上一节的回顾 二 上一节的补充 三 ftok shmget 函数配合生成的key值 四 shmat 函数 共享内存映射到用户态的根据 五 shmdt函数 六 shmctl函数 1 ...

  8. linux.调整收发队列,linux消息队列通信

    程序目的:学习linux消息队列通信 所用主要函数:msgget(),msgsnd(),msgrcv(),msgctl() 首先介绍每个函数的用法: (1)msgget 使用格式: #include ...

  9. linux扩充消息队列,Linux关于消息队列的使用分享

    今天完成消息队列的使用. 消息队列类似FIFO,但是可以实现随机查询. 主要函数:msgget,msgsnd,msgrcv,msgctl 实验实现两个进程之间的通信. 一个发送,一个接受.并不需要额外 ...

最新文章

  1. 查看dev下设备名的含义
  2. [BJWC2018]Border 的四种求法(后缀自动机+链分治+线段树合并)
  3. In-Loop Filters in HEVC
  4. C#设计模式之1-工厂方法模式
  5. python程序内存分析_python 如何测量运行中的程序内存 -- Valgrind
  6. 【kafka】Kafka扩容
  7. 光纤到桌面FTTD解决方案
  8. Linux下修改Mysql的用户(root)的密码的俩种方法
  9. [BZOJ2850]巧克力王国
  10. Mybatis-学习笔记(9)Mybatis3+spring4+springMVC
  11. mysql5.7.19zip,mysql5.7.19zip详细安装过程和配置
  12. 数据挖掘:计算边的中介中心值 edge_betweenness value
  13. python黑帽子学习笔记(三)—— ssh隧道
  14. MATLAB简单入门
  15. 真格量化学习处理——几个功能小函数
  16. 夏普PC_1500计算机使用,夏普PC-1500袖珍计算机的检修(续)
  17. 电容之超级电容简易测试方法
  18. java 数字转换字母大写_大写字母或小写字母转换为数字
  19. H3C交换机静态路由与NQA联动
  20. 代码执行器 hook console.log 方案

热门文章

  1. 张高兴的 .NET IoT 入门指南:(八)基于 GPS 的 NTP 时间同步服务器
  2. Python网络爬虫8 - 爬取彼岸图网美图
  3. NoSQL数据库概念与NoSQL数据库家族
  4. C语言 简单选择排序法
  5. TCP会话开始与结束
  6. RK平台mipi屏初始化序列配置
  7. 浅谈android源码之system
  8. 使用Python开发windows桌面程序【超简单】
  9. Apache服务器虚拟主机和别名配置及问题
  10. pyhton-字符串对象的函数