文档章节

linux进程通讯之CD程序

Foundation
 Foundation
发布于 2016/02/05 09:53
字数 2270
阅读 30
收藏 4
点赞 1
评论 0

linux进程通讯之CD程序

CD数据库程序 

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


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


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


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


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


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


试验--修改服务器函数 

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

1
2
3
4
5
6
7
8
9
10
11
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23include" class = "bdcs-inlinelink"   target= "_blank" >#include</a> “cd_data.h”
#include “cliserv.h”
#include <<a href="http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=sys%2Ftypes.h" class="bdcs-inlinelink" target="_blank">sys/types.h</a>>
#include <<a href="http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=sys%2Fipc.h" class="bdcs-inlinelink" target="_blank">sys/ipc.h</a>>
#include <sys/msg.h>
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23define" class = "bdcs-inlinelink"   target= "_blank" >#define</a> 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函数所返回的两个队列标识符:

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

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

1
2
3
4
5
6
7
8
9
10
11
int  server_starting()
{
     # if  DEBUG_TRACE
         printf (“%d :- server_starting()/n”,  getpid());
     <a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=%23endif" class = "bdcs-inlinelink"   target= "_blank" >#endif</a>
     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。

1
2
3
4
5
6
7
8
9
10
<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=void"   class = "bdcs-inlinelink"   target= "_blank" > void </a> server_ending()
{
     # if  DEBUG_TRACE
         printf (“%d :- server_ending()/n”, getpid());
     #endif
     ( void )<a href= "http://so.21ops.com/cse/search?s=9181936462520079739&entry=1&q=msgctl" class = "bdcs-inlinelink"   target= "_blank" >msgctl</a>(serv_qid, IPC_RMID, 0);
     ( void )msgctl(cli_qid, IPC_RMID, 0);
   serv_qid = -1;
   cli_qid = -1;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
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来发送响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
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 当客户端启动时,他需要发现服务器与客户端队列标识符。客户端并不创建队列。如果服务器没有运行,这个函数就会失败,因为消息队列并不存在。

1
2
3
4
5
6
7
8
9
10
11
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。

1
2
3
4
5
6
7
8
void  client_ending()
{
     # if  DEBUG_TRACE
         printf (“%d :- client_ending()/n”, getpid());
     #endif
     serv_qid = -1;
     cli_qid = -1;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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来接收只发送给他的消息,而忽略其他进程的消息。

1
2
3
4
5
6
7
8
9
10
11
12
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的完全兼容,我们需要定义另外四个函数。然而,在我们的新程序中,这个函数是空的。当使用管道时他们所实现的操作也不再需要了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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命令。如果存在一些信号量,输出就会有如下的形式:

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


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

1
$ ./ipcrm -s 768

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

1
$ ./ipcrm sem 768

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

共享内存

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

如下面的例子输出:

1
2
3
4
$ 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>。


如下面的例子输出:

1
2
3
4
$ 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软件中心试用一下。
免费下载地址:http://www.nfs-cloud.cn:81/appCenter/open/softcenter

© 著作权归作者所有

共有 人打赏支持
Foundation
粉丝 6
博文 208
码字总数 157873
作品 0
无锡
Linux进程和线程间IPC机制

Linux进程间IPC 1.管道(Pipe)及有名管道(named pipe): 1、管道是半双工的,要实线读写需建立两根管道; 2、匿名管道用于父子进程或者兄弟进程之间(如forkexec创建的进程),命名管道允许没...

dodonei ⋅ 04/16 ⋅ 0

Ubuntu 16.04+.Net Core+Docker+Nginx安装部署

前言   最近公司的项目打算移植到.Net Core平台,所以调研了一下.Net Core在Linux下的安装部署。本篇文章会一步步的描述从安装到配置到部署的全部过程。在文章的结构和内容里,笔者借鉴了很...

dotNET跨平台 ⋅ 05/03 ⋅ 0

嵌入式Linux学习基础规划篇

嵌入式的学习是需要日积月累的,是通过一点一滴的积累才能成为大神。下面来介绍一下嵌入式linux学习基础规划,目标是达到适应嵌入式应用软件开发、嵌入式系统开发或嵌入式驱动开发的基本素质...

创客学院 ⋅ 04/10 ⋅ 0

Linux 目录结构:/lib 分析

我们在之前的文章中已经分析了其他重要系统目录,比如 、、、 等。可以根据自己的兴趣进入下列链接了解更多信息。本文中,让我们来看看 目录都有些什么。 目录结构分析:/bin 文件夹 目录结构...

作者: Surendra Anne ⋅ 04/26 ⋅ 0

聊聊BIO,NIO和AIO (2)

本文从操作系统的角度来解释BIO,NIO,AIO的概念,含义和背后的那些事。本文主要分为3篇。 第一篇 讲解BIO和NIO以及IO多路复用 第二篇 讲解磁盘IO和AIO 第三篇 讲解在这些机制上的一些应用的...

大宽宽 ⋅ 05/13 ⋅ 0

测试人员的常用linux命令(持续更新)2018.03.26

我这里从操作目的出发说明linux命令,而不是将linux命令逐个说明用法、注意事项,毕竟我只是一个测试人员,而不是一个专职的linux运维人员,无须掌握所有的命令,掌握每个命令的所有用法; ...

Ye_Kwii ⋅ 06/20 ⋅ 0

JavaWeb23-HTML篇笔记(二)

1.1 Vi和Vim编辑器1.1.1 Vim编辑器: 在Linux下一般使用vi编辑器来编辑文件。vi既可以查看文件也可以编辑文件。三种模式:命令行、插入、底行模式。 切换到命令行模式:按Esc键; 切换到插入...

我是小谷粒 ⋅ 06/11 ⋅ 0

Linux下进程间通讯方式 - UNIX Domain Socket

概述 Linux下进程通讯方式有很多,比较典型的有套接字,平时比较常用的套接字是基于TCP/IP协议的,适用于两台不同主机上两个进程间通信, 通信之前需要指定IP地址. 但是如果同一台主机上两个进程...

程序手艺人 ⋅ 01/03 ⋅ 0

[84题]Linux运维常见笔试题(填空题)

[84题]Linux运维常见笔试题(填空题) 1. 在Linux 系统 中,以文件方式访问设备 。 2. Linux 内核引导时,从文件/etc/fstab中读取要加载的文件系统 。 3. Linux 文件系统中每个文件用indoe节...

代金券优惠 ⋅ 05/23 ⋅ 0

Linux运维工程师笔试题系列2(30题)

Linux运维工程师笔试题系列2(30题) 如果您对问题有疑问,或者认为答案不准确的,欢迎留言交流。 问题如下: 1. 下列哪个git命令不是合并代码用的: A git pull –rebase B git merge C gi...

优惠券发放 ⋅ 05/25 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Jenkins实践3 之脚本

#!/bin/sh# export PROJ_PATH=项目路径# export TOMCAT_PATH=tomcat路径killTomcat(){pid=`ps -ef | grep tomcat | grep java|awk '{print $2}'`echo "tom...

晨猫 ⋅ 今天 ⋅ 0

Spring Bean的生命周期

前言 Spring Bean 的生命周期在整个 Spring 中占有很重要的位置,掌握这些可以加深对 Spring 的理解。 首先看下生命周期图: 再谈生命周期之前有一点需要先明确: Spring 只帮我们管理单例模...

素雷 ⋅ 今天 ⋅ 0

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 今天 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 今天 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

VS中使用X64汇编

需要注意的是,在X86项目中,可以使用__asm{}来嵌入汇编代码,但是在X64项目中,再也不能使用__asm{}来编写嵌入式汇编程序了,必须使用专门的.asm汇编文件来编写相应的汇编代码,然后在其它地...

simpower ⋅ 今天 ⋅ 0

ThreadPoolExecutor

ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ......

4rnold ⋅ 昨天 ⋅ 0

Java正无穷大、负无穷大以及NaN

问题来源:用Java代码写了一个计算公式,包含除法和对数和取反,在页面上出现了-infinity,不知道这是什么问题,网上找答案才明白意思是负的无穷大。 思考:为什么会出现这种情况呢?这是哪里...

young_chen ⋅ 昨天 ⋅ 0

前台对中文编码,后台解码

前台:encodeURI(sbzt) 后台:String param = URLDecoder.decode(sbzt,"UTF-8");

west_coast ⋅ 昨天 ⋅ 0

实验楼—MySQL基础课程-挑战3实验报告

按照文档要求创建数据库 sudo sercice mysql startwget http://labfile.oss.aliyuncs.com/courses/9/createdb2.sqlvim /home/shiyanlou/createdb2.sql#查看下数据库代码 代码创建了grade......

zhangjin7 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部