Linux环境下C程序设计-第八章-进程间通信_第1页
Linux环境下C程序设计-第八章-进程间通信_第2页
Linux环境下C程序设计-第八章-进程间通信_第3页
Linux环境下C程序设计-第八章-进程间通信_第4页
Linux环境下C程序设计-第八章-进程间通信_第5页
已阅读5页,还剩80页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第八章进程间通信

本章首先介绍Linux下进程间通信的相关内容;然后介绍

Linux平台下进程间通信相互通信的方法,包括共享内存、信号量、管道通信、命名管道、消息队列;最后结合具

体的项目案例,阐述进程间通信的相关操作的具体应用。本节学习目标:√共享内存√信号量√管道通信√命名管道√消息队列15:51/2678.1进程间通信概述

Linux系统是多任务多进程的操作系统,对于大型的应用系统而言,通常需要多个任务多个进程相互协作共同完成,而进程的地址空间又是各自独立的,所以需要进程间进行通信。进程间的通信(Internet

ProcessConnection简称IPC)是指多个进程间相互协调,进行信息交换,相互交流的方法。Linux下进程间进行相互通信的方法包括共享内存、信号量、管道、命名管道、消息队列等。下面对Linux下的进程间的通信方法做个简单介绍。

共享内存:共享内存是指由一个进程创建并且能够被其它进程访问的内存段。共享内存是进程间通信方式最快的IPC方式,经常与信号量等通信机制配合使用,通信速度最快、运行效率最高。15:5in2/2678.1进程间通信概述15:53/267

信号量:信号量在形式上是一个计数器,主要用来协调进程间或同一个进程内不同线程间同时访问共享资源的问题。所以,信号量也称为一种信号锁,保证同一时刻只有一个进程或线程访问共享资源,其余进程或线程无法访问该共享资源;信号量也是一种同步机制,同一时刻可以有多个进程访问共享资源。

管道:管道在形式上是一种文件,主要用在具有亲缘关系的进程间的通信。管道是一种半双工的通信方式,它的显著特点是管道有读入端和写入端,数据只能单向流动。和共享内存相比,管道通信相对慢些,但是它用起来方便很多,系统开销也小。8.1进程间通信概述15:54/267

命名管道:命名管道是管道中的一种,具有管道的所有功能和特点。命名管道是一个设备文件,不仅可用在具有亲缘关系的进程间的通信,而且也可用在不具有亲缘关系的进程间。命名管道要求提供一个路径名与它关联的文件,只要可以访问这个路径文件的进程,都可以进程间的相互通信。

消息队列:消息队列是存放在操作系统内核中的消息链表,每个消息队列由消息队列标识符标示。消息队列存放在内核中,所以只有重启操作系统或主动删除一个消息队列,消息队列才会消除。消息队列具有传递信息量大、可以承载各种格式的字节流和可以动态设置缓冲区大小的优点。8.2共享内存15:55/267

在Linux系统中,共享内存是由一个进程创建并且能够被其它进程访问的内存段。每个进程所创建的共享内存在操作系统内核中维护着一个与之相应的数据结构

shmid_ds,它的具体定义如下所示:8.2共享内存15:56/2678.2共享内存15:57/267•••••••

这个结构详细描述了共享内存的各种属性,它们的具体含义如下所示:shm_perm:表示共享内存的用户ID、组ID等信息;shm_segsz:以字节为单位表示共享内存的大小;shm_lkcnt:表示共享内存段锁定的时间大小;shm_cpid:表示创建共享内存的进程ID;shm_lpid:表示最后一次操作共享内存的进程ID;shm_nattch:表示当前使用共享内存的进程数;shm_atime:表示最后一次附加共享内存的时间;8.2共享内存15:58/267shm_dtime:表示最后一次分离共享内存的时间;shm_ctime:表示最后一次修改共享内存的时间;下面给出基于上述共享内存数据结构的函数调用。1共享内存的创建15:59/267

