IPC-管道
内容提要:
- 管道简介
- 使用无名管道实现一个简单的本地文件服务器
- 使用标准I/O函数库提供的管道实现
- 使用popen实现本地文件服务器
- 有名管道-FIFO
- 使用FIFO改写本地文件服务器
- 管道和FIFO的额外属性
- 使用FIFO将本地文件服务器改写成单服务器多客户端
- FIFO和NFS的关系
- 字节流和消息
- 使用自定义的结构化接口改写单服务多客户端程序
- 管道和FIFO的限制
1.管道简介
Unix中的进程间通信方式之一是通过管道实现的,管道分为有名管道和无名管道,对于有名管道FIFO,可以实现没有亲缘关系的进程间通信,而对于无名管道,可以实现父子进程间的通信。
管道这种IPC存在的意义是为了实现进程间消息的传递。无名管道是Unix最初的IPC形式,但是由于无名管道的局限性,后来出现了有名管道FIFO,这种管道由于可以在文件系统中创建一个名字,所以可以被没有亲缘关系的进程访问。
管道打开后的标识是以文件描述符的形式提供的,可以使用Unix系统中的read和write系统调用访问。
管道的实现形式有多种,在一些系统中,管道被实现为全双工的,在管道的一端既可以读也可以写,但是Posix.1和Unix 98只要求半双工管道,在Linux系统中,管道是半双工的。
IPC类型 |
持续性 |
用于打开或创建IPC的名字空间 |
IPC打开后的标识 |
fork, exec和exit对IPC对象的影响 |
||
fork |
exec |
_exit |
||||
管道 |
随进程 |
没有名字 |
描述符 |
子进程取得父进程的所有打开着的描述符的副本 |
所有打开着的描述符继续打开着,除非已经设置描述符的FD_CLOEXEC位 |
关闭所有打开着的描述符,最后一个关闭时删除管道或FIFO中残留的所有数据 |
FIFO |
路径名 |
Unix中的无名管道是通过 pipe 函数创建的,该函数创建了一个半双工的管道。
#include <unistd.h> int pipe(int fd[]); 返回值:成功返回0,出错返回-
函数通过参数fd[2]返回两个描述符,fd[0]表示管道的读端,fd[1]表示管道的写端。
管道一般是由一个父进程创建,然后被用来在父子进程间进行通信:
在父子进程通过管道进行通信的程序中,一般在父进程中先创建一个管道,然后 fork 出一个子进程,然后在两个进程中关闭不写和不读的两端。
由于Unix中的管道默认实现是单向的,为了实现双向的,可以用两个单向的管道模拟:
2.使用无名管道实现一个简单的本地文件服务器
使用两个管道实现一个客户端-服务器程序,客户端向服务器请求文件,服务器向客户端输出请求文件的内容:
main函数:
/*
* use pipo to communicate between server and client
* */ #include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h> #define _err(msg) \
{\
fprintf(stderr, "error : %s -- %s\n", msg, strerror(errno)); \
exit(-); \
} #define MAX_SIZE 1024 int
main(void)
{
int fd1[], fd2[];
pid_t pid; pipe(fd1);
pipe(fd2); if((pid = fork()) < )
_err("fork fail");
if(pid > )
{
/*parent*/
close(fd1[]);
close(fd2[]);
server(fd1[], fd2[]);
printf("server stoped\n");
waitpid(pid, NULL, );
}
else
{
close(fd1[]);
close(fd2[]);
client(fd2[], fd1[]);
printf("client stoped\n");
exit();
}
return ;
}
client 函数:
void
client(int readfd, int writefd)
{
char buffer[MAX_SIZE];
size_t size;
printf("client is start ...\n");
printf("input the filename : ");
fgets(buffer, MAX_SIZE, stdin);
size = strlen(buffer);
if(buffer[size - ] == '\n')
size--;
if(write(writefd, buffer, size) <= )
_err("write to server fail");
while((size = read(readfd, buffer, MAX_SIZE)) > )
{
buffer[size] = '\0';
printf("%s", buffer);
fflush(stdout);
}
close(readfd);
close(writefd);
}
server函数:
void
server(int readfd, int writefd)
{
char buffer[MAX_SIZE];
int fd;
size_t size;
printf("server start ...\n");
if((size = read(readfd, buffer, MAX_SIZE)) <= )
_err("read pipe fail");
buffer[size] = '\0';
if(access(buffer, F_OK | R_OK) == -)
{
strcpy(buffer, "file is not exists or can not be read");
if(write(writefd, buffer, strlen(buffer)) <= )
_err("test file fail");
}
else
{
printf("file name = %s\n", buffer);
if((fd = open(buffer, O_RDONLY)) == -)
_err("open file fail");
while((size = read(fd, buffer, MAX_SIZE)) > )
{
if(write(writefd, buffer, size) <= )
_err("write to client fail");
}
close(fd);
}
close(writefd);
close(readfd);
}
3. 使用标准I/O函数库提供的管道实现
标准I/O函数库提供了另一种使用管道的形式,标准函数库通过 popen 函数创建一个管道,并且启动另外一个进程,该进程可以读或写这个管道,可以在使用popen打开时指定是从管道中读还是写。
使用popen函数创建的进程,如果在调用popen的时候指定了'r',则该进程将标准输出写到管道中,如果指定了'w',则将从管道中读出的数据作为标准输入。
#include <stdio.h> FILE *popen(const char *command, const char *type); 返回:成功返回文件指针,出错返回NULL int pclose(FILE *stream); 返回:成功返回shell的终止状态,出错返回-
其中的command是一个shell命令,该命令是使用sh程序处理的,所以可以通过PATH环境变量定位到该命令。popen函数在调用进程和指定的命令之间创建一个管道,由popen函数返回一个标准I/O 文件指针,该指针用于输入输出,具体取决于参数type:
- 如果type为r,那么调用进程读command的标准输出
- 如果type为w,那么调用进程写到command的标准输入
pclose函数关闭popen函数创建的I/O流,等待其中的命令终止,然后返回shell的终止状态。
4.使用popen实现本地文件服务器
/*
* file server implementation by popen
* */ #include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h> #define MAX_SIZE 1024
#define _err(msg) \
{\
fprintf(stderr, "Error: %s -- %s\n", msg, strerror(errno)); \
exit(-); \
} int
main(void)
{
char filename[MAX_SIZE];
char buffer[MAX_SIZE];
char cmd[];
FILE *file;
int len; printf("Input the filename > ");
fgets(filename, sizeof(filename), stdin);
if(strcmp(filename, "") == )
_err("file name is illegal");
len = strlen(filename);
if(filename[len-] == '\n')
filename[len-] = '\0';
snprintf(cmd, sizeof(cmd), "cat %s", filename);
if((file = popen(cmd, "r")) == NULL)
_err("popen fail");
while(fgets(buffer, sizeof(buffer), file) != NULL)
printf("%s", buffer);
return ;
}
5. 有名管道-FIFO
无名管道由于没有名字,所以只能用于具有共同祖先进程的各个进程之间,无法用于在没有亲缘关系的进程之间创建无名管道(除非将管道的描述符保存在一个文件中被另一个进程使用),这造成了无名管道使用的局限性。
FIFO(first-in-first-out)在Unix中的行为类似于无名管道,它是一个单向的数据流,不同于管道,FIFO和一个和路径名关联的,这使得FIFO可以用于不具有亲缘关系的进行之间,只要知道路径名就可以访问该FIFO,FIFO所以又称为有名管道。
FIFO使用mkfifo函数创建:
#include <sys/types.h>
#include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 返回:成功返回0,出错返回-
其中pathname是一个普通的Unix路径名,它是该FIFO在文件系统中的名字。
mode参数指定了文件访问权限位,类似于open函数的mode参数,这些访问权限位定义在<sys/stat.h>头文件中。
mkfifo函数已经隐含指定了O_CREAT | O_EXCL ,所以调用mkfifo函数的时候,要么创建了一个新的FIFO,要么返回一个EEXIST错误表示创建的这个文件已经存在。
在创建一个FIFO后,必须使用读打开或者使用写打开,不能使用读写方式打开,因为FIFO是半双工的。打开一个FIFO可以使用Unix的open系统函数,也可以使用标准I/O函数库的fopen函数打开。
在对一个管道或FIFO进行write写的时候,总是将写的数据添加到尾部,对于他们的read读总是从头部开始,所以不能对一个管道或FIFO调用lseek,会返回ESPIPE错误。
6.使用FIFO改写本地文件服务器
main函数:
/*
* a file server use fifo as a IPC
* */ #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h> #define MAX_SIZE 1024
#define FIFO_FILE_1 "/tmp/fifo_1"
#define FIFO_FILE_2 "/tmp/fifo_2" #define _err(msg) \
{\
fprintf(stderr, "Error : %s -- %s\n", msg, strerror(errno));\
exit(-); \
} int
main(void)
{
int sfdread, sfdwrite, cfdread, cfdwrite;
pid_t pid; if(mkfifo(FIFO_FILE_1, O_CREAT | O_EXCL) < && errno != EEXIST)
_err("make or visit a fifo fail");
if(mkfifo(FIFO_FILE_2, O_CREAT | O_EXCL) < && errno != EEXIST)
_err("make or visit a fifo fail"); if((pid = fork()) < )
_err("fork fail");
if(pid)
{
/*parent*/
if((sfdread = open(FIFO_FILE_1, O_RDONLY)) < )
_err("open a fifo fail");
if((sfdwrite = open(FIFO_FILE_2, O_WRONLY)) < )
_err("open a fifo file"); server(sfdread, sfdwrite);
waitpid(pid, NULL, );
close(sfdwrite);
close(sfdread);
}
else
{
/*child*/
if((cfdwrite = open(FIFO_FILE_1, O_WRONLY)) < )
_err("open a fifo file");
if((cfdread = open(FIFO_FILE_2, O_RDONLY)) < )
_err("open a fifo fail"); client(cfdread, cfdwrite);
close(cfdwrite);
close(cfdread);
}
return ;
}
client 函数
void
client(int fdread, int fdwrite)
{
char filename[MAX_SIZE];
char buffer[MAX_SIZE];
size_t size; printf("input the filename > ");
fgets(filename, sizeof(filename), stdin);
size = strlen(filename);
if(filename[size - ] == '\n')
filename[size - ] = '\0'; if(write(fdwrite, filename, size) <= )
_err("write to server fail"); while((size = read(fdread, buffer, sizeof(buffer))) > )
{
write(STDOUT_FILENO, buffer, size);
}
exit();
}
server 函数
void
server(int fdread, int fdwrite)
{
char filename[MAX_SIZE];
char buffer[MAX_SIZE];
int fd;
size_t size; if((size = read(fdread, filename, sizeof(filename))) <= )
_err("can not get the file name"); if(access(filename, F_OK | R_OK) == -)
_err("test file fail");
if((fd = open(filename, O_RDONLY)) == -)
_err("open file fail"); while((size = read(fd, buffer, sizeof(buffer))) > )
if(write(fdwrite, buffer, size) <= )
_err("fail to write to client");
exit();
}
7. 管道和FIFO的额外属性
一个文件描述符可以通过两种方式设置成非阻塞:
- 调用open时可以指定O_NONBLOCK标识
- 如果一个描述符已经打开,则可以使用fcntl函数设置描述符的O_NONBLOCK标识
对于管道来说,由于管道是通过pipe函数创建的,不能使用open函数打开,所以不能使用open方式设置O_NONBLOCK标识,只能使用fcntl函数设置描述符的O_NONBLOCK标识,而对于FIFO来说,FIFO的打开可以使用open实现的,所以对于FIFO设置O_NONBLOCK标识可以使用open也可以使用fcntl实现。
打开了O_NONBLOCK标识的描述符对FIFO和管道会有影响:
当前操作 |
管道或FIFO的现有打开状态 |
返回 |
|
阻塞(默认设置) |
O_NONBLOCK设置 |
||
只读方式open FIFO |
FIFO以写方式打开 |
成功返回 |
成功返回 |
FIFO 不是以写方式打开 |
阻塞到FIFO打开来写为止 |
成功返回 |
|
只写方式open FIFO |
FIFO以读方式打开 |
成功返回 |
成功返回 |
FIFO不是以读方式打开 |
阻塞到FIFO打开来读为止 |
返回ENXIO错误 |
|
从空管道或空FIFO读 |
管道或FIFO以写方式打开 |
阻塞到管道或FIFO中有数据或者管道或FIFO被关闭 |
返回EAGAIN错误 |
管道或FIFO不是以写方式打开 |
read操作返回0(文件结束) |
read操作返回0(文件结束) |
|
向管道或FIFO写 |
管道或FIFO打开来读 |
和PIPE_BUF有关(见下文) |
和PIPE_BUF有关(见下文) |
管道或FIFO不是打开来读 |
给线程产生SIGPIPE信号 |
给线程产生SIGPIPE信号 |
对于管道和FIFO的读出和写入存在如下的一些规则:
- 如果请求读出的数据量多于管道或者FIFO中当前可用数据量,那么只返回这些可用的数据。
- 如果请求写入的数据的字节数小于或等于PIPE_BUF(在<limits.h>头文件中定义),那么write操作保证写入是原子操作,这意味着如果有两进程同时写一个管道或FIFO的时候,写入操作保证只有在一个进程写完所有请求写入的数据后,才会运行另一进行进行写入操作。但是如果进行请求写入的数据PIPE_BUF的值,那么write操作对写入的原子性没有保证。
- O_NONBLOCK标识对写操作的原子性没有影响,写操作的原子性取决于写入数据请求写入的数据是否超过PIPE_BUF。但是设置为O_NONBLOCK标识的管道或FIFO,对其写操作的返回值取决于待写的字节数和管道或FIFO中当前可用的空间的大小:
- 如果待写字节数小于等于PIPE_BUF:
- 如果该管道或FIFO中有足以存放请求数据的空间,那么所有的数据都将原子地写入。
- 如果该管道或FIFO中没有足够的空间存放请求的数据,那么不写入数据并且立即返回一个EAGAIN错误。因为设置了O_NONBLOCK标识,调用进程不希望自己被阻塞,但是内核无法在接受部分数据的时候保证write操作的原子性,所以会返回一个错误,告诉进程下一次在尝试
- 如果待写的数据大于PIPE_BUF:
- 如果管道或FIFO中至少有1字节空间,那么内核写入该管道或FIFO当前可以容难的数据,写入的数据量将作为写操作的返回值返回。
- 如果该管道或FIFO已满,则写操作立即返回一个EAGAIN错误
- 如果向一个没有为读而打开着的管道或FIFO写入,那么内核将产生一个SIGPIPE信号:
- 如果调用进程没有捕捉也没有忽略SIGPIPE信号,则采取的默认行为是终止该进程
- 如果调用进程忽略了SIGPIPE信号,或者捕获了该信号并从其信号处理程序中返回,那么write返回一个EPIPE错误
8.使用FIFO将本地文件服务器改写成单服务器多客户端
FIFO的优势表现在服务器可以是一个长期运行的进程(如守护进程),而且可以与其客户端可以没有亲缘关系。
对于服务器所在的进程,可以使用一个众所周知的路径名创建一个FIFO,以供所有的客户端进程使用。而对于每一个客户端进程,可以通过服务器提供的FIFO给服务器发送每个客户进程自己创建的FIFO路径名,以实现客户进程和服务进程之间的通信。
服务器实现:
/*
* multiply client connect to the server and communicate use fifo
* */ #include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <error.h> #define FIFO_FILE "/tmp/server_fifo"
#define MAX_SIZE 1024
#define FILE_NAME 256 void _err(char *message)
{
fprintf(stderr, "error %s -- %s\n", message, strerror(errno));
exit(-);
} int main(int argc, char *argv[])
{
int readno, writeno, dummyfd, fd;
char *ptr, buffer[MAX_SIZE], fifoname[FILE_NAME];
pid_t pid;
ssize_t size; if(mkfifo(FIFO_FILE, ) == - && errno != EEXIST)
_err("fifo file create fail"); if((readno = open(FIFO_FILE, O_RDONLY)) < )
_err("fifo file open fail");
if((dummyfd = open(FIFO_FILE, O_WRONLY)) < )
_err("fifo file open fail"); /*never use*/ while((size = read(readno, buffer, sizeof(buffer))) > )
{
if(buffer[size-] == '\n')
buffer[size - ] = , size--;
if((ptr = strchr(buffer, ' ')) == NULL)
_err("message's format is invalid");
*ptr++ = ;
snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%s", buffer);
if((writeno = open(fifoname, O_WRONLY)) < )
_err("open fifo fail");
if((fd = open(ptr, O_RDONLY)) < )
/*tell the client the error message*/
{
snprintf(buffer + size, sizeof(buffer) - size, ": can't open, %s\n", strerror(errno));
size = strlen(buffer);
write(writeno, buffer, size);
close(writeno);
}
else
{
while((size = read(fd, buffer, MAX_SIZE)) > )
write(writeno, buffer, size);
close(fd);
close(writeno);
}
}
exit();
}
客户端实现:
/*
* multipy client to connect to server
* */ #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h> #define MAX_SIZE 1024
#define FIFO_FILE "/tmp/server_fifo"
#define FILE_NAME 256 void _err(char *message)
{
fprintf(stderr, "error: %s -- %s\n", message, strerror(errno));
exit(-);
} int main(int argc, char *argv[])
{
int readno, writeno;
char buffer[FILE_NAME], fifoname[FILE_NAME], *ptr;
pid_t pid;
int fd;
ssize_t size; pid = getpid();
snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%d", pid);
if(mkfifo(fifoname, ) < )
_err("create fifo fail");
snprintf(buffer, sizeof(buffer), "%d ", pid);
printf("Input the file name > ");
size = strlen(buffer);
ptr = buffer + size;
fgets(ptr, MAX_SIZE - size, stdin); if((writeno = open(FIFO_FILE, O_WRONLY)) < )
_err("open fifo fail"); size = strlen(buffer);
if(write(writeno, buffer, size) != size)
_err("write to fifo fail");
if((readno = open(fifoname, O_RDONLY)) < )
_err("open fifo fail");
while((size = read(readno, buffer, sizeof(buffer))) > )
write(STDOUT_FILENO, buffer, size);
close(readno);
close(writeno);
exit();
}
9.FIFO和NFS的关系
FIFO是一种只能在单台主机上使用的IPC形式。尽管在文件系统中有名字,它们也只能用在本地文件系统上,而不能用在通过NFS安装的文件系统上。
虽然有些系统允许在通过NFS安装的文件系统上创建FIFO,但是数据无法在这样的两个系统之间通过这些FIFO传递。这意味着FIFO只能用于处在同一台主机上的客户机和服务器之间使用,即使可以在不同的主机上通过NFS打开同一个FIFO,也不能通过该FIFO进行通信
10.字节流和消息
管道和FIFO都是使用字节流I/O模型的,这是Unix的原生I/O模型。字节流I/O模型不存在记录边界,也就是说读写操作不检查数据。例如:如果从某个FIFO中读出100个字节的进程无法判断向该FIFO进行写入这100个字节数据的进程执行了怎样的操作来写入这100个字节,可能是一次写入了100个字节,也可能是通过写入5次20字节的数据。还可能的情况可能是这100个字节的数据根本来自两个不同的进程。这样的数据没有明确的边界,称为字节流,系统不对这些数据进行解释。
有时候,如果应用程序需要对这些数据加上某种结构,或者希望使用一种数据结构作为传输数据的单位,则需要对这种类型的数据进行处理。有三种方式可以处理这些情况:
- 带内特殊终止序列:很多的Unix应用程序使用换行符来分隔每个消息。写进程会给每一个消息添加一个换行符,读进程则每次读出一行。这中方式一般要求数据中任何出现分隔符的位置都需要进行转义处理。很多的因特网程序(FTP、SMTP、HTTP、NNTP)使用一个回车符后跟一个换行符构成双字符序列来分隔记录。
- 显示长度:每个记录前冠以它的长度,当在TCP上时,Sun PRC也使用这种技巧,这种技巧的优势在于不再需要通过转义出现在数据中的分隔符,因为接收这明确知道了数据的长度。
- 每次连接一个记录:应用通过关闭与其对等端的连接来指示一个记录的结束,这要求为每一个记录都建立一个连接。
标准I/O库可以用于读写一个管道或者FIFO,既然打开一个管道的唯一方法是使用pipe,那么为了创建一个标准I/O流,可以使用标准I/O函数fdopen函数将标准I/O流和一个pipe打开的文件描述符相关联。
也可以创建一个结构化的消息,这种能力是由Posix消息队列和System V消息队列提供的。每个消息都有一个长度和一个优先级(System V中称为类型),长度和优先级通过发送发指定,消息被读出后,接收方可以从消息中获取消息的长度和优先级
类似于消息队列,可以给FIFO或管道人为的增加一些结构,实现结构化的通信。
自定义消息的结构:
/* get the max size of the date */
#define MAXMESGDATE (PIPE_BUF - 2 * sizeof(long)) /* get the size of the mesg_len and mesg_type */
#define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATE) /* message struct */
struct mymesg {
long mesg_len;
long mesg_type;
char mesg_date[MAXMESGDATE];
}; /* error process */
void
_err(char *message)
{
fprintf(stderr, "error:%s -- %s\n", message, strerror(errno));
} /* send message message to fifo */
ssize_t
mesg_send(int fd, struct mymesg *mptr)
{
return write(fd, mptr, MESGHDRSIZE + mptr -> mesg_len);
} /* recive message from fifo */
ssize_t
mesg_recv(int fd, struct mymesg *mptr)
{
size_t len;
ssize_t size; if((size = read(fd, mptr, MESGHDRSIZE)) <= )
_err("recive date error");
if(size != MESGHDRSIZE)
_err("recived date's head is error"); if((len = mptr -> mesg_len) > )
{
if((size = read(fd, mptr -> mesg_date, len)) != len)
_err("read error");
}
return size;
}
11.使用自定义的结构化接口改写单服务多客户端程序
服务器程序:
/*
* server use struct to send and recive message
* */ #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h> #define FIFO_NAME "/tmp/server.fifo"
#define FILE_NAME 256 /* server */
int
main(void)
{
struct mymesg buffer;
size_t size;
char fifo_file_name[FILE_NAME];
char *ptr, fd; int readfd, writefd, dummpy; /* read and write interface */ if(mkfifo(FIFO_NAME, ) < && errno != EEXIST)
_err("fifo file create fail"); if((readfd = open(FIFO_NAME, O_RDONLY)) == -)
_err("open fifo fail"); /* keep the fifo always can be read */
if((dummpy = open(FIFO_NAME, O_WRONLY)) == -)
_err("open file fail"); while((size = mesg_recv(readfd, &buffer)) > )
{
buffer.mesg_date[size] = ; /* get fifo name and request file name from buffer */
if((ptr = strchr(buffer.mesg_date, ' ')) == NULL)
_err("message is invalid"); *ptr++ = ; /* sprit the fifo name and request file name*/
snprintf(fifo_file_name, FILE_NAME, "/tmp/fifo.%s", buffer.mesg_date); /* open client fifo file */
if((writefd = open(fifo_file_name, O_WRONLY)) < )
_err("open client fifo fail"); /* open request file */
if((fd = open(ptr, O_RDONLY)) < )
_err("open request file fail"); /* send file content to client */
while((size = read(fd, buffer.mesg_date, MAXMESGDATE)) > )
{
buffer.mesg_len = size;
mesg_send(writefd, &buffer);
}
close(fd);
close(writefd);
}
close(readfd);
return ;
}
客户端程序:
/*
* client use struct to send and recive message
* */ #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <limits.h>
#include <fcntl.h> #define FIFO_NAME "/tmp/server.fifo"
#define FILE_NAME 256 /* client */ int
main(void)
{
int readfd, writefd;
char fifoname[FILE_NAME];
char filename[FILE_NAME];
size_t len, size;
struct mymesg buffer;
pid_t pid; printf("Input the file name > ");
fgets(filename, FILE_NAME, stdin);
len = strlen(filename);
filename[--len] = ; /* remove the suffix '\n' */ pid = getpid(); snprintf(fifoname, FILE_NAME, "/tmp/fifo.%d", pid); /* create client fifo file */
if(mkfifo(fifoname, ) < && errno != EEXIST)
_err("create client fifo fail"); /* create message to send to server */
snprintf(buffer.mesg_date, MAXMESGDATE, "%d %s", pid, filename);
len = strlen(buffer.mesg_date);
buffer.mesg_len = len; /* open the server fifo to send message */
if((writefd = open(FIFO_NAME, O_WRONLY)) < )
_err("open server fifo fail"); /*send message to server */
if((size = mesg_send(writefd, &buffer)) < )
_err("send to server fail"); /* open client fifo to read message */
if((readfd = open(fifoname, O_RDONLY)) < )
_err("open client fifo fail"); while((size = mesg_recv(readfd, &buffer)) > )
{
write(STDOUT_FILENO, buffer.mesg_date, buffer.mesg_len);
}
close(readfd);
close(writefd);
return ;
}
12. 管道和FIFO的限制
系统对管道和FIFO的唯一限制为:
- OPEN_MAX 一个进程在任意时刻打开的最大描述符数(Posix要求至少为16)
- PIPE_BUF 可以原子地往一个管道或FIFO写的最大数据量(Posix要求至少为512,在Linux 2.6.11以前,管道的容量是和系统的页长(system page)一样的(在i386中为4096字节,即4k),从Linux 2.6.11开始,管道的容量改为65536字节。)
IPC-管道的更多相关文章
- 进程间通信IPC -- 管道, 队列
进程间通信--IPC(Inter-Process Communication) 管道 from multiprocessing import Pipecon1,con2 = Pipe()管道是不安全的 ...
- 进程-IPC 管道 (一)
详见:https://github.com/ZhangzheBJUT/linux/blob/master/IPC(%E4%B8%80).md 一 IPC 概述 进程间通信就是在不同进程之间传播或交换信 ...
- IPC$管道的利用与远程控制
实验目的 通过实验了解IPC$攻击的原理与方法. 实验原理 IPC$攻击的相关原理 IPC$(Internet Process Connection)是共享"命名管道"的资源,它是为了让进程间通信而开 ...
- IPC——管道
概述 管道通信分为无名管道.有名管道 管道通信的本质 不管是有名管道,还是无名管道,它们的本质其实都是一样的,它们都是内核所开辟的一段缓存空间.进程间通过管道通信时,本质上就是通过共享操作这段缓存来实 ...
- Linux 进程间通信(一)(经典IPC:管道、FIFO)
管道 管道是Unix系统IPC的最古老方式,有两种局限性: (1) 历史上它们是半双工的(即数据只能在一个方向上流动),虽然现在某些系统提供了全双工管道,但是为了可移植性,不要抱有绝对的全双工假设 ...
- IPC$命令详解
一 摘要二 什么是ipc$三 什么是空会话四 空会话可以做什么五 ipc$所使用的端口六 ipc管道在hack攻击中的意义七 ipc$连接失败的常见原因八 复制文件失败的原因九 关于at命令和xp对i ...
- 空连接ipc$入侵
使用命令 net use url=file://\\IP\ipc$\\IP\ipc$ "" /user:"" 就可以简单地和目标建立一个空连接(需要目标开放ip ...
- unix进程间通信方式(IPC)
unix进程间通信方式(IPC) 管道(Pipe):管道可用于具有亲缘关系进程间的通信,允许一个进程和另一个与它有共同祖先的进程之间进行通信. 命名管道(named pipe):命名管道克服了管道没有 ...
- nodejs复习02
process 这个模块是单线程的,无法完全利用多核CPU 基本信息 //程序目录 process.cwd(); //应用程序当前目录 process.chdir('/home'); //改变应用程序 ...
- 【NodeJS线程】Boss和他的职员们
>>>[说明]还是一如既往的,这篇文章是从我的个人博客里挪过来的.原文参见:http://www.jscon.co/coding/frontend/nodejs_fork_child ...
随机推荐
- Balsamiq Mockups 注册码
Blacklist: Organization name: Rick DongSerial Key: eNrzzU/OLi0odswsqgnKTM5WcMnPS1eoMTQyMjexMDQyAIEa5 ...
- CRM系统简析
寄语: 简单阐述一下对CRM系统应用的理解,此内容参考网上资料所整理. CRM是Customer Relationship Management的缩写,简称客户关系管理. CRM系统可以从三个方面来分 ...
- 读《编写可维护的JavaScript》第一章总结
第一章 基本的格式化 1.4 ① 换行 当一行长度到达了单行最大的字符限制时,就需要手动将一行拆成俩行.通常我们会在运算符后换行,下一行会增加俩个层级的缩进. // 好的做法: 在运算符后换行,第二行 ...
- iOS开发之UITapGestureRecognizer单双击
转自手势开发 IOS开发之手势——UIGestureRecognizer 共存 在 iPhone 或 iPad 的开发中,除了用 touchesBegan / touchesMoved / touch ...
- Linux 笔记总览
LInux 性能分析 Linux IO实时监控命令详解
- CAD迷你看图
CAD迷你看图http://www.aec188.com/CAD迷你看图 2016R12超快.超小的CAD多功能看图工具,完全脱离AutoCAD浏览R14-R2016各版本DWG/DXF/DWF的二三 ...
- oracle表空间不足相关问题解决办法
欢迎和大家交流技术相关问题: 邮箱: jiangxinnju@163.com 博客园地址: http://www.cnblogs.com/jiangxinnju GitHub地址: https://g ...
- 利用jsoup进行模拟登录
因为工作的原因,近段时间开始接触jsoup.大概也弄清了用java来爬网页是怎样一个过程.特此,写篇日志以便他日方便查看. Jsoup是一个java平台的能够对xml文档结构的文档进行解析.有点类似于 ...
- C# 检测程序运行时间的方法,Stopwatch类
//需要引用命名空间,System.Diagnostics Stopwatch watch = new Stopwatch(); //实例化一个计时器 watch.Start(); //开始计时 #r ...
- Tomcat下使用war包发布项目
Tomcat下使用war包发布项目 转自<Tomcat下使用war包发布项目 >,地址:http://blog.csdn.net/wy818/article/details/7240294 ...