`
mylxiaoyi
  • 浏览: 328450 次
  • 性别: 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 函数形成同原有进程基本上具有一致性的进程,并且使用信号量和等待函数来实现进程间的通信和同步。通过这些实践,可以更好的理解和掌握多进程程序设计的...

    Linux操作系统实验报告

    通过以上实验,学生能够全面地理解和掌握Linux操作系统的基本命令、编程技巧、进程间通信、内存管理、设备驱动以及网络编程等多个方面的知识和技术,为进一步深入研究和应用Linux操作系统奠定坚实的基础。

    题目1--shell-程序设计.pdf

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

    厦大-unix程序设计课实验作业.zip

    - **进程间通信(IPC)**:可能涉及管道(pipe)、消息队列、共享内存等通信方式。 - **信号处理**:学习如何使用`signal()`函数注册信号处理函数,以及常见的信号如SIGINT(中断),SIGTERM(终止)等。 3. **...

    《嵌入式Linux应用程序开发标准教程+源码》(第2版全)[高清版]

    这本书涵盖了从基础到高级的多个关键领域,包括Linux操作系统原理、C编程、进程控制、多线程编程、进程间通信、网络编程以及设备驱动开发等。每个章节都配有高清的doc文档,以便读者深入理解并实践。此外,教程还...

    嵌入式Linux应用程序开发标准教程

    - **第8章:进程间通信开发** - 信号量与互斥锁:同步访问共享资源。 - 消息队列与管道:进程间数据交换机制。 - **第9章:多线程开发** - 线程概念与创建:理解线程的优势。 - 线程同步与死锁避免:确保程序的...

    新版Android开发教程.rar

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

    embeded-Linux-introduction-070822.pdf

    - 进程通信——信号的使用:使用信号进行进程间通信。 - 防止僵死进程:解决僵尸进程问题。 #### 五、用户图形界面设计 - **QT应用编程**: - QT基础:了解QT框架的基本概念。 - 编写GUI应用程序:使用QT ...

    嵌入式Linux应用程序开发标准教程(第2版)——华清远见

    第8章“进程间通信”讲解了Linux中不同进程如何共享数据和协调工作,包括管道、消息队列、共享内存、信号量和套接字等多种通信机制,是实现多进程协同工作的关键。 第9章“多线程编程”涉及线程的概念、创建和管理...

    qt for windows版本下编译qtdbus模块

    QtDBus 是用于实现基于 DBus 协议的进程间通信 (IPC) 的组件。尽管 QtDBus 默认支持 Unix 类操作系统,但 Windows 用户同样希望能够在 Windows 平台上使用这一功能。本文将详细介绍如何在 Windows 环境下构建 QtDBus...

    进程的管道通信实验任务书.doc

    **管道通信**是操作系统中一种重要的进程间通信方式。本实验旨在帮助学生深入理解管道通信的工作原理及其在UNIX/Linux环境中的具体应用。 1. **理解管道概念**: - 管道是一种特殊的文件,用于连接两个进程的输出...

    嵌入式linux开发应用程序开发详解

    本章将介绍`ps`(查看进程状态)、`kill`(发送信号给进程)、`nohup`(让进程在后台持续运行)等命令,以及进程间通信(IPC)的基本原理。 **第6章 网络编程** 嵌入式Linux系统常常需要处理网络通信。本章将讲解套...

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

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

    嵌入式Linux应用程序开发详解.rar

    **第七章LinuxApp07-ProcessControlling.pdf**:这章可能涉及进程管理,包括进程创建(fork、exec系列函数)、进程间通信(管道、消息队列、共享内存等)、信号处理等,这些都是多任务操作系统中理解和控制程序行为...

    嵌入式Linux应用程序开发详解02-Linux基础命令.pdf

    《嵌入式Linux应用程序开发详解02-Linux基础命令》主要涵盖了Linux系统中的一些核心概念和常用命令,这些对于在嵌入式系统中进行应用程序开发至关重要。本章旨在帮助读者熟练掌握Linux的基础操作,以便更好地利用...

    dbus-core详解

    D-Bus,全称为Desktop Bus,是一种低延迟、异步的IPC(进程间通信)协议。它主要用于Linux、BSD以及其他自由的类UNIX系统。D-Bus的目的是作为各种早期临时机制的替代品,这些机制在Linux桌面早期年份中用于进程间...

Global site tag (gtag.js) - Google Analytics