创建共享内存的函数是shmget。系统通过调用它在程序中完成共享内存的创建工作。在Linux系统终端中使用帮助命令“man

shmget”,得到共享内存的创建函数信息如下:#include

<sys/ipc.h>#include

<sys/shm.h>int

shmget(key_t

key,

size_t

size,

int

shmflg);1共享内存的创建15:510/267

shmget:该函数的返回类型为整型,用于创建进程的共享内存。key参数表示由ftok生成的共享内存键。size参数表示共享内存的大小,若是新创建一个共享内存则size须大于0;若是访问已经存在的共享内存则size为0。

shmflg参数表示共享内存的操作标志位,用于设置共享内存的访问权限,若shmflg参数取值为IPC_CREATE,则表示系统将参数key与其它的共享内存key进行比较,如果相同则返回已经存在的共享内存区的标识符,如果不同则新建一个共享内存区并返回其标识符;若shmflg参数取值为IPC_EXCL,则表示无意义;若shmflg参数取值为IPC_CREATE

|

IPC_EXCL,表示如果发现信号集已经存在,则返回-1。1共享内存的创建15:511/267

函数shmget调用成功返回共享内存的引用标示符,同时该共享内存的shmid_ds结构被初始化;否则调用失败返回-1。2共享内存的附加15:512/267

共享内存的附加函数是shmat。系统通过调用它在程序中完成共享内存的附加工作。在Linux系统终端中使用帮助命令“man

shmat”,得到共享内存的附加函数信息如

下:#include

<sys/types.h>#include

<sys/shm.h>void

*shmat(int

shmid,

const

void

*shmaddr,

int

shmflg);2共享内存的附加15:513/267

shmat:该函数的返回类型为指针,指向共享内存的地址,用于附加进程的共享内存。shmid参数表示附加的共享内

存的引用标示符。shmflg参数表示共享内存的读写操作方式。shmaddr参数表示共享内存的附加地址空间,若

shmaddr参数取值为空,则表示由内核选择一个空闲的内存区;若shmaddr参数取值为非空,并且shmflg参数指定为SHM_RND值则附加地址为共享内存的低端边界地址后的地址,否则附加地址为shmaddr指定的地址。通常shmaddr参数设置为NULL。

函数shmat调用成功返回共享内存的附加地址;否则调用失败返回-1。3共享内存的分离15:514/267

共享内存的分离函数是shmdt。系统通过调用它在程序中完成共享内存的分离工作。在Linux系统终端中使用帮助命令“man

shmdt”,得到共享内存的分离函数信息如

下:#include

<sys/types.h>#include

<sys/shm.h>int

shmdt(const

void

*shmaddr);3共享内存的分离15:515/267

shmdt:该函数的返回类型为整型,用于分离进程的共享内存。shmaddr参数为函数shmat的返回值。进程与共享内存分离后,shmid_ds中的shm_nattch会自动减1。若

shm_nattch的值减为0后,表示没有任何进程使用此共享内存,该共享内存将被系统删除。函数shmdt调用成功返回0;否则调用失败返回-1。4共享内存的控制15:516/267

共享内存的控制函数是shmctl。系统通过调用它在程序中完成共享内存的控制工作。在Linux系统终端中使用帮助命令“man

shmctl”,得到共享内存的控制函数信息如

下:#include

<sys/ipc.h>#include

<sys/shm.h>int

shmctl(int

shmid,

int

cmd,

struct

shmid_ds

*buf);4共享内存的控制15:517/267

shmctl:该函数的返回类型为整型,用于对共享内存的控制。shmid参数表示附加的共享内存的引用标示符。buf参数为指向shmid_ds结构体的指针。cmd参数为操作标志位,若cmd参数取值为IPC_RMID则表示删除由shmid标示的共享内存区;若cmd参数取值为IPC_SET则表示设置共享内存区shmid_ds结构;cmd参数取值为IPC_STAT则表示将共享内存区shmid_ds结构存储到buf指向的地址中。函数shmdt调用成功返回0;否则调用失败返回-1。5综合示例15:518/267

