文章目录
1.单工,半双工,全双工
单工:数据传输只支持数据在一个方向上传输
半双工:数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输(pipe)
全双工:数据通信允许数据同时在两个方向上传输(msg)
协议:约定对话格式
主动端 : 先发包的一方
被动端: 先收包的一方(先运行)
2.匿名管道
匿名管道的特征:
(1)有空间大小,只能进行单向通信;
(2)只适用于有血缘关系之间的进程;
(3)自带同步和互斥机制;
(4)在进行通信时面向字节流服务;
(5)生命进程随周期。
- 当进程读到一个空管道时会处于阻塞态,直到数据到来。没有写端,返回eof表示读完。
- 当进程写到一个满管道时会处于阻塞态,直到数据读走。没有读端只有写端,会产生信号结束进程
- 必须有读写双方
- 不能用lseek来定位文件偏移量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azHmDb9V-1632914955078)(C:\Users\renlichao\AppData\Roaming\Typora\typora-user-images\image-20210926150440485.png)]
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>
#define BUFSIZE 128
static int mycp(int rfd, int wfd);
int main(int argc, char *argv[])
{
int pfd[2] = {};
pid_t pid;
int fd;
if (argc < 2)
exit(1);
if (pipe(pfd) == -1) {
perror("pipe()");
exit(1);
}
pid = fork();
if (-1 == pid) {
perror("fork()");
close(pfd[0]);
close(pfd[1]);
exit(1);
}
if (0 == pid) {
close(pfd[1]);
mycp(pfd[0], 1);
close(pfd[0]);
exit(0);
}
close(pfd[0]);
// 打开argv[1]
fd = open(argv[1], O_RDONLY);
if (-1 == fd) {
close(pfd[1]);
perror("open()");
exit(1);
}
mycp(fd, pfd[1]);
close(pfd[1]);
wait(NULL);
return 0;
}
static int mycp(int rfd, int wfd)
{
char buf[BUFSIZE] = {};
int cnt;
while (1) {
cnt = read(rfd, buf, BUFSIZE);
if (-1 == cnt) {
return -errno;
}
if (0 == cnt)
break;
write(wfd, buf, cnt);
}
}
3.消息队列
ipcs 查看消息队列 共享内存端 信号量数组
ipcrm -q msgid删除消息队列
四步走
ftok XXXget XXXop XXXctl
都服务于多进程
消息队列:交换数据
共享内存:得到共享存储空间
信号量:解决多进程之间的竞争,可以限制资源,也可以限制资源的使用
(1)获取key值
key_t ftok(const char *pathname, int proj_id);
//拿到同一个key值
参数1: pathname必须是存在的文件
参数2:一个0-255的数
(2)创建消息队列
int msgget(key_t key, int msgflg);
参数1:传一个key值
参数2:
IPC_PRIVATE | 私有的 作用于有亲缘关系的进程 |
---|---|
IPC_CREAT | 无则创建 |
IPC_EXCL | 有则取消 |
(3)接受和发送消息
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
//往队列发东西
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
//接收东西 msgtyp为0从头接收消息 <0时接收<=绝对值的消息 >0时接受msgtyp的消息
参数1:接受(发送)消息的消息队列
参数2:接受(发送)数据地址
参数3:接受(发送)数据的大小
参数4:接受(发送)特殊要求
(4)对消息队列进行操作(通常是销毁消息队列)
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
//要对那个队列进行什么样的cmd 对cmd进行什么样的传参
例:
.h文件
#ifndef _MSG_H_
#define _MSG_H_
#define PATHNAME "/etc/passwd"
#define PROJ 1
#define NAMESIZE 32
struct stu_st
{
int id;
char name[NAMESIZE];
};
struct msg_st
{
long mtype;
struct stu_st student;
};
#endif
rcv.c(接收端)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include "msg.h"
int msgInit(void)
{
key_t key;
int msgid;
key = ftok(PATHNAME,PROJ);//获取key值
if(key==-1)
{
return -errno;
}
msgid = msgget(key,IPC_CREAT | IPC_EXCL | 0600);//创建消息队列
if (-1 == msgid) {
if (EEXIST == errno) {
msgid = msgget(key, 0);
} else
return -errno;
}
return msgid;
}
int main()
{
int msgid;
struct msg_st rcvbuf;
msgid = msgInit();//创建消息队列
if(msgid<0)
{
fprintf(stderr,"msgInit():%s",strerror(-msgid));
exit(0);
}
while(1)
{
if(msgrcv(msgid,&rcvbuf,sizeof(struct stu_st),0,0)==-1)//接收数据
{
perror("msgrcv()");
msgctl(msgid,IPC_RMID,NULL);
exit(1);
}
printf("%d %s\n",rcvbuf.student.id,rcvbuf.student.name);
}
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <errno.h>
#include "msg.h"
int msgInit(void)
{
key_t key;
int msgid;
key = ftok(PATHNAME,PROJ);
if(key==-1)
{
return -errno;
}
msgid = msgget(key,IPC_CREAT | IPC_EXCL | 0600);
if(msgid==-1){
if(errno==EEXIST)
{
msgid = msgget(key,0);
}
else
return -errno;
}
return msgid;
}
int main(int argc,char *argv[])
{
int msgid;
struct msg_st sndbuf;
msgid = msgInit();
if(msgid<0)
{
fprintf(stderr,"msgInit():%s",strerror(-msgid));
exit(0);
}
//初始化结构体
sndbuf.student.id = atoi(argv[1]);
strncpy(sndbuf.student.name, argv[2], NAMESIZE);
sndbuf.mtype = atoi(argv[3]);
msgsnd(msgid,&sndbuf,sizeof(struct stu_st),0);//发送数据
exit(0);
}
4.共享内存端
(1)创建共享内存
int shmget(key_t key, size_t size, int shmflg);
//得到一个共享内存标识符或创建一个共享内存对象并返回共享内存标识符
参数1:获得一个key值
参数2:新建的共享内存大小,以字节为单位
参数3:IPC_CREAT|IPC_EXCL:如果内核中不存在键值 与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存则报错
(2)映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
//把共享内存区对象映射到调用进程的地址空间
int shmdt(const void *shmaddr);
//解除映射关系
成功:返回共享内存地址
(3)共享内存管理
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//共享内存管理
参数1:共享内存标识符
参数2:
IPC_STAT | 得到共享内存的状态 |
---|---|
IPC_SET | 改变共享内存的状态 |
IPC_RMID | 删除这片共享内存 |
参数3:共享内存管理结构体
(4)例题
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(void)
{
int shmid;
pid_t pid;
char *ptr;
shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT | IPC_EXCL | 0600);//创建获取共享内存
if (-1 == shmid) {
if (EEXIST == errno) {
shmid = shmget(IPC_PRIVATE, 1024, 0);
} else {
perror("shmget()");
exit(1);
}
}
pid = fork(); //创建子进程
if (-1 == pid) {
perror("fork()");
shmctl(shmid, IPC_RMID, NULL);
exit(1);
}
if (0 == pid) {
ptr = shmat(shmid, NULL, 0);//内存映射
memcpy(ptr, "good afternoon", 14);
shmdt(ptr);//解除映射
exit(0);
}
wait(NULL);
ptr = shmat(shmid, NULL, 0);
sleep(20);
puts(ptr);
shmdt(ptr);
shmctl(shmid, IPC_RMID, NULL);//销毁共享内存
return 0;
}