本文是对前文的一个整理,使代码的编写更加高效以及移植的可重用性变高。
详细可见上文:http://blog.csdn.net/gubenpeiyuan/article/details/10465721
这篇文章里面详细讲述了,Linux进程通信的创建方法,管理方法,以及例举了通信方法。
但是其中的实例,比较杂乱,在你的大项目里面移植时,需要花上你一些功夫了。
这篇文章存在的目的就是尽量减少因为移植而带来的不必要的麻烦,还有就是加强了工程的可维护性。不多说了。上代码。
主函数:
/*----------------------------------------------
* 作者:LEOLUO
* 时间:2013-11-10
* email:leo_luopy@139.com
* 功能:进程间管道通信/队列通信/共享内存通信,移植Demo
----------------------------------------------*/
#include "Pipe_Communication/PipeFun.hpp"
/*----------------------------------------------------
* 函数名:创建子进程
* 功能:创建子进程执行
* 返回:子进程进程号
* 输入:子进程入口函数,函数指针
----------------------------------------------------*/
int CreateSubProcess(int (*subpro)())
{
pid_t pid;
if((pid = fork()) == -1) {
printf("Error in fork\n!\n!\n!\n");
exit(1);
}
if(pid == 0) {
printf("spawning sub process \n");
(*subpro)();
exit(0);
}
return pid ;
}
int main()
{
char buf[256];
/*创建无名管道*/
pipe(pipe_descriptors);
CreateSubProcess(&SubPipeProcess);
printf("in the spawning (parent) process...\n");
printf("parent sleep 4s \n");
sleep(4);
printf("%d bytes of data received from spawned process: %s\n",
ClosePipeToRead(buf,256), buf);
return 0 ;
}
进程创建了子进程,并和子进程进行了简单的通信,拷贝代码跑一跑就知道是怎么回事了。祝你好运。头文件和相关的函数见下。
PipeFun.cpp
/*----------------------------------------------
* 作者:LEOLUO
* 时间:2013-11-10
* email:leo_luopy@139.com
* 功能:进程间管道通信
----------------------------------------------*/
#include "PipeFun.hpp"
int pipe_descriptors[2];
int SubPipeProcess()
{
printf("in the spawned (child) process...\n");
printf("sleep 2s\n");
sleep(2);
/*子进程向父进程写数据,关闭管道的读端*/
ClosePipeToWrite((char*)"test data",10);
printf("child exited \n");
return 0 ;
}
void ClosePipeToWrite(char *Sendbuf , int LenOfSendbuf)
{
close(pipe_descriptors[INPUT]);
write(pipe_descriptors[OUTPUT],Sendbuf, LenOfSendbuf);
}
int ClosePipeToRead(char *Recbuf,int LenOfRecbuf)
{
/*父进程从管道读取子进程写的数据,关闭管道的写端*/
close(pipe_descriptors[OUTPUT]);
return read(pipe_descriptors[INPUT], Recbuf, LenOfRecbuf);
}
PipeFun.hpp
/*----------------------------------------------
* 作者:LEOLUO
* 时间:2013-11-10
* email:leo_luopy@139.com
* 功能:进程间管道通信
----------------------------------------------*/
#ifndef PIPEFUN_HPP_
#define PIPEFUN_HPP_
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define INPUT 0
#define OUTPUT 1
int SubPipeProcess();
void ClosePipeToWrite(char *Sendbuf , int LenOfSendbuf);
int ClosePipeToRead(char *Recbuf,int LenOfRecbuf) ;
extern int pipe_descriptors[2];
#endif /* PIPEFUN_HPP_ */
在这个示例中,父子进程他们共有了一个管道描述符pipe_descriptor,子进程函数入口没有使用参数的方式将这个描述符传入进去,这样的好处是,降低了因为以后需要扩展的添加的不必要的编程负担。如果是C工程,使用公有数据结构即可。如果是Cpp工程,把他们划归统一类的私有数据成员即可。
signal(SIGCHLD,SIG_IGN);
FIFO相关函数整理 直接套用:
if ((mkfifo(m_RtspChannelFifoName,S_IRWXU) < 0) && (errno != EEXIST)) //如果该fifo文件不存在,创建之
{
printf("mkfifo error\n");
} else{printf("mkfifo OK\n");}
//O_RDONLY O_WRONLY O_NONBLOCK
if ((m_RtspFIFOHandle = open(m_RtspChannelFifoName,O_WRONLY | O_NONBLOCK)) < 0) //非阻塞方式打开
{
printf("open error\n");
}else{
printf("FIFO open OK! Handle at %d \n",m_RtspFIFOHandle);
}
附文:
PIPE
概述:
int pipe(int pipefd[2]);
调用pipe函数在内核中开辟一块缓冲区(称为管道)用于单向通信,它有一个读端一个写端,然后通过filedes参数传给用户程序两个文件描述符,filedes[0]指向PIPE的读端,filedes[1]指向PIPE的写端。所以在用户程序看起来就像一个打开的文件,通过read(filedes[0]);
或者write(filedes[1]); 向这个文件读写数据其实是在读写内核缓冲区。
创建PIPE的基本步骤:
• 父进程调用pipe 开辟PIPE,得到两个文件描述符指向管道的两端。
• 父进程调用fork 创建子进程,那么子进程也有两个文件描述符指向同一管道。
• 父进程关闭管道读端,子进程关闭管道写端。父进程可以往PIPE里写,子进程可以从PIPE里读,PIPE是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
int
main()
{
char
buf[20];
pid_t pid;
int
fd[2];
int
n;
pipe(fd);
//创建管道
if
((pid = fork()) < 0)
//fork子进程
{
perror
(
"fork error"
);
exit
(-1);
}
else
if
(pid == 0)
//子进程中
{
close(fd[1]);
//关闭写端
n = read(fd[0],buf,20);
write(STDOUT_FILENO,buf,n);
close(fd[0]);
//关闭读端
exit
(0);
}
else
//父进程中
{
close(fd[0]);
//关闭读端
write(fd[1],
"hello world"
,
strlen
(
"hello world"
));
close(fd[1]);
//关闭写端
waitpid(pid,NULL,0);
exit
(0);
}
}
|
popen函数与pclose函数
标准IO函数库提供了popen函数,它创建一个管道并启动另外一个进程,该进程从该PIPE读出标准输入或将标准输出写入该PIPE。
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函数:先执行fork,然后调用exec(sh)以执行command,并且返回一个标准I/O文件指针。(错误返回NULL)
如果type是”r”,则文件指针连接到command的标准输出,(该进程为读段,command所指进程为写端),参数”w”同理.
This command is passed to /bin/sh using the -c flag;
pclose函数:关闭由popen创建的标准I/O流,等待命令执行结束,然后返回shell的终止状态(错误返回-1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#define PAGER "${PAGER:-more}" //如果shell变量PAGER已经定义而且非空,则使用其值,否则使用字符串more
int
main()
{
char
line[100];
FILE
*fpin,*fpout;
if
((fpin =
fopen
(
"A.txt"
,
"r"
)) == NULL)
{
perror
(
"can't open A.txt"
);
exit
(-1);
}
if
((fpout = popen(PAGER,
"w"
)) == NULL)
{
perror
(
"popen error"
);
exit
(-1);
}
while
(
fgets
(line,100,fpin) != NULL)
{
if
(
fputs
(line,fpout) == EOF)
{
perror
(
"fputs error to pipe"
);
exit
(-1);
}
}
if
(pclose(fpout) == -1)
perror
(
"pclose error"
);
exit
(0);
}
|
FIFO
FIFO即是命名PIPE,文件系统中有个路径名与之关联。PIPE只能由有亲缘关系的进程使用,它们共同的祖先进程创建了管道。但是,通过FIFO,不相关的进程也能交换数据
创建FIFO
int mkfifo(const char *pathname, mode_t mode);
mode为存取许可权(需结合进程的umask).一般的文件I/O函数都可以用于FIFO
mkfifo函数已经隐含指定O_CREAT | O_EXCL,也就是说,要么创建一个新的FIFO,要么返回EEXIST错误(文件已经存在)
删除FIFO
int unlink(const char *pathname);
不同于PIPE,FIFO只有通过unlink才能从文件系统中删除
打开FIFO
int open(const char *pathname, int flags);
使用open函数打开FIFO,默认情况下没有指定O_NONBLOCK标志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
//fifo_write.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
#define FIFO_FILE "/tmp/myfifo"
int
main()
{
int
fd = 0;
int
n;
char
buf[100];
if
((fd = open(FIFO_FILE,O_WRONLY | O_NONBLOCK)) < 0)
//非阻塞方式打开
{
perror
(
"open error"
);
exit
(-1);
}
while
(1)
{
fgets
(buf,100,stdin);
n =
strlen
(buf);
if
((n = write(fd,buf,n)) < 0)
{
if
(
errno
== EAGAIN)
printf
(
"The FIFO has not been read yet.Please try later\n"
);
}
}
return
0;
}
//fifo_read.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#define FIFO_FILE "/tmp/myfifo"
int
main()
{
char
buf[100];
int
n = 0;
int
fd;
if
((mkfifo(FIFO_FILE,S_IRWXU) < 0) && (
errno
!= EEXIST))
//如果该fifo文件不存在,创建之
{
perror
(
"mkfifo error"
);
exit
(-1);
}
if
((fd = open(FIFO_FILE,O_RDONLY | O_NONBLOCK)) < 0)
//非阻塞方式打开
{
perror
(
"open error"
);
exit
(-1);
}
while
(1)
{
if
((n = read(fd,buf,100)) < 0)
{
if
(
errno
== EAGAIN)
{
printf
(
"No data yet\n"
);
}
}
else
write(STDOUT_FILENO,buf,n);
sleep(1);
//sleep
}
unlink(FIFO_FILE);
return
0;
}
|
FIFO与PIPE的读写:
(1)对于PIPE或FIFO的write总是往末尾添加数据,对他们的read则总是从开头返回数据。如果对PIPE或FIFO调用lseek,那就返回ESPIPE错误
(2)一个文件描述符能以2中方式设置成非阻塞:(默认为阻塞)
• 调用open是可指定O_NONBLOCK标志
• 如果文件描述符已经打开,那么可以调用fcntl设置O_NONBLOCK标志(PIPE只能采用这种方式)
(3)读写规则:
阻塞(缺省设置):
只读open
• FIFO已经被只写打开:成功返回
• FIFO没有被只写打开:阻塞到FIFO被打开来写
只写open
• FIFO已经被只读打开:成功返回
• FIFO没有被只读打开:阻塞到FIFO被打开来读
从空PIPE或空FIFO中read
• FIFO或PIPE已经被只写打开:阻塞到PIPE或FIFO中有数据或者不再为写打开着
• FIFO或PIPE没有被只写打开:返回0(文件结束符)
write
• FIFO或PIPE已经被只读打开:
写入数据量不大于PIPE_BUF(保证原子性):有足够空间存放则一次性全部写入,没有则进入睡眠,直到当缓冲区中有能够容纳要写入的全部字节数时,才开始进行一次性写操作
写入数据量大于PIPE_BUF(不保证原子性):缓冲区一有空闲区域,进程就会试图写入数据,函数在写完全部数据后返回
• FIFO或PIPE没有被只读打开:给线程产生SIGPIPE(默认终止进程)
O_NONBLOCK设置:
只读open
• FIFO已经被只写打开:成功返回
• FIFO没有被只写打开:成功返回
只写open
• FIFO已经被只读打开:成功返回
• FIFO没有被只读打开:返回ENXIO错误
从空PIPE或空FIFO中read
• FIFO或PIPE已经被只写打开:返回EAGAIN错误
• FIFO或PIPE没有被只写打开:返回0(文件结束符)
write
• FIFO或PIPE已经被只读打开:
写入数据量不大于PIPE_BUF(保证原子性):有足够空间存放则一次性全部写入,没有则返回EAGAIN错误(不会部分写入)
写入数据量大于PIPE_BUF(不保证原子性):有足够空间存放则全部写入,没有则部分写入,函数立即返回
• FIFO或PIPE没有被只读打开:给线程产生SIGPIPE(默认终止进程)
PIPE或FIFO若干额外的规则:
• 如果请求读取的数据量多余当前可用的数据量,那么返回这些可用的数据
• 如果请求写入的数据字节数小于或等于PIPE_BUF,那么write操作保证是原子的(O_NONBLOCK标志的设置对原子性没有影响)
• 当对PIPE或FIFO最后一个关闭时,仍在该PIPE或FIFO上的数据将被丢弃
FIFO与PIPE的限制:
• 它们是半双工的(单向性),即数据只能在一个方向上流动。由进程A流向进程B或由进程B流向进程A。
• PIPE的读写端通过打开的文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那里继承PIPE文件描述符。FIFO可以实现无关进程间的通信。
• 一个进程在任意时刻打开的最大文件描述符个数OPEN_MAX(通过调用sysconf(_SC_OPEN_MAX)获得)
• 可原子地写往PIPE或FIFO的最大数据量PIPE_BUF(通常定义在limits.h)
小结:
• PIPE普遍用于SHELL中,不过也可以从程序中使用,往往是从子程序向父程序回传信息。使用PIPE时涉及的某些代码(pipe、fork、close、exec和waitpid)可通过使用popen和pclose来避免,由它们处理具体细节并激活一个shell
• FIFO与管道类似,但他们是用mkfifo创建的,之后需要用open打开。打开管道时必须小心,因为有许多规则制约着open的阻塞与否(甚至发生死锁)