本节最后,通过利用共享内存的相关函数进行系统调用,来演示如何创建和使用共享内存,加强对进程通信的理解。共享内存的读函数

my_shmget_reader如范例8-1a所示,共享内存的写函数my_shmget_writer如范例8-1b所示。例8-1a

my_shmget_reader.c/****my_shmget_reader.c***/例8-1b

my_shmget_writer.c/****

my_shmget_writer.c***/在VIM编辑器中具体阐述代码在eclipse集成开发环境IDE中编译、调试、运行代码8.3信号量15:519/267

在Linux系统中,信号量实质是整数计数器,常用于处理进程或线程的对共享资源的同步和互斥问题。同步共享资源要求同一时刻允许多个进程或线程访问该资源;互斥共享资源要求同一时刻只允许一个进程或线程访问该资源。这里的资源可以是某种硬件资源、一段代码或一个变量等。当信号量的值大于或等于0时,表示并发进程或线程可使用的资源实体数;信号量小于0表示正在等待使用共享资源的进程数。8.3信号量

每个进程所创建的信号量在操作系统内核中维护着一个与之相应的数据结构semid_ds实例,它的具体定义如下所示:15:520/2678.3信号量15:521/267

每个进程所创建的信号量在操作系统内核中维护着一个与之相应的数据结构semid_ds实例,它的具体定义如下:struct

semid_ds

{

struct

ipc_perm

sem_perm;

/*

Ownership

andpermissions

*/*sem_base;

/*

Pointer

of

first

sem

*/sem_otime;

/*

Last

semop

time

*/sem_ctime;

/*

Last

change

time

*/struct

semtime_ttime_t•unsigned

short

sem_nsems;

/*

No.

of

semaphoresin

set

*/};8.3信号量15:522/267

这个结构详细描述了信号集的各种属性,它们的具体含义如下所示:sem_perm:表示信号集的用户ID、组ID、权限等信息;

sem_base:表示信号量的基地址,指向信号集中第一个信号量的地址;sem_otime:表示最后一次调用semop函数的时间;sem_ctime:表示最后一次改变该信号集的时间;sem_nsems:表示信号集中信号量的个数;8.3信号量15:523/267

这个结构详细描述了信号集中信号量的各种属性,它们的具体含义如下所示:struct

sem{ushort

semval;pid_t

sempid;ushort

semncnt;ushort

semzcnt;};8.3信号量15:524/267

这个struct

sem结构详细描述了它的各种属性,它们的具体含义如下所示:semval:表示信号量的值;sempid:表示最近一次访问共享资源的进程ID号;semncnt:表示等待利用资源的进程数;semzcnt:表示全部资源被独占的进程数;下面给出基于上述信号集和信号量数据结构的函数调用。1信号集的创建15:525/267

信号集的创建函数是semget。系统通过调用它在程序中完成信号集的创建工作。在Linux系统终端中使用帮助命令“man

semget”,得到信号集的创建函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/sem.h>int

semget(key_t

key,

int

nsems,

int

semflg);1信号集的创建15:526/267

semget:该函数的返回类型为整型,用于创建或打开一个信号集。key参数表示由ftok生成的信号集键。nsems参数表示创建信号集中信号量的个数,若是新创建一个信号集则nsems须大于0;若是访问已经存在的信号集则

nsems为0。semflg参数表示信号集的操作标志位,用于设置信号集的访问权限,若semflg参数取值为

IPC_CREATE,则表示系统将参数key与其它的信号集key进行比较,如果相同则返回已经存在的信号集标识符,如果不同则新建一个信号集并返回其标识符;若semflg

参数取值为IPC_EXCL,则表示无意义;若semflg参数取

