IPC(Inter-Process Communication)进程间通信总结

141 篇文章 6 订阅
109 篇文章 12 订阅

-------根据网络视频整理

1、进程间通信分为:

1.1同一台主机上的进程间通信;

1)管道:

  • 匿名管道
  • 命名管道

2)XSI ---->SysV

  • Message Queues//消息队列
  • Semaphore array//信号量数组
  • Shared Memory //共享内存

1.2不同主机上的进程间通信;

  • 网络套接字socket

2、各种进程间通信方式

2.1管道

是内核提供的,单工(一端为读端,一端为写端),具有自同步机制(迁就慢的一方,读或写要去等慢的那一方有写的内容或者读的那一方)。

1)匿名管道

磁盘上看不到匿名管道名(即ls 是看不到的),若两个进程没有血缘关系,是找不到同一个匿名管道来通信的,即只有有血缘关系的两个进程才能用匿名管道通信;

pipe()函数

#include <unistd.h>
int pipe(int pipefd[2]);

参数:会回填两个文件描述符

pipefd[0]  作为读端

pipefd[1] 作为写端

成功返回0,失败返回-1,设置errno.

因为是应用在有血缘关系的两个进程间通信;所以可以父写子读,或者父读子写。

下例:父写子读,故父的读fd关上;子的写fd关上。

先pipe,后fork,因为fork之后便创建了子进程,拿到了父进程中的pipe中的两个文件描述符。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFSIZE 1024
int main()
{
    int pd[2];
    pid_t pid;
    int  len;
    char buf[BUFSIZE];
    if(pipe(pd)<))
    {    
        perror("pipe()");
        exit(1);
    };
    pid = fork();
    if(pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if(pid == 0)//子进程
    {
        close(pd[1]);//子进程的写端不用,先关闭
        len = read(pd[0],buf,BUFSIZE);
        write(1,buf,len);
        close(pd[0]);//子进程的读端读完后关闭
        exit(0);
    }
    else{//父进程
        close(pd[0]);//父进程的读端不用,先关闭
        write(pd[1],"hello!",6);
        close(pd[1]);//父进程的写端写完后关闭
        wait(NULL);//收尸
        exit(0);

    }

    //exit(0);
}

实质:是将文件打开,给了一个fd或者FILE*,

2)命名管道

文件名为p的文件---通过ls能看到的(是命名管道,匿名管道是看不到的)

不具有血缘关系的两个进程也能通信,管道其实就是当前存在的一个文件。

mkfifo()

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

int mkfifo(const char *pathname,mode_t mode);

 参数:

pathname:创建的命令管道的名称;

mode:管道权限

返回值:

成功返回0;

失败返回-1,设置errno.

2.2XSI--->SysV  (system 5)

三种SysV的实现都以如下步骤:

  • ftok        //有血缘关系的进程,此函数可以不调用,只要获取匿名的IPC即可。形如  shmid = shmget(IPC_PRIVATE,MEMSIZE,0600);
  • xxget
  • xxop   //例如:直接man   msgop  就直接能找到  msgrcv 和msgsend
  • xxctl

通过ipcs(ipc show)命令查看有以下类型:

 既可以用在有血缘关系也可用在没血缘关系的进程间通信,有血缘关系的好办,fork之后就拿到了。

没有血缘关系的进程间是通过上图中的key值。

key:  使用ftok();函数 (f  to  key)产生同一个key ,然后以下三种机制都对通过key值产生id号,msqid,semid,shmid。

即:同一个key值创建实例,产生同一个id,通信双方就可以通过该id通信。

key_t ftok(const char *pathname,int proj_id);

 

以下三中机制用到的函数都是形如:

XXXget -----创建

XXXop ------操作,如读写

XXXctl -------控制,如,销毁

将XXX替换成msg  sem shm 可以通过man手册查看。

2.2.1Message Queues//消息队列

消息队列为双工的。

-----msgget():

 key为使用ftok获得的值。

返回值成功为ID号。失败为-1,并设置errno.

使用man   msgop可以查看如下megop分为如下两个操作。

------megrcv

int msgrcv(int msgid, void *msgp, size_t msg_sz, long msgtyp, int msgflg);

 参数: msgid创建的id

             msgp,收到的内容存放的首地址,

             msgsz,收到的内容的大小

             msgtyp:是否要挑消息来收,即包的编号。

             msgflg:特殊要求,比如nowiat,没有消息立即返回。

 msgp指向的是如上的msgbuf大概的结构体格式,即必须得有long mtype(>0); 

char mtext[1];则是说是变参的,并不是说数组中只有一个数据。,并不是说只能是一个数组,可以是任意类型的数据,例如,可以如下方式,

也就是,结构体名可以变,结构体中除了mtype必须有,其他的都可以任意更改。

返回值:成功:返回接收到的真正的字节个数

失败,返回-1,设置errno.

------megsnd

int msgsend(int msgid, const void *msgp, size_t msgsz, int msgflg);

参数:

msgid :id

msgp:待发送的数据

msgsz:数据长度

msgflg:特殊要求

-------msgctl

cmd有很多,根据cmd的不同,buf是否需要传参

//协议proto.h
#ifndef PROTO_H__
#defifin PROTO_H__

