int pipe(int filedes[2])函数使用
#include <stdio.h>
#include #include <string.h> #include <sys/types.h> int main() { pid_t pid = 0; int fds[2], nwr = 0; char buf[128];
pipe(fds);
pid = fork(); if(pid < 0) { printf("Fork error.\n"); return -1; }else if(pid == 0) { printf("This is child process, pid = %d\n", getpid()); #if 0
//part A printf("Child:waiting for message...\n"); nwr = read(fds[0], buf, sizeof(buf)) printf("Child:received\"%s\"\n", buf); #endif #if 0
//part B printf("Child:send reply\n"); strcpy(buf, "Reply from child"); nwr = write(fds[1], buf, sizeof(buf)); printf("Child:send %d bytes to parent.\n", nwr); #endif }else{ printf("This is parent process, pid = %d\n", getpid());
printf("Parent:sending message...\n"); strcpy(buf, "Message from parent"); nwr = write(fds[1], buf, sizeof(buf)); printf("Parent:send %d bytes to child.\n", nwr); #if 1
//part C printf("Parent:waiting for reply from child...\n"); nwr = read(fds[0], buf, sizeof(buf)); printf("Parent:received \"%s\" from child\n", buf); #endif } return 0; }
|
《1》如果part A、part B和part C部分全部打开,子进程无法收到父进程发送的信息;
《2》如果part A、part B关闭,part C打开,那么父进程发送的信息被自己接收;
《3》如果part A打开,part B和part C关闭,那么父进程发送的信息被子进程接收;
比较常见的用法如下
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> int main() { pid_t pid = 0; int fds[2]; char buf[128]; int nwr = 0;
pipe(fds);//should before fork()
pid = fork(); if(pid < 0) { printf("Fork error.\n"); return -1; }else if(pid == 0) { printf("This is child process, pid = %d\n", getpid()); printf("Child:waiting for message...\n"); close(fds[1]); nwr = read(fds[0], buf, sizeof(buf)); printf("Child:received\"%s\"\n", buf); }else{ printf("This is parent process, pid = %d\n", getpid()); printf("Parent:sending message...\n"); close(fds[0]); strcpy(buf, "Message from parent"); nwr = write(fds[1], buf, sizeof(buf)); printf("Parent:send %d bytes to child.\n", nwr); } return 0; }
|
下图可能会助于理解为什么要pipe两次才能实现父子进程间的双向通信
----------------- ----------------- -----------------
|Parent | | inode | |Child |
|Readfd |<------| |----->|Readfd |
|Writefd |------>| |<-----|Writefd |
----------------- ----------------- -----------------
FILE *popen(const char *command, const char *type)函数使用
The return value from popen() is a normal standard I/O stream in all respects save that it must be closed with pclose() rather than fclose(). Writing to such a stream writes to the standard input of the command; the command's standard out-
put is the same as that of the process that called popen(), unless this is altered by the command itself. Conversely, reading from a ``popened'' stream reads the command's standard output, and the command's standard input is the same as
that of the process that called popen().
下面是一个从网上看来的例子,稍微改了一下
UpperToLower.c
#include <ctype.h> #include <stdio.h>
int main() { int c;
while(( c = getchar()) != EOF) { if (isupper(c)) c = tolower(c); if (putchar(c) == EOF) perror("output error"); if (c == '\n') fflush(stdout); } }
|
main.c
#include <sys/wait.h> #include <stdio.h> #include <fcntl.h> int main() { char line[128]; FILE *fpin;
if ((fpin = popen("./UpperToLower", "r")) == NULL) perror("popen error");
for (; ;) { fputs("prompt>; ", stdout); fflush(stdout); if (fgets(line, 128, fpin) == NULL) break; if (fputs(line, stdout) == EOF) perror("fputs error to pipe"); } if (pclose(fpin) == -1) perror("pclose error");
putchar('\n');
return 0; }
|
运行后输出如下
./a.out
prompt>; pppp
pppp
prompt>; TTTT
tttt
prompt>; asTcHG
astchg
prompt>;
main.c通过popen()+read方式调用UpperToLower.c,被调用的进程UpperToLower标准输出被重定向到main.c中的FILE指针,标准输入继承了调用popen进程的标准输入.所以整个流程是这样:main.c输出提示符"prompt>;",然后用户输入字符,UpperToLower进程读取输入的字符进行处理,处理结果通过管道的方式返回给main.c,main.c显示处理结果
int mkfifo(const char *pathname, mode_t mode)函数使用
#include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h>
int main() { char * path = "/home/jsun/tmp.fifo"; int fd = -1; char buf[128]; pid_t pid = 0;
unlink(path); mkfifo(path, 0777); pid = fork();
if(pid > 0) { char * mess = "message from parent"; fd = open(path, O_RDWR); printf("parent: fd = %d\n", fd); write(fd, mess, strlen(mess)); printf("parent: write \"%s\" to child ok\n", mess); }else if(pid == 0) { fd = open(path, O_RDWR); printf("child: fd = %d\n", fd); read(fd, buf, sizeof(buf)); printf("child: read done:\"%s\"\n", buf); } unlink(path); return 0; }
|
程序输出如下
child: fd = 3
parent: fd = 3
parent: write "message from parent" to child ok
child: read done:"message from parent"
在使用FIFO进行通信时还有阻塞和非阻塞的问题,引用man 7 fifo如下
NAME
fifo - first-in first-out special file, named pipe
DESCRIPTION
A FIFO special file (a named pipe) is similar to a pipe, except that it is accessed as part of the file system. It can be opened by multiple processes for reading or writing. When processes are exchanging data via the FIFO, the kernel passes all data internally without writing it to the file system. Thus, the FIFO special file has no contents on the file system, the file system entry merely serves as a reference point so that processes can access the pipe using a name in the file system.
The kernel maintains exactly one pipe object for each FIFO special file that is opened by at least one process. The FIFO must be opened on both ends (reading and writing) before data can be passed. Normally, opening the FIFO blocks until the other end is opened also.
A process can open a FIFO in non-blocking mode. In this case, opening for read only will succeed even if no-one has opened on the write side yet; opening for write only will fail with ENXIO (no such device or address) unless the other end has already been opened.
Under Linux, opening a FIFO for read and write will succeed both in blocking and non-blocking mode. POSIX leaves this behaviour undefined. This can be used to open a FIFO for writing while there are no readers available. A process that uses both ends of the connection in order to communicate with itself should be very careful to avoid deadlocks.
NOTES
When a process tries to write to a FIFO that is not opened for read on the other side, the process is sent a SIGPIPE signal.
FIFO special files can be created by mkfifo(3), and are specially indicated in ls -l.
关于阻塞和非阻塞有如下测试代码
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h>
#define FIFO1 "/tmp/fifo1" #define MODE (S_IRWXU | S_IRWXO | S_IRWXG)
int main() { int rfd, wfd; if ((mkfifo(FIFO1, MODE) < 0) && errno != EEXIST) { return -1; }
printf("000000\n"); #ifdef READONLY rfd = open(FIFO1, O_RDONLY, 0); printf("111111\n"); #endif
#ifdef WRITEONLY rfd = open(FIFO1, O_WRONLY, 0); printf("111111\n"); #endif
#ifdef READONLY_NB rfd = open(FIFO1, O_RDONLY | O_NONBLOCK, 0); printf("111111\n"); #endif
#ifdef WRITEONLY_NB rfd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0); printf("111111\n"); #endif
#ifdef READWRITE rfd = open(FIFO1, O_RDWR, 0); printf("111111\n");
wfd = open(FIFO1, O_RDONLY, 0); printf("222222\n", wfd); #endif
#ifdef READWRITE1 rfd = open(FIFO1, O_RDWR, 0); printf("111111\n");
wfd = open(FIFO1, O_WRONLY, 0); printf("222222\n"); #endif
#ifdef TEST1 rfd = open(FIFO1, O_RDONLY | O_NONBLOCK, 0); printf("111111\n");
wfd = open(FIFO1, O_WRONLY, 0); printf("222222\n"); #endif
#ifdef TEST2 rfd = open(FIFO1, O_WRONLY | O_NONBLOCK, 0); printf("111111\n");
wfd = open(FIFO1, O_RDONLY, 0); printf("222222\n"); #endif
return 0; }
|
大概总结一下:
1 如果有O_NONBLOCK标志那么open不会阻塞
2 如果有O_RDWR标志那么open不会阻塞
3 只有O_RDONLY或者O_WRONLY标志时会阻塞,直到管道的另一端有进程打开.例如进程A以O_RDONLY方式open,那么A会阻塞;此时如果进程B以O_WRONLY方式open,那么进程A,B都会正常返回.
4 比较奇怪的是TEST1和TEST2,TEST1中代码不会出现阻塞现象,但是TEST2中代码第二个open会阻塞.按照最初的设想,如果管道的一端被打开,那么另一端被打开时就不会阻塞,为什么TEST2中第二个open会阻塞呢?
阅读(1028) | 评论(0) | 转发(0) |