值为IPC_CREATE

|

IPC_EXCL,表示如果发现信号集已经存在,则返回-1。1

ftok函数深度解析15:527/267

关于ftok函数,先不去了解它的作用来先说说为什么要用它,共享内存,消息队列,信号量它们三个都是找一个

中间介质,来进行通信的,这种介质多的是。就是怎么

区分出来,就像唯一一个身份证来区分人一样。你随便

来一个就行,就是因为这。只要唯一就行,就想起来了

文件的设备编号和节点,它是唯一的,但是直接用它来

作识别好像不太好,不过可以用它来产生一个号。ftok()就出场了。ftok函数具体形式如下:1

ftok函数深度解析15:528/267key_t

ftok(const

char

*pathname,

int

proj_id);

其中参数fname是指定的文件名,这个文件必须是存在的而且可以访问的。id是子序号,它是一个8bit的整数。即范围是0~255。当函数执行成功,则会返回key_t键值,

否则返回-1。在一般的UNIX中,通常是将文件的索引节

点取出,然后在前面加上子序号就得到key_t的值。有关该函数的三个常见问题:

1.pathname是目录还是文件的具体路径,是否可以随便设置2.pathname指定的目录或文件的权限是否有要求

3.proj_id是否可以随便设定,有什么限制条件1

ftok函数深度解析15:529/267解答:

1、ftok根据路径名,提取文件信息,再根据这些文件信息及project

ID合成key,该路径可以随便设置。

2、该路径是必须存在的,ftok只是根据文件inode在系统内的唯一性来取一个数值,和文件的权限无关。

3、proj_id是可以根据自己的约定,随意设置。这个数字,有的称之为project

ID;在UNIX系统上,它的取值是1到255;ftok()函数深度解析

/u013485792/article/details/5076422

41

ftok函数深度解析15:530/2671

ftok函数深度解析15:531/2671信号集的创建15:532/267

函数调用成功返回信号集的引用标示符,同时该共享内存的shmid_ds结构被初始化;否则调用失败返回-1。2信号集的操作15:533/267

信号量的值和资源使用情况有关系,当信号量的值大于或等于0时,表示并发进程或线程可使用的资源实体数;信号量小于0表示正在等待使用共享资源的进程数。信号量值的改变通过在PV操作中调用信号量的操作函数

semop,来实现信号量的改变。2信号集的操作15:534/267

信号量的操作函数是semop。系统通过调用它在程序中完成信号量的操作工作。在Linux系统终端中使用帮助命令“man

semop”,得到信号量的操作函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/sem.h>

int

semop(int

semid,

struct

sembuf

*sops,

unsignednsops);2信号集的操作15:535/267sem_op;

/*

semaphore

operation

*/sem_flg;

/*

operation

flags

*/

semop:该函数的返回类型为整型,用于创建或打开一个信号集。semid参数表示信号集的标识符。nsops参数表示将要进行操作的信号个数。sops参数表示指向所要操作结构体数组的首地址。每个sembuf结构体对应一个信号的操作,该结构体的数据结构如下所示:struct

sembuf{unsigned

short

sem_num;

/*

semaphore

number

*/shortshort}2信号集的操作15:536/267

若sem_op大于0,信号加上sem_op的值,进程释放资源;若sem_op等于0,若没设置IPC_NOWAIT则调用进程进入睡眠状态,直到信号值为0,否则不睡眠,直接返回EAGAIN;若sem_op小于0,信号加上sem_op的值,若

没设置IPC_NOWAIT则调用进程进入阻塞状态,直到资源可用为止,否则直接返回EAGAIN。函数调用成功返回0;否则调用失败返回-1。3信号集的控制15:537/267

信号集的控制函数是semctl。系统通过调用它在程序中完成信号集的控制工作。在Linux系统终端中使用帮助命令

“man

semctl”,得到信号集的控制函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/sem.h>