#define KEYPATH "/etc/services"   //任意找的一个文件
#define KEYPROJ 'g'    //保证一定是个整型,因为是ascii码值
#define NAMESIZE 32
struct msg_st
{
    long mtype;//这是格式要求的类型。
    char name[NAMESIZE];
    int math;
    int chinese;
};

#endif

 //发包的一方一定是主动端

//发送进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h?
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>

#include "proto.h"
int main()
{
    key_t key;
    struct msg_st sbuf;
    int msgid;
    key = ftok(KEYPATH,KEYPROJ);
    if(key<0)
    {
        perror("ftok()");
        exit(0);
    }
    msgid = msgget(key,0);//后运行一方不需要再CREAT了。

    if(msgid<0)
    {
        perror("msgget");
        exit(0);
    }
    sbuf.mtype = 1;
    strcpy(sbuf.name,"Alan");
    sbuf.math = rand()%100;
    sbuf.chinese = rand()%100;
    if(msgsnd(msgid ,&sbuf,sizeof(sbuf)-sizeof(long),0)<0)
    {
        perror("msgsnd()");
        exit(1);
    }
    //msgctl();  //此处不需要,因为并没有创建,所以并不用销毁。
    puts("ok");
    exit(0);
}

 //被动端一定是先收包的一方,动端一定是先运行的一方。故,接收方一定要有IPC_CREAT权限。

send方可以有IPC_CREAT也可以没有。

 注意:msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0);的写法,大小是实际所传数据的大小,故要减去一个long的大小。

//接收方进程
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include "proto.h"

int main()
{
    key_t key;
    msg_t msgid;
    struct msg_st rbuf;
    key = ftok(KEYPATH,KEYPROJ);
    if(key<0)
    {
        perror("ftok()");
        exit(0);
    }
    msgid = msgget(key,IPC_CREAT|0600);//只要有IPC_CREAT,就一定有后面的权限,权限可以根据幻定义
    if(msgid<0)
    {
        perror("msgget");
        exit(0);
    }
    while(1)
    {
        if( msgrcv(msgid,&rbuf,sizeof(rbuf)-sizeof(long),0,0)<0)
        {
            perror("msgrcv");
            exit(0);
        }
        printf("NAME= %s\n",rbuf.name);
        printf("MATH=%s\n",rbuf.math);
        printf("CHINESE=%s\n",rbuf.chinese);
    
    }
   
    msgctl(msgid,IPC_RMID,NULL);//销毁实例,故三参为NULL
    exit(0);
}

运行该接收进程后,

在终端中查看命令 ipcs发现:会看到key值,和id,以及权限perms 600

使用ipcrm -q  0 //可以kill掉上面的消息队列。

2.2.2Semaphore array//信号量数组

//创建
int semid;
semid = semget(IPC_PRIVATE,1,0600);//父子进程间通信,可直接使用IPC_PRIVATE,使用IPC_PRIVATE表示创建,必须有权限,如0600
if(semid<0)
{
    perror("semget()");
    exit(1);
}

//初始化
if(semctl(semid,0,SETVAL,1)<0)
{
  perror("semctl()");
    exit(1);
}

//销毁
semctl(semid,0,IPC_RMID);

 

2.2.3Shared Memory//共享内存 ,这个与 mmap函数实现的功能类似

key 为同一个key值,返回值int 即为id

shmget()

shmop();

shmctl();

 shmat---->把共享内存映射过来。

   shmid:id

   shmaddr:映射到当前的哪块地址上,一般置空,因为当我们不确定哪块内存地址可用的时候,值为NULL,就会自动给我们找到当前进程空间可以使用的内存地址。

    shmflg:操作权限。无特殊要求的话,一般设置为0

返回值:

    成功:真正映射过来的地址位置。

    失败:返回 (void*)-1,设置errno

shmdt----->把共享内存解除映射。delet

//实现子进程读,父进程写的功能

注意:只有在父进程中销毁实例shmctl(),子进程中没有.因为是谁创建谁销毁,谁打开,谁释放。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
#define  MEMSIZE 1024

int main()
{
    //ftok();//有血缘关系的进程,我们不关心key值,可以不用,使用匿名IPC。即shmget中使用IPC_PRIVATE

    char *ptr = NULL;
    shmid = shmget(IPC_PRIVATE,MEMSIZE,0600);
    if(shmid<0)
    {
        perror("shmget()");
        exit(1);
    }
    pid = fork();
    if(pid<0)
    {
        perror("fork()");
        exit(1);
    }
    if(pid == 0) //子进程
    {
        ptr = shmat(shmid,NULL0);
        if(ptr = (void*)-1)
        {
              perror("shmat()");
              exit(1);
        }
        strcpy(ptr,"hello");
        shmdt(ptr);//子进程解除映射
        exit(0);
            
    }
    else  //父进程
    {
        wait(NULL);//收尸,因为没有用到阻塞的系统调用,放在这能确保子进程写完。
        ptr = shmat(shmid,NULL,0);
        if(ptr== (void*)-1)
        {
            perror("shmat");
            exit(1);
        }
        puts(ptr);
        shmdt(ptr);
        shmctl(shmid,IPC_RMID,NULL);//因为是销毁,故第3个参数为NULL
        exit(0);
    }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值