文档章节

linux进程通讯之CD程序

Foundation
 Foundation
发布于 2016/02/05 09:53
字数 2270
阅读 30
收藏 4

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
粉丝 7
博文 208
码字总数 157873
作品 0
无锡
76.linux 常用命令 记录(6.7版本)

1.Linux版本 centOs 6.7 1.1 准备工作 (1)安装虚拟机 : (2)Linux系统 : (3)CRT(Linux客户端): 2. 常用命令 (1)pwd:查看现在所在位置 root账号默认在进入后的位置在 root文件夹下...

Lucky_Me
01/01
0
0
Linux小知识-5:内核结构

Linux内核主要由五个部分组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信。 稍微加以理解:CPU资源——内存资源——外存资源——外部资源——通信。 进程调度(SCHED):控制进...

lp_king
2013/09/28
0
0
Linux进程和线程间IPC机制

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

dodonei
04/16
0
0
【转载】配置开发支持高并发TCP连接的Linux应用程序全攻略

1、修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,最高的并发数量都要受到系统对用户单一进程同时可打开文件数量 的限制(这是...

晨曦之光
2012/03/09
0
0
Ubuntu 16.04+.Net Core+Docker+Nginx安装部署

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

dotNET跨平台
05/03
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

day58-20180816-流利阅读笔记-待学习

苹果市值破万亿,iPhone 会涨价吗? Lala 2018-08-16 1.今日导读 苹果教父乔布斯曾经说过:“活着就是为了改变世界。”虽然他在 56 岁时就遗憾离世,但他极具创新和变革的精神早已深埋进苹果...

aibinxiao
31分钟前
4
0
[雪峰磁针石博客]python3快速入门教程1 turtle绘图-2函数

菲波那契序列: >>> # Fibonacci series:... # the sum of two elements defines the next... a, b = 0, 1>>> while b < 10:... print(b)... a, b = b, a+b...112......

python测试开发人工智能安全
今天
0
0
java环境变量配置最正确的方式

原贴:https://blog.csdn.net/qq_40007997/article/details/79784711,十分详细,亲测有效

kitty1116
今天
0
0
49.Nginx防盗链 访问控制 解析php相关 代理服务器

12.13 Nginx防盗链 12.14 Nginx访问控制 12.15 Nginx解析php相关配置(502的问题) 12.16 Nginx代理 扩展 502问题汇总 http://ask.apelearn.com/question/9109 location优先级 http://blog....

王鑫linux
今天
2
0
Nginx防盗链、访问控制、解析php相关配置、Nginx代理

一、Nginx防盗链 1. 编辑虚拟主机配置文件 vim /usr/local/nginx/conf/vhost/test.com.conf 2. 在配置文件中添加如下的内容 { expires 7d; valid_referers none blocked server_names *.tes......

芬野de博客
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部