linux学习笔记-读《Linux编程技术详解》(11)-POSIX IPC

来源:互联网 发布:智慧城市 云计算 编辑:程序博客网 时间:2024/06/08 04:47

使用信号传递的信息有限,使用管道虽然能够传输一定量的信息,但是只能传递无格式的字节流。为解决这些问题,引入了三种新的进程间通信(IPC)机制,分别是:消息队列、共享内存和信号量。在POSIX标准中,这些进程间通信机制被编入POSIXXSI中。

l  消息队列:消息队列指的是存放消息的队列。让让系统中其他进程访问共有的消息类型,必须通过一个唯一的标识,这个标识在消息队列中称为消息的Key。不同进程通过向消息队列中写入消息或读取消息来实现进程间的数据交换。

l  信号量:信号量用于多进程情况下的进程同步问题。信号量是一个含有整数值的资源,进程通过检测该整数值,来保证其它进程在某个时间不会进行类似的操作。

l  共享内存:共享内存通过建立一段允许其他进程访问的内存空间,实现资源和数据的共享。进程可以对共享内存空间进行读写操作。当多个进程同时进行写操作时,需要使用信号量控制对共有资源的访问。

POSIX IPC提供的这3IPC通信机制都只能用于本机进程之间的进程通信。要实现不同主机之间的进程通信,可以使用socket套接字或RPC等方式。

 

消息队列、信号量和共享内存都是IPC资源,而在使用IPC资源前,需要创建该资源。

Linux内核中,为了标识IPC资源,使用了一个非负整数的标识符,可通过它访问与标识符相关的IPC资源,如消息队列、信号量或共享内存。这一标识符被称为IPC标识符。

要创建IPC标识符,需要制定一个关键字。IPC关键字(ipc_perm结构体中的_key)可以通过调用ftok函数获得,参数pathname必须指向文件系统中存在的文件或目录。

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

 

基本IPC命令

查看内核中存储的IPC资源的命令:ipcs

从内核中删除IPC资源的命令:ipcrm

 

消息队列

消息队列实际上就是一个消息链表,而消息是链表中具有特定格式及优先级的记录。

           int msgget(key_t key, int msgflg);

msgget函数用于创建或访问消息队列,并获得消息队列标识符。

           int msgctl(int msqid, int cmd, struct msqid_ds *buf);

msgctl函数用于修改消息队列的权限和所有者属性。

 

           int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msgsnd 函数用于向消息队列中发送消息。如果消息队列已经达到了最大消息量,msgsnd函数将阻塞(参数msgflg没有使用IPC_NOWAIT情况下,如果使用了该参数,msgsnd将调用失败,同时,将errno设置为EAGAIN),直到消息队列中有足够的空间。

           ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgrcv函数从msgid代表的消息队列中读取一个消息,并把消息存储在msgp指向的缓冲结构中。该缓冲结构的定义类似于msgbuf结构体。

 

信号量

信号量用于控制多个进程对共享资源或数据的访问,使得在某个时刻,只有一个进程使用资源或访问数据。其他的进程如果希望使用该资源或访问数据,只有在资源可用时才能够实现。

临界区(Critical Section)是一段独占某些共享资源访问的代码。如果有多个线程试图同时访问临界区,那么在有一个线程进入后,其他所有试图访问此临界区的线程将被挂起,挂起操作将一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占。以此达到用原子方式操作(原子操作是指不可再细分的操作),实现对资源共享的目的。

合理的临界区问题解决方案要求公平和排他性的访问。任何试图进入临界区的进程都有机会进入临界区,而不应该处于无限期的等待中。如果临界区中没有进程,应该允许等待的进程进入。

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

semget函数用于创建信号量集,该函数返回与参数key相关的信号量集标识符。

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

semctl函数提供了对信号量集的控制操作。

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

POSIX IPC提供了semop函数对信号量集中的信号量进行操作,在单个信号量集上执行sops参数指定的操作。

 

共享内存

共享内存被认为是最快的IPC通信方式。与使用管道、消息队列等方式相比,共享内存方式不需要在内核空间与用户空间的数据之间进行复制操作,而是直接进行内存的读写。由于多个进程同时需要对共享内存空间进行操作,不需对共享内存空间的访问采用某种同步方式。常见的方式是使用信号量来确保在某个时刻只有一个进程访问共享内存。

           int shmget(key_t key, size_t size, int shmflg);

shmget函数用于创建新的共享内存段,或是对一个已经存在的共享内存段进行存取。

           int shmtl(int shmid, int cmd, struct shmid_ds *buf);

shmctl函数根据参数cmd给出的控制信息,对共享内存标识符为shmid的共享内存段进行控制。参数buf为指向shmid_ds结构体的指针。

shmat函数与shmdt函数用于实现对共享内存的操作。shmat函数用于将共享内存连接到指定的进程地址空间中。当共享内存连接到指定地址空间后,进程将获得指向该内存段的指针。然后,可以根据该指针实现对共享内存段的访问。完成对共享内存段的操作后,需要调用shmdt函数将共享内存段从进程的地址空间中分离。

           void *shmat(int shmid, const void *shmaddr, in shmflg);

shmat函数将指定共享内存标识符为shmid的共享内存段连接到进程的地址空间中。参数shmid为要连接的共享内存段标识符。shmaddr为连接的内存地。

           int shmdt(const void *shmaddr);

shmdt函数用于在完成对共享内存段操作后,实现共享内存段与进程空间的脱离。该函数并没有删除共享内存段。参数shaddr为调用shmat函数后获得的地址指针。

当存在多线程同时对共享内存进行访问时,必须使用信号量来进行进程间互斥。