进程间通信(一)

本文介绍了进程间通信的目的,如数据传输、资源共享、事件通知和进程控制。详细讲解了管道(匿名管道与命名管道)的概念、工作原理和特性,并展示了创建和使用匿名管道与命名管道的示例。接着,阐述了消息队列,包括其结构、创建与删除方法,以及msgget、msgsnd和msgrcv等函数的使用,通过实例展示了进程间消息的传递。
摘要由CSDN通过智能技术生成

进程间通信的目的

1、数据传输
一个进程将数据发送给另一个进程
2、资源共享
多个进程之间共享相同的资源
3、通知事件
第一个进程需要向另一个进程发送消息,通知发生的事件
4、进程控制
有些进程希望完全控制另一个进程的执行,此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够知道它的状态改变

进程间通信的本质就是两个互不相干的进程看到同一份资源,该资源一定是操作系统提供的

进程间通信分类

管道

* 匿名管道
* 命名管道

System V IPC

* System V 消息队列
* System V 共享内存
* System V 信号量

POSIX IPC

* 消息队列
* 共享内存
* 信号量
* 互斥量
* 条件变量
* 读写锁

一、管道

本质上,管道也是一种文件,我们把一个进程连接到另一个进程的数据流称为管道

*匿名管道*
具有亲缘关系的进程进行数据间通信
我们创建一个子进程,通过管道实现子进程写的数据父进程可以读出来

1、父进程创建管道
这里写图片描述
2、父进程fork出子进程
这里写图片描述
3、父进程关闭写端、子进程关闭读端
这里写图片描述
代码实现:
这里写图片描述
匿名管道特点:
1、只能用于具有亲缘关系的进程
2、进程退出,管道释放,所以管道的生命周期随进程
3、内核会对管道操作进行同步与互斥(管道自带同步 )
4、单向传输,半双工的
5、管道是基于字节流的

四种情况:
* 写段关闭,读端一直在读,直到0值
这里写图片描述
这里写图片描述
* 写端不写也不关闭,读端一直在读
这里写图片描述
这里写图片描述
* 写端一直写,读端不读,管道满,等待读
* 写端一直写,读端关闭,写端被系统终止

命名管道
可以用于没有亲缘关系的进程
创建一个命名管道
方法一:
这里写图片描述
方法二:
这里写图片描述
这里写图片描述

匿名管道与命名管道的区别:

* 匿名管道由pipe函数创建并打开
* 命令管道由mkfifo函数创建,打开用open
* 匿名管道与匿名管道的唯一区别在于它们创建与打开方式不同

用命名管道实现客户端与服务器端之间的通信
server.c(服务器端)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define ERR_EXIT(m) \
    do{ \
        perror(m); \
        exit(EXIT_FAILURE); \
    }while(0)

 int main()
 {
     umask(0);
     if(mkfifo("ppipe",0644) == -1)
     {
         ERR_EXIT("mkfifo");
     }

     int fd = open("ppipe",O_RDONLY);
     if(fd == -1){
         ERR_EXIT("open");
     }

     char buf[1024];
     while(1){
         buf[0] = 0;
         ssize_t s = read(fd, buf, sizeof(buf)-1);
         if(s > 0){
             buf[s-1] = 0;
             printf("client : %s\n",buf);
         }else if(s == 0){
             printf("client quit,exit now\n");
         }else{
             ERR_EXIT("read");
         }
     }

     close(fd);
     return 0;
 }

client.c(客户端)

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define ERR_EXIT(m) \
     do{ \
         perror(m); \
         exit(EXIT_FAILURE); \
     }while(0)

 int main()
 {
     int fd = open("ppipe",O_WRONLY);
     if(fd == -1)
     {
         ERR_EXIT("open");
     }                                                                                                                                          22
     char buf[1024];
     while(1){
         buf[0] = 0;
         printf("Please Enter:");
         fflush(stdout);
         ssize_t s= read(0, buf, sizeof(buf)-1);
         if(s > 0){
             buf[s] = 0;
             write(fd, buf, strlen(buf));
         }else if(s <= 0){
             ERR_EXIT("read");
         }
     }

     close(fd);
     return 0;

 }

打开两个终端,客户端输入一条信息,服务器端接受到信息后在屏幕上显示。
这里写图片描述
这里写图片描述

二、消息队列

* 一个进程向另一个进程发送有类型数据块的方法
* 可以实现相互通信的功能
* 进程的生命周期随内核
* 消息队列是基于消息的

