进程间通信

7 篇文章 0 订阅
  • 进程间通信(Inter Process Communication,IPC)就是在不同进程之间传播或交换信息
  • 进程间通信至少可以通过传送、打开文件来实现
  • Linux几乎支持所有的UNIX下常用的进程间通信方法:管道、消息队列、共享内存、信号量、套接字等
  • 管道、消息队列、共享内存、信号量用于同一台机器上面进程间的通信,而套接字主要用于不同机器之间的网络通信
一、管道
  • 头文件:#include <unistd.h>
  • 父进程和子进程之间是通过管道进行通信的
  • 管道是一种两个进程之间进行单向通信的机制,因为管道传递数据的单向性,管道又被称为半双工管道
  • 管道的特点:
1.数据只能由一个进程流向另一个进程(一个进程读管道,一个进程写管道)。如果要实现双工通信,必须要有两个管道
2.管道只能用于父进程和子进程之间,或者兄弟进程之间通信,即管道只能用于具有亲缘关系的进程之间的通信
3.管道没有名字
4.管道的缓冲区大小是受限制的
5.管道所传输的是无格式的字节流,所以需要管道的输入方和输出方事先约定好数据格式
  • 使用管道进行通信,是通过创建管道的时候系统设置的文件描述符进行的
  • 从本质上说,管道也是一种文件,这个文件只存在于内存中
  • 写入管道的数据每次都添加到管道缓冲区的末尾,读数据的时候都是从管道缓冲区的头部读数据(队列操作)
  • 管道的函数原型:
//一个进程在由pipe函数创建了管道之后,再用fork函数创建一个子进程,然后通过管道实现父子进程之间的通信
//管道两边可以分别使用描述字fd[0]和fd[1]来描述,其中fd[0]的一端只能用于读,称为管道读端,而fd[1]描述的一端只能用于写,称为管道写端
//一般文件的I/O函数都可以用于管道,比如说close,read,write等
int pipe(int fd[2]);
//父子线程之间通信
//为了实现父子线程之间的通信,需要把无关的读端和写端关闭掉
//建立是 无名管道
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int main()
{
    int fd[2];
    pid_t pid;
    char buf[256];
    int returned_cnt;
    pip(fd);
    pid = fork();
    if(pid < 0){
        //输出错误信息
        exit(1);
    }
    else if(pid == 0){        //子进程是写端,即进行发送
        printf("In the child process...");
        close(fd[input]);
        write(fd[output],"Hello World.",strlen("Hello World."));
        exit(0);
    }
    else{                     //父进程是读端,即接收端
        printf("In the father process...");
        close(fd[output]);
        returned_cnt = read(fd[input],buf,sizeof(buf));
        printf("%d bytes of data received from child process: %s\n",returned_cnt,buf);
    }

    return 0;
}
  • 对无名管道进行改进得到有名管道,它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中
  • 有名管道也是半双工的
  • 有名管道的特点:
1.使互不相关的两个进程间实现通信
2.该管道可以通过路径名来指定,并且在文件系统中可见。 在建立了管道之后,两个进程可以把它当作普通文件一样进行读写操作
3.FIFO严格的遵循先进先出规则,对管道及FIFO的读操作总是从头端开始,而写操作总是从末尾开始
  • 有名管道的函数原型:
//头文件
#include <sys/types.h>
#include <sys/stat.h>
//参数:
// pathname:普通的路径名,也是FIFO创建之后的名字。如果路径名已经存在,则返回EEXIST错误,如果出现这个错误,直接调用打开fifo的函数
//mode:与打开普通文件的open()函数的mode参数相同
//返回值:
//返回小于0时表示调用失败
int mkfifo(const char *pathname,mode_t mode);
//注意,创建了管道之后还需要使用open函数来打开。并且只需要在一端执行mkfifo函数创建管道即可,另外一端只需要以正确的模式调用open函数打开管道
//access函数
//在Linux系统中,为access,头文件是<unistd.h>,在C++中,该函数为_access,头文件是<io.h>
//主要用来判断文件是否存在,判断文件是否可写
//参数和mkfifo一样
int _access(const char *pathname,int mode);
// execlp函数
execlp("rm","-f",P_FIFO,NULL);        //其中一个调用,表示删除名为P_FIFP的文件
  • 一个有名管道可以进行一对多的通信,但是存在问题。一般是每个客户端在给服务器发送信息之前或者之后都建立自己的读入管道。使用客户端的PID作为管道名
二、消息队列
  • 消息队列用于运行于同一台机器上的进程间通信。它和管道很相似,是一个在系统内核中用来保存消息的队列。
  • 它在系统内核中是以消息链表的形式出现。消息链表中节点的结构用msg声明
  • 使用msgget函数来创建新消息队列或取得已存在的消息队列:
