管道,消息队列,共享内存,信号量

1、管道

管道是一种两个进程点进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道,管道的这一特点决定了其使用的局限性

管道具有以下特点:

(1)数据只能由一个进程流向另一个进程(其中一个读管道,另一个写管道);如果要进行双工通信,则需要建立两个管道

(2)管道只能用于父子进程或者兄弟进程间的通信,也就是说管道只能用于具有亲缘关系的进程间通信

管道的不足之处:

(1)管道没有名字

(2)管道的缓冲区是受限制的

(3)管道所传输的是无格式的字节流数据,这就需要双方事先约定好数据格式

管道的函数原型

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

以上是无名管道,还有一种管道叫做有名管道

有名管道区别于无名管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存在于文件系统中。这样

即使与FIFO的创建的进程不存在亲缘关系的进程,只要访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间)

因此通过FIFO不相关的进程也能交换数据

有名管道具有以下的特点:

(1)它可以使互不相关的两个进程间实现彼此通信

(2)管道可以通过路径名来指出并且在文件系统是可见的

(3)FIFO严格的遵循着先进先出规则,对管道及FIFO的读操作总是从开始处返回操作,对他们的写操作则是把数据添加到尾部

有名管道的函数原型

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

int mkfifo(const char *pathname,mode_t mode)

第一个参数为路径名,创建后的FIFO的名字,第二个参数跟open()函数的Mode相同

2、消息队列

消息队列用于运行在同一台及其上的进程间通信,他和管道十分类似,都是一个系统内核中用于保存消息的队列,它在系统内核中

是以消息链表的形式出现。消息链表的结构用msg声明

相关的函数有一下几个:

(1)创建新消息队列或去的已存在的消息队列,函数模型是:

int msgget(key_t key,int msgflg)

参数描述:

  (1)Key可以认为是一个端口号,也可以由函数ftok生成

  (2)msgflg如果等于IPC_CREATE,若没有该队列, 则创建一个并返回 新标识符,若已存在则返回原标识符

msgflg如果等于IPC_EXCL,若没有该队列,则返回-1,若已存在,则返回0

(2)向队列读/写消息

取消息
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);

参数中msqid不用说应该都知道是消息队列的表示码,

*msgp则是指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,但是一般定义如下

struct msgstru{
   long mtype;
   char mtext[512];  
}

msgsz则是指消息的大小

msgtyp是指从消息队列内读取的消息状态。如果为0则代表消息队列内的所以消息都会被读取

msgflg:用来指明核心程序在队列没有数据的情况下所应采取的行动,如果msgflg和常数IPC_NOWAIT合用,则在msgsnd()执行时,若是

消息队列已满,则msgsnd()将不会阻塞,而会立即返回-1,如果执行的是msgrcv(),则在消息队列为空时,不做等待立刻返回-1,并设定错误码为ENOMSG

当msgflg为0时,msgsnd()和msgrcv()在队列为空或满的情景是,采取阻塞等待的处理模式

设置消息队列的函数

int msgctl(int msgqid,int cmd,struct msqid_ds *buf)

参数中msgctl系统调用对msgqid标识的消息队列执行cmd操作,系统中定义了3中cmd操作

(1)IPC_STAT:用来获取消息队列对应的msqid_ds数据结构,并将其保存到Buf指定的地址空间

(2)IPC_SET:用来设置消息队列的属性,要设置的属性存储在buf中

(3)IPC_RMID:从内核中删除msqid标识的消息队列

消息队列相比命名管道的优势在于:

(1)消息队列也可以独立于发送和接收进程二存在,从而消除了在同步命名管道的打开和关闭时可能产生的困难

(2)可以同时通过发送消息以避免命名管道的同步和阻塞问题,而不需要由进程自己来提供同步方法

(3)接收程序可以通过消息类型有选择的接收数据,而不是像命名管道中那样,只能默认的接收

3、共享内存