系统为每一个IPC对象保存一个ipc_perm结构体,该结构体说明了IPC对象的权限和所有者,每一个版本的内核有不同的ipc_perm结构成员,最重要的是key成员,表示队列的编号。
这里写图片描述
消息队列结构
从第一个成员,我们可以知道,消息队列中也含有队列的编号。
这里写图片描述
因为消息队列的生命周期随内核,所以我们需要手动删除一个消息队列
ipcs -q:显示IPC资源
ipcrm -q [消息队列的key值]:删除IPC资源

要想实现一个消息队列,首先认识一些函数
1、msgget:创建和访问一个消息队列
第二个参数:
IPC_CREAT|IPC_EXCL:如果没有该消息队列,则创建一个;如果有,则出错返回。如此创建的消息队列是全新的。
这里写图片描述
2、ftok函数
说明:
* ftok函数根据路径名,提取文件信息,再根据这些文件信息及proj_id合成key,该路径可以随便设置
* pathname必须存在,和文件的权限无关
* proj_id是随意设置的,在UNIX系统上,取值为1~255
这里写图片描述
3、msgsnd函数:把一条消息添加到消息队列中
msgrcv函数:从一个消息队列接受消息
这里写图片描述

代码实现:
makefile

.PHONY:all
 all:client server

 client:client.c comm.c
         gcc -o $@ $^

 server:server.c comm.c
         gcc -o $@ $^

 .PHONY:clean
 clean:
         rm -f client server

comm.h

#ifndef __COMM__H_
#define __COMM__H_

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

 #define PATHNAME "."
 #define PROJ_ID 0x664

 #define SERVER_TYPE 0                                                     
 #define CLIENT_TYPE 200

 struct msgbuf{
     long mtype;
     char mtext[1024];
 };

 int createMsgQueue();
 int getMsgQueue();
 int destroyMsgQueue(int msgid);
 int sendMsg(int msgid, int who, char *msg);
 int recvMsg(int msgid, int recvType, char out[]);


 #endif //_COMM_H_  

comm.c

#include"comm.h"                                                         
static int commMsgQueue(int flags)
{
    key_t _key=ftok(PATHNAME, PROJ_ID);
    if(_key < 0){
        perror("ftok");
        return -1;
    }
    int msgid = msgget(_key,flags);
     if(msgid < 0){
         perror("msgget");
     }

     return msgid;
 }

 int createMsgQueue()
 {
     return commMsgQueue(IPC_CREAT|IPC_EXCL|0x664);
 }

 int getMsgQueue()
 {
     return commMsgQueue(IPC_CREAT);
 }

 int destoryMsgQueue(int msgid)
 {
     if(msgctl(msgid, IPC_RMID, NULL)<0){
         perror("msgctl");
         return -1;
     }
     return 0;
 }

 int sendMsg(int msgid, int who,char *msg)
 {
     struct msgbuf buf;
     buf.mtype = who;
     strcpy(buf.mtext,msg);

     if(msgsnd(msgid, (void*)&buf,sizeof(buf.mtext),0)<0){
         perror("msgsnd");
         return -1;
     }
     return 0;
 }

 int recvMsg(int msgid, int recvType, char out[])
 {
     struct msgbuf buf;
     if(msgrcv(msgid,(void*)&buf, sizeof(buf.mtext),recvType, 0)<0){
         perror("msgrcv");
         return -1;
     }
     strcpy(out,buf.mtext);
     return 0;
 }

client.c

#include "comm.h"

int main()
{
    int msgid = getMsgQueue();

    char buf[1024];
    while(1){
        buf[0] = 0;
        printf("Please Enter:>");
        fflush(stdout);
        ssize_t s = read(0, buf, sizeof(buf));
        if(s>0){
            buf[s-1] = 0;
            sendMsg(msgid, CLIENT_TYPE, buf);
            printf("send done,wait...\n");
        }

        recvMsg(msgid, SERVER_TYPE, buf);
        printf("server:>%s\n", buf);
    }
    return 0;
 }

server.c

#include "comm.h"

 int main()
 {
     int msgid = createMsgQueue();

     char buf[1024];
     while(1){
         buf[0] = 0;
         recvMsg(msgid, CLIENT_TYPE, buf);
         printf("client:>%s\n",buf);

         printf("Please Enter:>");
         fflush(stdout);
         ssize_t s = read(0, buf,sizeof(buf));
         if(s > 0){
             buf[s-1] = 0;
             sendMsg(msgid, SERVER_TYPE, buf);
             printf("send done, wait...\n");
         }
     }

     destoryMsgQueue(msgid);      
     return 0;
 }   

运行后,可以看到实现了进程间消息的互相传输。
这里写图片描述
这里写图片描述
可以查看当前消息队列的信息,以及删除消息队列
这里写图片描述
共享内存和信号量会在接下来的博客中介绍到,谢谢关注哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值