- 浏览: 327493 次
- 性别:
- 来自: 北京
最新评论
-
lshhjxlj:
不错,赞一个
[翻译]Swing组件集合的事件处理(一) -
mxjisp:
...
Linux下的汇编开发 -
arust:
CSDN 的开发团队技术稀烂的
Linux杂记
管道
下面是管道实现文件,pipe_imp.c,其中有客户端与服务器端函数。
试验--管道实现头文件
1 首先是#include:
#include "cd_data.h"
#include "cliserv.h"
2 我们定义一些在此文件的其他函数中所需要的值:
static int server_fd = -1;
static pid_t mypid = 0;
static char client_pipe_name[PATH_MAX + 1] = {‘\0’};
static int client_fd = -1;
static int client_write_fd = -1;
服务器端函数
下面我们需要探讨服务器端函数。下面的试验部分向我们展示了打开与关闭有名管道以及由客户端读取消息的函数。第二个试验部分则显示了打开,发送与关闭客户端管道的代码,客户端管道基于进程ID。
试验--服务器函数
1 server_starting例程创建服务器要由其中读取命令的有名管道。然后打开这个管道用于读取。open操作将会阻塞,直到一个客户端打开这个管道用于写入。我们使用阻塞模式,从而服务器在等待发送给他的命令时可以执行在管道上执行阻塞读取。
int server_starting(void)
{
#if DEBUG_TRACE
printf(“%d :- server_starting()\n”, getpid());
#endif
unlink(SERVER_PIPE);
if (mkfifo(SERVER_PIPE, 0777) == -1) {
fprintf(stderr, “Server startup error, no FIFO created\n”);
return(0);
}
if ((server_fd = open(SERVER_PIPE, O_RDONLY)) == -1) {
if (errno == EINTR) return(0);
fprintf(stderr, “Server startup error, no FIFO opened\n”);
return(0);
}
return(1);
}
2 当服务器结束时,他移除有名管道,从而客户端可以检测到没有服务器在运行。
void server_ending(void)
{
#if DEBUG_TRACE
printf(“%d :- server_ending()\n”, getpid());
#endif
(void)close(server_fd);
(void)unlink(SERVER_PIPE);
}
4 下面代码中所显示的read_request_from_client函数将会阻塞服务器管道中的读取,直到有客户端向其中写入消息:
int read_request_from_client(message_db_t *rec_ptr)
{
int return_code = 0;
int read_bytes;
#if DEBUG_TRACE
printf(“%d :- read_request_from_client()\n”, getpid());
#endif
if (server_fd != -1) {
read_bytes = read(server_fd, rec_ptr, sizeof(*rec_ptr));
...
}
return(return_code);
}
4 在没有客户端打开管道用于写入的情况下,read操作会返回0;也就是说,客户端会检测到EOF。然后服务器会关闭管道并重新打开,从而他会在客户端打开这个管道之前阻塞。这与服务器第一次启动时的情况相类似;我们已经重新初始化了服务器。在前面的函数中插入下面这段代码:
if (read_bytes == 0) {
(void)close(server_fd);
if ((server_fd = open(SERVER_PIPE, O_RDONLY)) == -1) {
if (errno != EINTR) {
fprintf(stderr, “Server error, FIFO open failed\n”);
}
return(0);
}
read_bytes = read(server_fd, rec_ptr, sizeof(*rec_ptr));
}
if (read_bytes == sizeof(*rec_ptr)) return_code = 1;
服务器是单进程的,也许会同时服务多个客户端。因为每一个客户端使用一个不同的管道来接收他的响应,服务器需要写入不同的管道来向不同的客户端发送响应。因为文件描述符是限制资源,服务器只在他在有数据要发送的情况下才会打开客户端管道用于写入。
我们将打开,写入与关闭客户端管道分为三个独立的函数。当我们要向搜索返回多个结果时我们需要这样做,从而我们可以打管道一次,写入多个响应,然后再关闭管道。
试验--搭建管道
1 首先,我们打开客户管道:
int start_resp_to_client(const message_db_t mess_to_send)
{
#if DEBUG_TRACE
printf(“%d :- start_resp_to_client()\n”, getpid());
#endif
(void)sprintf(client_pipe_name, CLIENT_PIPE, mess_to_send.client_pid);
if ((client_fd = open(client_pipe_name, O_WRONLY)) == -1) return(0);
return(1);
}
2 消息都是通过调用这个函数来发送的。我们会在稍后来看一下相对应的客户端函数。
int send_resp_to_client(const message_db_t mess_to_send)
{
int write_bytes;
#if DEBUG_TRACE
printf(“%d :- send_resp_to_client()\n”, getpid());
#endif
if (client_fd == -1) return(0);
write_bytes = write(client_fd, &mess_to_send, sizeof(mess_to_send));
if (write_bytes != sizeof(mess_to_send)) return(0);
return(1);
}
3 最后,我们关闭客户端管道:
void end_resp_to_client(void)
{
#if DEBUG_TRACE
printf(“%d :- end_resp_to_client()\n”, getpid());
#endif
if (client_fd != -1) {
(void)close(client_fd);
client_fd = -1;
}
}
客户端函数
填充服务器的是pipe_imp.c中的客户端函数。他们与服务器端函数十分类似,所不同的就是send_mess_to_server函数。
试验--客户端函数
1 在检测服务器可以访问之后,client_starting函数初始化客户端管道:
int client_starting(void)
{
#if DEBUG_TRACE
printf(“%d :- client_starting\n”, getpid());
#endif
mypid = getpid();
if ((server_fd = open(SERVER_PIPE, O_WRONLY)) == -1) {
fprintf(stderr, “Server not running\n”);
return(0);
}
(void)sprintf(client_pipe_name, CLIENT_PIPE, mypid);
(void)unlink(client_pipe_name);
if (mkfifo(client_pipe_name, 0777) == -1) {
fprintf(stderr, “Unable to create client pipe %s\n”,
client_pipe_name);
return(0);
}
return(1);
}
2 client_ending函数关闭文件描述符并且删除多余的有名管道:
void client_ending(void)
{
#if DEBUG_TRACE
printf(“%d :- client_ending()\n”, getpid());
#endif
if (client_write_fd != -1) (void)close(client_write_fd);
if (client_fd != -1) (void)close(client_fd);
if (server_fd != -1) (void)close(server_fd);
(void)unlink(client_pipe_name);
}
3 send_mess_to_server函数通过服务器管道发送请求:
int send_mess_to_server(message_db_t mess_to_send)
{
int write_bytes;
#if DEBUG_TRACE
printf(“%d :- send_mess_to_server()\n”, getpid());
#endif
if (server_fd == -1) return(0);
mess_to_send.client_pid = mypid;
write_bytes = write(server_fd, &mess_to_send, sizeof(mess_to_send));
if (write_bytes != sizeof(mess_to_send)) return(0);
return(1);
}
与我们在前面所看到的服务器端函数类似,客户端使用三个函数由服务器得到返回的结果。
试验--得到服务器结果
1 这个客户端函数来监听服务器响应。他使用只读取模式打开一个客户端管道,然后作为只写模式响应这个管道文件。
int start_resp_from_server(void)
{
#if DEBUG_TRACE
printf(“%d :- start_resp_from_server()\n”, getpid());
#endif
if (client_pipe_name[0] == ‘\0’) return(0);
if (client_fd != -1) return(1);
client_fd = open(client_pipe_name, O_RDONLY);
if (client_fd != -1) {
client_write_fd = open(client_pipe_name, O_WRONLY);
if (client_write_fd != -1) return(1);
(void)close(client_fd);
client_fd = -1;
}
return(0);
}
2 下面是由服务器得到匹配数据库记录的主要read操作:
int read_resp_from_server(message_db_t *rec_ptr)
{
int read_bytes;
int return_code = 0;
#if DEBUG_TRACE
printf(“%d :- read_resp_from_server()\n”, getpid());
#endif
if (!rec_ptr) return(0);
if (client_fd == -1) return(0);
read_bytes = read(client_fd, rec_ptr, sizeof(*rec_ptr));
if (read_bytes == sizeof(*rec_ptr)) return_code = 1;
return(return_code);
}
3 最后,下面的客户端函数标识服务器响应的结束:
void end_resp_from_server(void)
{
#if DEBUG_TRACE
printf(“%d :- end_resp_from_server()\n”, getpid());
#endif
/* This function is empty in the pipe implementation */
}
工作原理
在start_resp_from_server中用于写入的客户端open操作:
client_write_fd = open(client_pipe_name, O_WRONLY);
用来在阻止当服务器需要快速响应客户端的多个请求所引起的竞争条件。
要详细的解释这一点,考虑下面的事件序列:
1 客户端向服务器发送请求
2 服务器读取请求,打开客户端管道并且发送响应,但是在他关闭客户端之前会被挂起。
3 客户端打开他的管道用于读取,读取第一个响应然后关闭管道。
4 然后客户端发送一个新的命令并且打开客户端管道用于读取。
5 服务器继续运行,关闭其客户端管道。
不幸的时,此时客户端正尝试读取管道,查找其下一个请求的响应,但是read操作会返回0字节,因为并没有进程使得客户端打开用于写入。
通过允许客户打开其管道用于读写,从而移除了重复重新打开管道的需要,我们可以避免这个竞争条件。注意,客户端并没有写入管道,从而并没有垃圾数据的危险。
程序总结
现在我们将我们的CD数据库程序分为客户端与服务器两部分,从而可以使得我们独立开发用户界面与底层数据库技术。我们可以看到一个定义良好的数据库接口可以使得程序的每一个主要元素都充分利用计算机资源。如果我们更为深入一些,我们可以将管道实现改变为网络实现,并且使用一个数据库服务器。我们将会在第15章了解更多的关于网络的内容。
小结
在这一章,我们了解了在进程之间使用管道来传递数据。首先,我们了解了无名管道,使用popen与pipe调用来进行创建,然后讨论了如何使用管道与dup调用,我们可以将数据由一个程序传递到另一个的标准输入。然后我们了解了有名管道,并且了解了如何在无关的程序之间传递数据。最后,我们实现了一个简单的客户端/服务器例子,使用FIFO提供给我们的不仅是处理同步,还有双向的数据流。
发表评论
-
Linux杂记
2009-09-04 23:28 1070最近一段时间似乎国内 ... -
关于Beginning Linux Programming 3ed
2009-08-25 19:00 1135关于<Beginning Linux Progr ... -
进程间通信应用-CD程序
2009-08-25 18:06 994CD数据库程序 现在我们可以使用我们在这一章所了解的IP ... -
进程间通信之消息队列
2009-08-24 23:33 1395消息队列 现在我们来讨论第三种也是最后一种System ... -
进程间通信之共享内存
2009-08-23 11:51 2039共享内存 共享内存是第二种IPC工具。他允许两个无关的进 ... -
Linux进程间通信之信号量
2009-08-22 08:43 1924第14章 信号量,共享内存与消息队列 在这一章,我们将会 ... -
fglrx 9.8与kernel 2.6.30
2009-08-20 12:11 861Linux Kernel 2.6.30的发布已是很早之前的事情 ... -
进程间通信(八)
2009-08-11 23:25 859搜索数据库 在CD关键字上的搜索比较复杂。函数的用户希望 ... -
进程间通信(七)
2009-08-09 10:11 917CD数据库程序 现在我们已经了解了我们可以如何使用有名管 ... -
进程间通信(六)
2009-08-08 10:08 1198读取与写入FIFO 使用O_NONBLOCK模式会影响作 ... -
进程间通信(五)
2009-08-04 12:20 1456有名管道:FIFO 到目前为止,我们只是可以在两个相关的 ... -
进程间通信(四)
2009-08-03 12:37 976父子进程 我们的pipe调用探索的下一步就是使得子进程是 ... -
进程间通信(三)
2009-08-02 09:04 1069管道调用 我们已经了解了高层的popen函数,现在我们继 ... -
进程间通信(二)
2009-08-01 13:00 1349传递更多的数据 到目 ... -
进程间通信(一)
2009-07-31 15:41 1489第13章 进程间通信:管道 在第11章,我们了解了使用信 ... -
POSIX线程-(六)
2009-07-23 18:24 1912线程属性-调度 下面我们来看一下我们也许希望改变的第二个 ... -
POSIX线程-(四)
2009-07-19 15:06 1295使用互斥同步 在多线程程序中同步访问的另一个方法就是使用 ... -
POSIX线程-(五)
2009-07-21 18:02 1417线程属性 当我们第一次了解线程时,我们并没有讨论线程属性 ... -
POSIX线程(三)
2009-07-08 18:21 1282并发执行 下面我们将要编写一个检测两个线程是否并发执行的 ... -
POSIX线程(二)
2009-07-05 10:32 1691第一个线程程序 有一 ...
相关推荐
在操作系统领域,进程间通信(IPC,Inter-Process Communication)是一项至关重要的技术,它使得不同进程之间能够交换数据,协同工作。本实验“进程间通信--操作系统实验”旨在帮助学生深入理解和熟练掌握这一核心...
在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程之间共享数据、交换信息的关键技术。本文将深入探讨Linux进程间通信的多种方法、原理以及实际应用。 一、管道(Pipe) 管道是一种...
在Windows操作系统中,进程间通信(IPC,Interprocess Communication)是一种技术,允许不同的进程之间共享数据、协调工作或交换信息。这种技术对于多线程和多进程应用的开发至关重要,尤其是在分布式系统和并发编程...
在Windows操作系统中,进程间通信(Inter-Process Communication, IPC)是一种允许不同进程之间交换数据和协调工作的技术。它是多任务环境下程序设计的关键部分,使得应用程序可以共享资源、协同工作,甚至实现...
### Linux共享内存实现进程间通信详解 #### 一、引言 在Linux系统中,进程间的通信(Inter-Process Communication, IPC)是一项重要的技术,它允许不同进程之间交换数据和同步执行。其中一种高效的进程间通信方法是...
### 全面深入的学习进程间通信机制之一:信号量 #### 一、信号量概述 **信号量**是一种用于管理进程间对共享资源访问的重要机制,尤其在操作系统中扮演着核心角色。它能够确保资源的一致性和完整性,避免了并发...
俺花了N个大洋买来的,现在免费提供给大家
第1章 UNIX基础知识 第2章 UNIX标准化及实现 ...第十四章 进程间通信 第十五章 高级进程间通信 第十六章 一个数据库函数库 第十七章 与PostScript打印机通信 第十八章 调制解调器拨号器 第十九章 伪终端 附录
消息队列作为一种高效的进程间通信机制,在Linux和Unix环境中被广泛应用。通过合理配置消息队列的创建、消息的发送与接收,开发人员能够轻松实现进程间的高效协作,同时确保数据处理的安全性和一致性。无论是对于...
ARM7~ARM9体系结构体系结构介绍 ARM7(9)TDMI处理器内核...进程间通信: 进程通信的基本概念,管道、信号、消息队列、信号量、共享内存。 网络通讯接口,socket通信编程。 串口通讯程序和编程实践 多线程程序设计 等。
进程间通信(IPC,Inter-Process Communication)是进程间交换信息的方式,包括管道、信号量、消息队列、共享内存、套接字等机制。 七、进程调度 Linux采用多种调度算法,如轮转调度(Round Robin)、优先级调度...
Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...
Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元...
Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...
Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...
Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输...
通过以上实验,学生能够全面地理解和掌握Linux操作系统的基本命令、编程技巧、进程间通信、内存管理、设备驱动以及网络编程等多个方面的知识和技术,为进一步深入研究和应用Linux操作系统奠定坚实的基础。
该程序是我写的博客“一起talk C栗子吧(第九十八回:C语言实例--使用消息队列进行进程间通信二)”的配套程序,共享给大家使用
其中,管道适用于父子进程间通信,消息队列提供了消息的存储和传递,信号用于发送简单的事件通知,共享内存允许进程直接读写同一块内存区域,套接字则适用于网络环境下的进程通信。 六、死锁 在多进程环境中,如果...