`
mylxiaoyi
  • 浏览: 321678 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

进程间通信应用-CD程序

阅读更多


CD数据库程序

现在我们可以使用我们在这一章所了解的IPC工具来修改我们的CD数据库程序。

我们可以使用三种IPC工具的多种不同组,但是因为我们需要传送的信息很少,直接使用消息队列实现请求的传递是一个很明显的选择。

如果我们需要传递的数据量很大,我们可以考虑使用共享内存传递实际的数据,并且使用信号量或是消息来传递一个标记通知其他的进程在共享内存中有数据可用。

消息队列接口解决我们了在第11章所遇到的问题,即当数据传递时我们需要两个进程使得管道打开。使用消息队列可以使得一个进程将消息放入队列,尽管这个进程是当前队列的唯一用户。

我们需要考虑的一个重要决定就是将答案返回给客户。一个简单的选择就是使得一个队列用于服务器而且每个客户有一个队列。如果有大量的并发客户,由于需要大量的消息队列就会引起问题。通过使用消息中的消息ID域,我们可以使得所有的用户使用一个队列,并且通过在消息中使用客户端进程ID来将响应发送给指定的客户端进程。从而每一个客户可以接收只属于他自己的消息,而将其他客户的消息留在队列中。

要转换我们的CD程序来使用IPC工具,我们只需要替换pipe_imp.c文件。在下面的部分,我们将会描述替换文件ipc_imp.c的原则部分。

试验--修改服务器函数

1 首先,我们包含正确的头文件,声明消息队列键值,并且定义一个保存我们消息数据的结构:

#include “cd_data.h”
#include “cliserv.h”
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#define SERVER_MQUEUE 1234
#define CLIENT_MQUEUE 4321
struct msg_passed {
long int msg_key; /* used for client pid */
message_db_t real_message;
};

2 两个全局变量保存由msgget函数所返回的两个队列标识符:

static int serv_qid = -1;
static int cli_qid = -1;

3 我们使得服务器负责创建两个消息队列:

int server_starting()
{
#if DEBUG_TRACE
printf(“%d :- server_starting()\n”, getpid());
#endif
serv_qid = msgget((key_t)SERVER_MQUEUE, 0666 | IPC_CREAT);
if (serv_qid == -1) return(0);
cli_qid = msgget((key_t)CLIENT_MQUEUE, 0666 | IPC_CREAT);
if (cli_qid == -1) return(0);
return(1);
}

4 服务器同时负责退出时的清理工作。当服务器结束时,我们设置我们的全局变量为非法值。这就会捕获服务器尝试在调用server_ending之后发送消息的bug。

void server_ending()
{
#if DEBUG_TRACE
printf(“%d :- server_ending()\n”, getpid());
#endif
(void)msgctl(serv_qid, IPC_RMID, 0);
(void)msgctl(cli_qid, IPC_RMID, 0);
serv_qid = -1;
cli_qid = -1;
}

5 服务器read函数由队列中读取一条任意的消息,并且返回消息的数据部分。

int read_request_from_client(message_db_t *rec_ptr)
{
struct msg_passed my_msg;
#if DEBUG_TRACE
printf(“%d :- read_request_from_client()\n”, getpid());
#endif
if (msgrcv(serv_qid, (void *)&my_msg, sizeof(*rec_ptr), 0, 0) == -1) {
return(0);
}
*rec_ptr = my_msg.real_message;
return(1);
}

6 使用存储在清求中标识消息的客户进程ID来发送响应:

int send_resp_to_client(const message_db_t mess_to_send)
{
struct msg_passed my_msg;
#if DEBUG_TRACE
printf(“%d :- send_resp_to_client()\n”, getpid());
#endif
my_msg.real_message = mess_to_send;
my_msg.msg_key = mess_to_send.client_pid;
if (msgsnd(cli_qid, (void *)&my_msg, sizeof(mess_to_send), 0) == -1) {
return(0);
}
return(1);
}

试验--修改客户端函数

1 当客户端启动时,他需要发现服务器与客户端队列标识符。客户端并不创建队列。如果服务器没有运行,这个函数就会失败,因为消息队列并不存在。

int client_starting()
{
#if DEBUG_TRACE
printf(“%d :- client_starting\n”, getpid());
#endif
serv_qid = msgget((key_t)SERVER_MQUEUE, 0666);
if (serv_qid == -1) return(0);
cli_qid = msgget((key_t)CLIENT_MQUEUE, 0666);
if (cli_qid == -1) return(0);
return(1);
}

2 与服务器一样,当客户端结束时,我们设置我们的全局变量为非法值。这将会捕获当客户端尝试在调用client_ending之后发送消息的bug。

void client_ending()
{
#if DEBUG_TRACE
printf(“%d :- client_ending()\n”, getpid());
#endif
serv_qid = -1;
cli_qid = -1;
}

3 要向服务器发送消息,我们在我们的结构中存储数据。注意,我们必须设置消息键值。因为0作为键值是非法的,保留键值未定义就意味着他可以使用一个随机值,所以如果这个值恰好为0时函数就会失败。

int send_mess_to_server(message_db_t mess_to_send)
{
struct msg_passed my_msg;
#if DEBUG_TRACE
printf(“%d :- send_mess_to_server()\n”, getpid());
#endif
my_msg.real_message = mess_to_send;
my_msg.msg_key = mess_to_send.client_pid;
if (msgsnd(serv_qid, (void *)&my_msg, sizeof(mess_to_send), 0) == -1) {
perror(“Message send failed”);
return(0);
}
return(1);
}

4 当客户端服务器接收消息时,他会使用其进程ID来接收只发送给他的消息,而忽略其他进程的消息。

int read_resp_from_server(message_db_t *rec_ptr)
{
struct msg_passed my_msg;
#if DEBUG_TRACE
printf(“%d :- read_resp_from_server()\n”, getpid());
#endif
if (msgrcv(cli_qid, (void *)&my_msg, sizeof(*rec_ptr), getpid(), 0) == -1) {
return(0);
}
*rec_ptr = my_msg.real_message;
return(1);
}

5 要获得与pipe_imp.c的完全兼容,我们需要定义另外四个函数。然而,在我们的新程序中,这个函数是空的。当使用管道时他们所实现的操作也不再需要了。

int start_resp_to_client(const message_db_t mess_to_send)
{
return(1);
}
void end_resp_to_client(void)
{
}
int start_resp_from_server(void)
{
return(1);
}
void end_resp_from_server(void)
{
}

此程序到消息队列的转换演示了IPC消息队列的强大。我们需要更少的函数,而我们所需要要比以前的实现少得多。

IPC状态函数

尽管X/Open并没有要求,大多数的Linux提供了一个命令集合来允许命令行访问IPC信息,并且清理无关联的IPC工具。这就是ipcs与ipcrm命令,当我们开发程序时,这是非常有用的。

编写糟糕的程序或是因为某些原因失败的程序会遗留其IPC资源。这会使得新的程序调用失败,因为程序期望以一个干净的系统开始,但是却发现一些遗留的资源。状态(ipcs)与清除(ipcrm)命令提供了一个检测与清除IPC遗留资源的一种方法。

信号量

要检测系统中信息量的状态,可以使用ipcs -s命令。如果存在一些信号量,输出就会有如下的形式:

$ ./ipcs -s
——— Semaphore Arrays ————
semid owner perms nsems status
768 rick 666 1

我们可以使用ipcrm命令来移除由程序偶然留下的信号量。要删除前面的信号量,可以使用下面的命令:

$ ./ipcrm -s 768

一些较老的Linux系统使用一些略微不同的语法:

$ ./ipcrm sem 768

但是这种风格不再推荐使用。查看我们系统的手册页来确定在我们的系统上是哪种格式。

共享内存

与信号量类似,许多系统提供了命令行程序用于访问共享内存的详细信息。命令为ipcs -m与ipcrm -m <id>。

如下面的例子输出:

$ ipcs -m
——— Shared Memory Segments ————
shmid owner perms bytes nattch status
384 rick 666 4096 2

这显示一个4KB的共享内存段与两个进程相关联。

ipcrm -m <id>命令可以移除共享内存。当一个程序清理共享内存失败时,这会十分有用。

消息队列

对于消息队列的命令为ipcs -q与ipcrm -q <id>。

如下面的例子输出:

$ ipcs -q
——— Message Queues ————
msqid owner perms used-bytes messages
384 rick 666 2048 2

这显示了在消息队列中有两个消息,共计2048字节。

ipcrm -q <id>命令可以移除消息队列。

总结

在这一章,我们了解了首次在UNIX Systme V.2中广泛使用并且在Linux中可用的三种进程间交互工具。他们是信号量,共享内存与消息队列。我们了解了他们所提供的高级功能以及如何提供这些功能,一旦理解了这些函数,他们就会为需要进程间通信的程序提供强大的解决方案。

分享到:
评论

相关推荐

    操作系统实验报告(LINUX进程间通信)

    操作系统实验报告(LINUX进程间通信)是操作系统课程的一部分,涵盖了Linux进程间通信的原理和应用,包括消息队列、C/S结构等内容。下面将对这些知识点进行详细的解释。 一、消息队列 消息队列是Linux进程间通信的...

    嵌入式linUX多进程应用程序的设计与实现.pdf

    此外,本文还对多进程程序设计方案进行了实践,例如使用 fork 函数形成同原有进程基本上具有一致性的进程,并且使用信号量和等待函数来实现进程间的通信和同步。通过这些实践,可以更好的理解和掌握多进程程序设计的...

    新版Android开发教程.rar

    Android 是一个专门针对移动设备的软件集,它包括一个操作系统,中间件和一些重要的应用程序。 Beta 版 的 Android SDK 提供了在 Android 平台上使用 JaVa 语言进行 Android 应用开发必须的工具和 API 接口。 特性 ...

    Android系统源代码情景分析 / 罗升阳著

    Android专用驱动系统篇介绍了Logger日志驱动程序、Binder进程间通信驱动程序以及Ashmem匿名共享内存驱动程序;Android应用程序框架篇从组件、进程、消息以及安装四个维度对Android应用程序的框架进行了深入的剖析。...

    WindowsAPI开发详解所附的CD光盘

    第11章 进程间通信 第12章 Windows Shell程序设计 第13章 Windows GDI 第14章 网络通信与配置 第15章 程序安装与设置 第16章 设备驱动管理与内核通信 第17章 用户、认证和对象安全 第18章 Windows API的内部原理

    Linux程序设计 第4版.haozip01

    第13章 进程间通信:管道 443 13.1 什么是管道 443 13.2 进程管道 444 13.3 将输出送往popen 445 13.3.1 传递更多的数据 446 13.3.2 如何实现popen 447 13.4 pipe调用 449 13.5 父进程和子进程 451 13.5.1 ...

    shell程序实验详解

     学习使用POSIX/UNIX系统调用、对进程进行管理和完成进程之间的通信,例如使用信号和管道进行进程间通信。  理解并发程序中的同步问题。  锻炼在团队成员之间的交流与合作能力。 2. 实验要求 1. ysh解释程序...

    Linux程序设计 第4版.haozip02

    第13章 进程间通信:管道 443 13.1 什么是管道 443 13.2 进程管道 444 13.3 将输出送往popen 445 13.3.1 传递更多的数据 446 13.3.2 如何实现popen 447 13.4 pipe调用 449 13.5 父进程和子进程 451 13.5.1 ...

    操作系统课程实验.rar

    通信的 具体内容可根据自己的需要随意设计,要求能试验阻塞型读写过程中的各种情况,测试管道 的默认大小,并且要求利用 Posix 信号量机制实现进程间对管道的互斥访问。运行程序,观 察各种情况下,进程实际读写的...

    C#程序开发范例宝典(第2版).part08

    实例246 创建应用程序快捷方式 340 7.5 系统设置 340 实例247 设置桌面背景 341 实例248 设置系统时间 342 实例249 设置屏幕分辨率 343 7.6 系统监控 344 实例250 检测系统启动模式 344 实例251 内存使用状态...

    Visual C++ 程序开发范例宝典 源码 光盘 part2

    cc实例029 自动运行的多媒体宣传光盘 1.8 多媒体触摸屏程序应用实例 cc实例030 采购中心多媒体触摸屏程序 cc实例031 为触摸屏程序添加虚拟键盘 1.9 窗体位置应用实例 cc实例032 不可移动的窗体 cc实例...

    Visual C++程序开发范例宝典(PDF扫描版).part2

     1.8 多媒体触摸屏程序应用实例   cc实例030 采购中心多媒体触摸屏程序   cc实例031 为触摸屏程序添加虚拟键盘   1.9 窗体位置应用实例   cc实例032 不可移动的窗体   cc实例033 始终在最上面的...

    Visual C++程序开发范例宝典(PDF扫描版).part3

     1.8 多媒体触摸屏程序应用实例   cc实例030 采购中心多媒体触摸屏程序   cc实例031 为触摸屏程序添加虚拟键盘   1.9 窗体位置应用实例   cc实例032 不可移动的窗体   cc实例033 始终在最上面的...

    C#程序开发范例宝典(第2版).part12

    实例246 创建应用程序快捷方式 340 7.5 系统设置 340 实例247 设置桌面背景 341 实例248 设置系统时间 342 实例249 设置屏幕分辨率 343 7.6 系统监控 344 实例250 检测系统启动模式 344 实例251 内存使用状态...

    Visual C++程序开发范例宝典(光盘) 第四部分

    1.8 多媒体触摸屏程序应用实例 实例030 采购中心多媒体触摸屏程序 实例031 为触摸屏程序添加虚拟键盘 1.9 窗体位置应用实例 实例032 不可移动的窗体 实例033 始终在最上面的窗体 实例034 动画显示窗体 实例...

    Visual C++程序开发范例宝典(光盘) 第八部分

    1.8 多媒体触摸屏程序应用实例 实例030 采购中心多媒体触摸屏程序 实例031 为触摸屏程序添加虚拟键盘 1.9 窗体位置应用实例 实例032 不可移动的窗体 实例033 始终在最上面的窗体 实例034 动画显示窗体 实例...

Global site tag (gtag.js) - Google Analytics