//参数
//key:可以认为是一个端口号,也可以由函数ftok生成
//msgflg如果等于IPC_CREAT,如果没有该队列,则创建一个并返回新标识符,如果已存在则返回原标识符;如果等于IPC_EXCL,如果没有该队列就返回-1,则存在就返回0
int msgget(key_t key,int msgflg);
  • 向队列读写消息
//从消息队列中读取消息
//参数:
//msqid:消息队列的标志码
//magp:指向消息缓冲股的指针,用于暂存发送和接收的消息
//msgsz:消息的大小
//magtyp:从消息队列内读取的消息形态
//magflg:
ssize_t msgrcv(int msqid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
//将数据放到消息队列中
int msgsnd(int msqid,const void *msgp,size_t msgsz,int msgflg);
  • 使用函数msgctl设置消息队列属性:
//
int msgctl(int msgqid,int cmd,struct msqid_ds *buf);
  • P359
  • 注意:通信的双方都必须建立消息队列
  • 注意二:程序结束时必须要删除消息队列。一般在接收方接收到结束标识之后,由接收方进行删除。
  • 消息队列有名管道的相似之处:
1.消息队列进行通信的进程可以是不相关的进程
2.它们都是通过发送和接收的方式来传递数据的。在命名管道中,发送数据用 write函数,接收数据用 read函数。在消息函数中,发送数据用 msgsnd函数,而接收数据用 msgrcv函数
  • 相比于命名管道,消息队列的优势:
1.消息队列可以独立于发送和接收进程而存在
2.
3.接收程序可以通过消息类型有选择的接收数据
三、共享内存
  • 共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方法
  • 将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址
  • 共享内存并未提供同步机制。即第一个进程对共享内存的的写操作结束之前,并无自动机制可以阻止第二个进程对它进行读取
  • 使用shmget函数创建共享内存:
//
#include <sys/shm.h>
//参数
//key:为共享内存段命名
//size:以字节单位指定需要共享的内存容量
//shmflg:权限标识,与open函数的mode参数一样,使用IPC_CREAT,则共享内存已经存在就只是来取得和已存在的共享内存的连接
//返回值: 程序先通过调用shmget函数并提供一个键(key),再由系统生成一个相应的共享内存标识符(shmget的返回值)
//调用失败返回-1
int shmget(key_t key,int size,int flag);
  • 使用shmat函数将其他进程创建的内存连接(映射)到自身的地址空间中:
//参数
//shmid:shmget函数返回的共享储存标识符
//addr:
//flag:addr和flag参数决定了以什么方式来确定连接的地址
//返回值:
//该进程数据段所连接的实际地址
void shmat(int shmid,void *addr,int flag);
  • 使用shmdt函数将共享内存从当前进程中分离:
//参数
//shmaddr:shmat函数返回的地址指针
//返回值
//调用成功返回0,失败返回-1
int shmdt(const void *shmaddr);
  • 使用shmctl函数设置共享内存的属性:
//主要是使用shmctl来删除共享内存,shmdt只是将共享内存删除掉了
shmctl(shmid,IPC_RMID,0);
  • 共享内存的优缺点:
优点:共享内存进行进程间通信非常方便,函数接口也简单,直接访问内存的方式效率也非常高。
缺点:没有提供同步机制
四、信号量
  • 多线程同步中使用的信号量是POSIX信号量,而用于进程通信的信号量是SYSTEM V信号量。这两种都是用户态进程可以使用的信号量
  • 使用函数semget来创建并打开信号量:
//
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
//返回值:
//函数执行成功则返回信号量标识符,失败则返回-1
//参数:
//key:函数通过调用 ftok函数得到的键值
//nsems:代表创建信号量的个数, 注意是信号量的个数而不是信号量的初始值,和线程的信号量是不同的,如果只是访问而不是创建那么指定该参数为0.一旦创建就不可修改
//semflg:指定该信号量的读写权限
int semget(key_t key,int nsems,int semflg);
  • 使用semop函数来改变信号量的值
  • 使用semctl函数来直接控制信号量信息,使用这个函数来初始化信号量的值
semid = semget(SEM_KEY,2,IPC_CREAT|0666);    //创建了一个semid,并且具有两个信号量
union semun semun1;
semun1.val = 0;
semctl(semid,0,SETVAL, semun1);        //把第一个信号量的初始值设置为0
  • P370 信号量例程,注意这里可以一个读取多个写入
五、ipcs命令
  • ipcs是一个UNIX/Linux命令,用于报告系统的消息队列、信号量、共享内存等
  • ipcs -a:列出本用户相关的ipcs参数
  • ipcs -q:列出进程中的消息队列
  • ipcs -s:列出所有的信号量
  • ipcs -m:列出所有的共享内存信息
  • ipcs -l:列出系统的限额
  • ipcs -t:列出最后的访问时间
  • ipcs -u:列出当前的使用情况
















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值