int

semctl(int

semid,

int

semnum,

int

cmd,

union

semunarg);3信号集的控制15:538/267

semctl:该函数的返回类型为整型,用于控制信号集中特定的信号量。semid参数表示信号集的标识符。semnum参数标示一个特定的信号量。

cmd参数表示希望执行的操作,若cmd参数取值为

IPC_STAT,表示返回当前的semid_ds结构体;若cmd参数取值为IPC_SET,表示对信号集的属性进行设置;若cmd参数取值为IPC_RMID,表示删除指定的信号量;若cmd参数取值为GETPID,表示返回最后一个执行

semop操作的进程ID;若cmd参数取值为GETVAL,表示返回信号集中指定信号的值;若cmd参数取值为GETALL,表示返回信号集中所有的信号值;3信号集的控制15:539/267

若cmd参数取值为GETNCNT,表示返回正在等待资源的进程数量;若cmd参数取值为GETZCNT,表示返回正在等待完全空闲资源的进程数量;若cmd参数取值为

SETVAL,表示设置信号集中指定信号的值;若cmd参数取值为SETALL,表示设置信号集中所有的信号值。3信号集的控制

arg参数表示semun类型的联合体,该联合体中各个量的使用情况与参数cmd设置有关。semun类型的联合体的数据结构如下所示:union

semun

{int

val;

/*

Value

for

SETVAL

*/

struct

semid_ds

*buf;

/*

Buffer

for

IPC_STAT,IPC_SET

*/*array;

/*

Array

for

GETALL,

SETALL*

buf;

/*

Buffer

for

IPC_INFO

(Linux-unsigned

short*/

struct

seminfospecific)

*/};15:540/2673信号集的控制15:541/267val:用于SETVAL操作,设置某个信号量的值;

buf:用于IPC_STAT和IPC_SET操作,存取semid_ds数据结构;array:用于SETALL和GETALL操作;

buf:为控制IPC_INFO所提供的缓冲区;函数调用成功返回非-1,否则调用失败返回-1。4综合示例15:542/267

在本节最后,通过利用信号量的相关函数进行系统调用,来演示如何创建和使用信号量,加强对进程通信的理解。共享内存的读函数my_semget_reader如范例8-2a所示,共享内存的写函数my_semget_writer如范例8-2b所示。例8-2a

my_semget_reader.c/****my_semget_reader.c***/例8-2b

my_semget_writer.c/****my_semget_writer.c***/在VIM编辑器中具体阐述代码在eclipse集成开发环境IDE中编译、调试、运行代码8.4管道通信15:543/267

在Linux系统中,管道实质是存放在内核中的特殊文件,常用于处理进程间的同步和通信问题。管道是一种半双工的通信方式,特点是每个管道都有读入端和写入端,数据只能单向流动,主要用在有亲缘关系的进程间的通信。

和共享内存、信号量和消息队列相比,管道通信有自己的优点和缺点。它的优点是用起来方便很多,系统开销也小。它的缺点一是数据只能单向流动,进行半双工通信;二是管道只能用于父子进程或兄弟进程间的通信,非亲缘关系的进程无法通信;管道没有名字,传输的是无格式的字节流,而且缓冲区大小受限等缺点。8.4.1管道的创建和关闭15:544/267

管道的创建和关闭函数分别是pipe和close。系统通过调用它在程序中完成管道的创建和关闭工作。在Linux系统终端中使用帮助命令“man

pipe”和命令“man

close”,得到管道的创建和关闭函数信息如下:#include

<unistd.h>int

pipe(int

pipefd[2]);#include

<unistd.h>int

close(int

fd);8.4.1管道的创建和关闭15:545/267

pipe:该函数的返回类型为整型,用于创建一个管道。

pipefd参数是一个二元型的整型数组,用于存放管道读写两端的文件描述符,pipefd[0]存放管道读入端的文件描述符,pipefd[1]存放管道写入端的文件描述符。函数调用成功返回0;否则调用失败返回-1。