共享内存就是允许两个不想管的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的

一种非常有效的方式。不同进程之间共享的内存通常安排在同一段物理内存中。进程可以将同一段共享内存连到他们自己的地址空间中

所有进程都可以访问共享内存中的地址。

如果某个进程向共享内存写入数据,这样做的改动也会影响到其他访问这段共享内存的任何其他进程

并且共享内存并没有提供同步机制,也就是说,在第一个进程对共享内存的写操作结束之前,并无自动机制阻止第二个进程对其进行读取

共享内存的函数原型

#include<sys.shm.h>

int shmget(key_t key,int size,int flag)

第一个参数key是用来有效的为共享内存段命名,shmget函数运行成功时会返回一个为Key相关的共享内存标识符,用于后续的共存内存函数

失败则会返回-1

不想管的进程可以通过该函数的返回值访问同意共享内存,他代表程序可能要使用的某个资源,程序对所有的共享内存的访问都是间接的

程序先通过shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符

第二个参数,size以字节为单位制定需要共享的内存容量

第三个参数,shmflag是权限标志,它的作用于Open函数的mode一样,如果想在Key标识的共享内存不存在的条件下,创建他的话

可以与IPC_CRATE做或操作。

共享内存的全线标志与文件的读写权限一样

举个栗子:0644表示允许一个进程创建的供需昂内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存

当共享内存创建后,其余进程可以调用shmat将其连接到自身的地址空间中,他的函数原型是

void *shmat(int shmid,void *addr,int flag);

(1)shmid为shmget函数返回的共享内存标识符,addr和flag参数决定了以什么方式来确定连接的地址,函数的返回值即是

该进程数据所连接的实际地址,其他进程可以对此进程进行读写操作

shmdt是用来将共享内存从当前进程中分离

int shmdt(const void *shmaddr);

参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1

共享内存的优缺点

优点:使用共享内存进行进程间的通信非常方便,而且函数的接口也简单,数据的共享还使进程间的数据不用传达,而是直接访问内存,

这也加快了程序的效率。同时,他也不像无名管道那样子要求通信的进程有一定的父子或兄弟关系

缺点:共享内存没有提供同步的机制,这就使得使用共享内存进行进程间通信的时候往往需要借助其他的手段来进行同步工作

4、信号量

Semge函数原型

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

int semget(key_t key,int nsems,int semflg);

参数描述:

(1)key是函数通过调用ftok函数得到的键值

(2)nsems代表创建信号量的个数,如果只是访问而不创建的话则可以指定该参数为0

(3)semflg指定该信号量的读写权限

当创建信号量时不加IPC_CREATE,若指定IPC_CREAT|IPC_EXCL后创建时发现存在该信号量,创建失败

semget函数成功时返回信号量标识符,失败时返回-1

semop函数,用于改变信号量的值

int semop(int semid,struct sembuf *sops,unsigned nsops)

semid是由semget返回的信号量标识符,sembuf结构体定义如下

struct sembuf {
    short sem_num;//除非使用一组信号量,否则为0
    short sem_op;//信号量在一次操作中需要改变的数据,
                 //通常为两个数,一次为-1(P,等待操作),一次为+1(发送信号操作)
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号
                  //并在进程没有释放该信号量而终止时,操作系统释放信号量
}

semctl桉树,用来直接控制信号量信息

int semctl(int semid,int semnum,int cmd,……)

如果有第四个参数,它通常为union semum结构,定义如下

union semun{
    int val;
    struct semid_ds *buf
    unsigned short *arry;
}

前两个参数与前一个参数一样,cmd通常是SETCAL或IPC_RMID.

SETVAL用来把信号量初始化为一个已知的值。p值通过union semun中的val成员设置

其作用是在信号量第一次使用前对他设置。IPC_RMID用来删除一个已经无须继续使用的信号量标识符

原文地址:https://www.cnblogs.com/tijie/p/10582869.html