2021-09-29 第十五章 进程间通信

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;	
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RLC214

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值