close:该函数的返回类型为整型,用于关闭一个管道。

fd参数是一个整型数,用于存放所要关闭管道的文件描述符。函数调用成功返回0;否则调用失败返回-1。8.4.2管道的读写操作15:546/267

管道是一种文件,所以对文件操作的读写函数入read()和

write()都适用于管道。管道创建成功后,当父进程调用

fork成功创建子进程时,父子进程才共享该管道的读入端和写入端的文件描述符,此时父子进程才可以通过管道

实现进程间的通信。

使用管道进行通信的两个父子进程,一个进程负责向管

道写数据(称为写进程),另一进程负责向管道读数据(称

为读进程)。因此,所创建的一个管道有读入端和写入端,分别用文件描述符fd[0]和fd[1]表示。管道的读入端fd[0]和写入端fd[1],分别具有两种状态开和关。8.4.2管道的读写操作15:547/267

因此通过管道要实现读写进程间同步通信,必须控制好管道两端的状态。如果当读进程需要从管道中读数据的时候,则读进程会打开管道的读入端fd[0]状态为开,同时会要求写进程关闭管道的写入端fd[1]状态为关;同样道理,如果当写进程需要向管道中写数据的时候,则写进程会打开管道的写入端fd[1]状态为开,同时会要求读进程关闭管道的读入端fd[0]状态为关。8.4.2管道的读写操作15:548/267

读进程在管道读入端读取数据时,若管道写入端不存在,则读到了文件尾返回读取字节数为0;若管道写入端存在,并且请求读取的数据量大于管道中的数据量,则返回管

道中所有现有的数据,否则,请求读取的数据量小于等

于管道中的数据量,则返回管道中缓冲区头部的请求字

节数的数据。8.4.2管道的读写操作15:549/267

写进程在管道写入端写入数据时,若读进程不读走管道中的数据,则写进程会等待处于阻塞状态。在写管道时,若请求写入的数据量小于等于管道中缓冲区的数据量,

则多进程的写操作不会交错进行;若请求写入的数据量

大于管道中缓冲区的数据量,则多进程的写操作会交错

进行。8.4.2综合示例15:550/267

在本节最后,通过利用管道的相关函数进行系统调用,来演示如何创建和使用管道,加强对进程通信的理解。管道函数my_pipe如范例8-3所示。例8-3

my_pipe.c/****my_pipe.c***/在VIM编辑器中具体阐述代码在eclipse集成开发环境IDE中编译、调试、运行代码8.4.2综合示例15:551/267

在本节最后,通过利用管道的相关函数进行系统调用,来演示如何创建和使用管道,加强对进程通信的理解。管道函数my_pipe如范例8-3所示。例8-3

my_pipe.c/****my_pipe.c***/在VIM编辑器中具体阐述代码在eclipse集成开发环境IDE中编译、调试、运行代码8.5命名管道15:552/267

上节介绍的管道只能用于具有亲缘关系的进程间的通信,其原因是管道没有名字。本节介绍命名管道,它是有名

字的管道。和管道相比较,它有自己独特的特点。

首先,命名管道的使用比管道更灵活方便。命名管道不但可用于亲缘关系进程间的通信,也可用于非亲缘关系进程间的通信,这是二者的最明显区别。8.5命名管道15:553/267

其次,命名管道存放在文件系统中,而管道存放在系统的内核中。当进程对管道使用结束后,系统会从内核中

删除管道的文件信息;而进程对命名管道使用结束后,

命名管道文件依然存放在文件系统中而系统不会删除它。

最后,无论是命名管道还是管道,它们只能进行单向数据传输,若实现数据的双向传输,则需要创建两个命名管道或管道。8.5.1命名管道的创建15:554/267

命名管道的创建函数有两种mkfifo和mknod。系统通过调用它们在程序中完成管道的创建工作。在Linux系统终端

中使用帮助命令“man

3

mkfifo”和命令“man

2

mknod”,得到管道的创建函数信息如下:8.5.1命名管道的创建15:555/267#include

<sys/types.h>#include

<sys/stat.h>int

mkfifo(const

char

*pathname,

mode_t

mode);#include

<sys/types.h>#include

<sys/stat.h>#include

<fcntl.h>#include

<unistd.h>

int

mknod(const

char

*pathname,

mode_t

mode,

dev_tdev);8.5.1命名管道的创建15:556/267

mkfifo:该函数的返回类型为整型,用于创建一个有名管道。pathname参数是一个字符串指针,用于存放命名管

道的文件名。mode参数是一个整型参数,用于表示所创

建文件的权限信息,具体含义和创建普通文件create函数中的mode参数类似。函数调用成功返回0;否则调用失败返回-1。8.5.1命名管道的创建15:557/267

mknod:该函数的返回类型为整型,用于创建一个有名管道。pathname和mod参数的含义和mkfifo函数一样。

dev参数是只有在创建设备文件时用到,表示设备的值,该值取决于创建文件的种类。函数调用成功返回0;否则调用失败返回-1。

命名管道也可通过shell命令创建,这种创建方式简单直接。实现创建的具体的命令也是mkfifo和mknod。8.5.2命名管道的使用15:558/267

命名管道创建成功后,系统会保存在文件系统中。如果一个进程要使用该命名管道,则该进程首先要打开这个

命名管道。命名管道是一个特殊的文件,它的操作和使

用方法与普通文件的操作和使用方法类似,通过open、read、write和close函数打开、读取、写入和关闭一个命名管道。若要删除一个命名管道,则使用unlink函数即可完成。8.5.2命名管道的使用15:559/267

命名管道的读写操作和管道的操作方式相似,在此不再重述。在使用命名管道时,需要注意的地方是通信的两

个进程要分别打开该命名管道,所以当某个进程读打开

(或写打开)一个命名管道时而其它进程却没及时写打开

(或读打开)该命名管道,某进程就会处于阻塞状态,直到有其它进程写打开(或读打开)该命名管道为止。8.5.2综合示例15:560/267

在本节最后,通过利用命名管道的相关函数进行系统调用,来演示如何创建和使用命名管道,加强对进程通信的理解。命名管道的写函数my_fifo_writer.c如范例8-4a所示,读函数my_fifo_reader.c如范例8-4b所示。例8-4a

my_fifo_writer.c/****my_fifo_writer.c***/例8-4b

my_fifo_reader.c/****

my_fifo_reader.c***/在VIM编辑器中具体阐述代码在eclipse集成开发环境IDE中编译、调试、运行代码8.6消息队列15:561/267

在Linux系统中,消息队列的实质是存放在操作系统内核中的消息链表,常用于处理进程间的同步和互斥问题。消息队列同信号量、共享内存一样,都是由引用标识符标示,不同的是消息队列对每个消息指定了特定消息类型和消息内容。消息队列具有传递信息量大、可以承载各种格式的字节流和可以动态设置缓冲区大小的优点。8.6消息队列

每个消息队列在操作系统内核中维护着一个与之相应的数据结构msqid_ds实例,它的具体定义如下所示:15:562/2678.6消息队列15:563/267

这个结构详细描述了消息队列的各种属性,它们的具体含义如下所示:msg_perm:表示消息队列的用户ID、组ID、权限等信息;msg_stime:表示最近一次调用msgsnd函数的时间;msg_rtime:表示最近一次调用msgrcv函数的时间;msg_ctime:表示最近一次改变该消息队列的时间;8.6消息队列15:564/267

msg_cbytes:表示消息队列中当前的总字节数;msg_qnum:表示消息队列中当前的消息总数;msg_qbytes:表示消息队列中最大容纳的字节数;msg_lspid:表示最进一次调用msgsnd函数的进程ID号;msg_lrpid:表示最进一次调用msgrcv函数的进程ID号;8.6消息队列15:565/267ipc_perm的内核数据结构具体定义如下所示:struct

ipc_perm

{key_t

key;/*

Key

supplied

to

msgget(2)

*/uid_tuid;/*

Effective

UID

of

owner

*/gid_tgid;/*

Effective

GID

of

owner

*/uid_tcuid;/*

Effective

UID

of

creator

*/gid_tcgid;/*

Effective

GID

of

creator

*/mode;

seq;/*

Permissions

*//*

Sequence

number

*/unsigned

shortunsigned

short};8.6消息队列15:566/267

key:表示创建消息队列用到的键值;uid:表示消息队列的用户ID;gid:表示消息队列的组ID;cuid:表示创建消息队列的进程用户ID;cgid:表示创建消息队列的进程组ID;mode:表示权限标示符;

seq:表示消息队列中的消息数;8.6消息队列15:567/267

消息队列中每个消息指定了特定消息类型和消息内容,消息的结构详细描述具体含义如下所示:struct

msgbuf{long

mtype;char

mtext[1024];};mtype:表示消息类型;mtext:表示消息内容;下面给出基于上述消息队列的数据结构的函数调用。8.6.1消息队列的创建和打开15:568/267

消息队列的创建函数是msgget。系统通过调用它在程序中完成消息队列的创建工作。在Linux系统终端中使用帮助命令“man

msgget”,得到消息队列的创建函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/msg.h>int

msgget(key_t

key,

int

msgflg);8.6.1消息队列的创建和打开15:569/267

msgget:该函数的返回类型为整型,用于创建或打开一个消息队列。key参数表示由ftok生成的消息队列键。msgflg参数表示消息队列的操作标志位,用于设置消息队列的访问权限。

msgflg参数含义和共享内存的shmget函数一样。msgget函数所执行的操作由参数key和msgflg共同决定,相关约定和shmget函数类似,在此不再重述。8.6.1消息队列的创建和打开15:570/267

函数调用成功返回消息队列的引用标示符,同时该消息队列的msqid_ds结构被初始化,msg_perm中各成员被设置相应的值,msg_qtypes设置为系统限制的值,

msg_ctime设置为当前时间,其余成员的值设置为0;否则调用失败返回-1。8.6.2向消息队列中发送消息15:571/267

发送消息的函数是msgsnd。系统通过调用它在程序中完成消息的发送工作。在Linux系统终端中使用帮助命令

“man

msgsnd”,得到消息的发送函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/msg.h>

int

msgsnd(int

msqid,

const

void

*msgp,

size_t

msgsz,int

msgflg);8.6.2向消息队列中发送消息15:572/267

msgsnd:该函数的返回类型为整型,用于向一个消息队列的末尾中发送消息。msqid参数表示消息队列的引用标示符。msgp参数是一个指针,用于指向要发送的消息。

msgsz参数是发送消息中数据的字节长度。msgflg参数用于指定当消息队列满的时处理方法,若消息队列满了,且IPC_NOWAIT位,则立即返回出错,否则阻塞发送消

息的进程,直到有消息被删除或消息队列中有空间时返回。函数调用成功返回0,否则调用失败返回-1。8.6.3从消息队列中接收消息15:573/267

接收消息的函数是msgrcv。系统通过调用它在程序中完成消息的接收工作。在Linux系统终端中使用帮助命令

“man

msgrcv”,得到消息的接收函数信息如下:#include

<sys/types.h>#include

<sys/ipc.h>#include

<sys/msg.h>

ssize_t

msgrcv(int

msqid,

void

*msgp,

size_t

msgsz,long

msgtyp,

int

msgflg);8.6.3从消息队列中接收消息15:574/267

msgrcv:该函数的返回类型为整型,用于从指定的消

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论