网站左侧漂浮代码,外贸php网站源码,17173在线玩,iis的默认网站没有自动启动介绍 管道本质上就是一个文件#xff0c;前面的进程以写方式打开文件#xff0c;后面的进程以读方式打开。这样前面写完后面读#xff0c;于是就实现了通信。虽然实现形态上是文件#xff0c;但是管道本身并不占用磁盘或者其他外部存储的空间。在Linux的实现上#xff0c;…介绍 管道本质上就是一个文件前面的进程以写方式打开文件后面的进程以读方式打开。这样前面写完后面读于是就实现了通信。虽然实现形态上是文件但是管道本身并不占用磁盘或者其他外部存储的空间。在Linux的实现上它占用的是内存空间。所以Linux上的管道就是一个操作方式为文件的内存缓冲区。管道分类匿名管道、命名管道命令行中使用
1、mkfifo或mknod命令来创建一个命名管道
[rootVM-90-225-centos ~]# mkfifo pipe
[rootVM-90-225-centos ~]# ls -l pipe
prw-r--r-- 1 root root 0 Feb 28 21:02 pipe我们现在让一个进程写这个管道文件 echo 12345 pipe
此时这个写操作会阻塞因为管道另一端没有人读。此时如果有进程读这个管道那么这个写操作的阻塞才会解除:
[rootVM-90-225-centos ~]# cat pipe
12345当我们cat完这个文件之后另一端的echo命令也返回了. Linux系统无论对于命名管道和匿名管道底层都用的是同一种文件系统的操作行为这种文件系统叫pipefs,可以通过下面命令查看是否具有这个系统
[rootVM-90-225-centos ~]# cat /proc/filesystems |grep pipefs
nodev pipefs
nodev rpc_pipefs系统编程中使用
匿名管道和命名管道分别叫做PIPE和FIFO创建匿名管道的系统调用是pipe()而创建命名管道的函数是mkfifo()。 匿名管道
#include unistd.h
int pipe(int pipefd[2]);这个方法将会创建出两个文件描述符: pipefd[0]是读方式打开作为管道的读描述符。pipefd[1]是写方式打开作为管道的写描述符。从管道写端写入的数据会被内核缓存直到有人从另一端读取为止。
pipe示例一简单的写入读出
#include stdlib.h
#include stdio.h
#include unistd.h
#include string.h#define STRING hello world!int main()
{int pipefd[2];char buf[BUFSIZ];// 创建一组管道if (pipe(pipefd) -1) {perror(pipe());exit(1);}// 从[1]写入STRINGif (write(pipefd[1], STRING, strlen(STRING)) 0) {perror(write());exit(1);}// 从[0]读出,结果存到bufif (read(pipefd[0], buf, BUFSIZ) 0) {perror(write());exit(1);}// 打印读出来的结果printf(%s\n, buf);exit(0);
}程序创建了一个管道并且对管道写了一个字符串之后从管道读取并打印在标准输出上。 当然这不属于进程间通信实际情况中我们不会在单个进程中使用管道。 进程在pipe创建完管道之后往往都要fork产生子进程。下面的demo是父子两个进程使用一个管道可以完成半双工通信。此时父进程可以通过fd[1]给子进程发消息子进程通过fd[0]读。子进程也可以通过fd[1]给父进程发消息父进程用fd[0]读。
pipe示例二父子进程半双工通信
#include stdlib.h
#include stdio.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/wait.h#define STRING hello world!int main()
{int pipefd[2];pid_t pid;char buf[BUFSIZ];// 创建管道if (pipe(pipefd) -1) {perror(pipe());exit(1);}// fork产生子进程pid fork();if (pid -1) {perror(fork());exit(1);}// fork()新进程返回0旧进程返回新进程的进程ID。if (pid 0) {/* this is child. */printf(Child pid is: %d\n, getpid());// 子进程会继承父进程对应的文件描述符// 父进程先pipe创建管道之后子进程也会得到同一个管道的读写文件描述符if (read(pipefd[0], buf, BUFSIZ) 0) {perror(write());exit(1);}printf(%s\n, buf);// 清空bufbzero(buf, BUFSIZ);snprintf(buf, BUFSIZ, Message from child: My pid is: %d, getpid());// 把读取到的数据重新发送给主进程if (write(pipefd[1], buf, strlen(buf)) 0) {perror(write());exit(1);}} else {/* this is parent */printf(Parent pid is: %d\n, getpid());snprintf(buf, BUFSIZ, Message from parent: My pid is: %d, getpid());// 父进程写数据到管道if (write(pipefd[1], buf, strlen(buf)) 0) {perror(write());exit(1);}// 等待1ssleep(1);// 清空bufbzero(buf, BUFSIZ);// 读取管道消息到bufif (read(pipefd[0], buf, BUFSIZ) 0) {perror(write());exit(1);}printf(%s\n, buf);wait(NULL);}exit(0);
}打印结果
Parent pid is: 17697
Child pid is: 17702
Message from parent: My pid is: 17697
Message from child: My pid is: 17702如果在vscode中debug的话是debug不到if (pid 0) 的分支的你只能debug主进程的流程。 从以上的demo中用同一个管道的父子进程可以分时给对方发送消息我们可以看到对管道读写的一些特点 1、在管道中没有数据的情况下对管道的读操作会阻塞直到管道内有数据为止。 2、当一次写的数据量不超过管道容量的时候对管道的写操作一般不会阻塞直接将要写的数据写入管道缓冲区即可。 管道实际上就是内核控制的一个内存缓冲区既然是缓冲区就有容量上限。我们把管道一次最多可以缓存的数据量大小叫做PIPESIZE。内核在处理管道数据的时候底层也要调用类似read和write这样的方法进行数据拷贝这种内核操作每次可以操作的数据量也是有限的一般的操作长度为一个page即默认为4k字节。我们把每次可以操作的数据量长度叫做PIPEBUF。POSIX标准中对PIPEBUF有长度限制要求其最小长度不得低于512字节。PIPEBUF的作用是内核在处理管道的时候如果每次读写操作的数据长度不大于PIPEBUF时保证其操作是原子的。而PIPESIZE的影响是大于其长度的写操作会被阻塞直到当前管道中的数据被读取为止。 在Linux 2.6.11之前PIPESIZE和PIPEBUF实际上是一样的。在这之后Linux重新实现了一个管道缓存并将它与写操作的PIPEBUF实现成了不同的概念形成了一个默认长度为65536字节的PIPESIZE而PIPEBUF只影响相关读写操作的原子性。从Linux 2.6.35之后在fcntl系统调用方法中实现了F_GETPIPE_SZ和F_SETPIPE_SZ操作来分别查看当前管道容量和设置管道容量。管道容量容量上限可以在/proc/sys/fs/pipe-max-size进行设置。 在实际情境下半双工管道管道的两端都可能有多个进程进行读写处理。如果再加上线程则事情可能变得更复杂。实际上我们在使用管道的时候并不推荐这样来用。管道推荐的使用方法是其单工模式即只有两个进程通信一个进程只写管道另一个进程只读管道。
pipe示例三父子进程单工通信
这个程序实际上比上一个要简单父进程关闭管道的读端只写管道。子进程关闭管道的写端只读管道。 此时两个进程就只用管道实现了一个单工通信并且这种状态下不用考虑多个进程同时对管道写产生的数据交叉的问题这是最经典的管道打开方式也是我们推荐的管道使用方式。
#include stdlib.h
#include stdio.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/wait.h#define STRING hello world!int main()
{int pipefd[2];pid_t pid;char buf[BUFSIZ];if (pipe(pipefd) -1) {perror(pipe());exit(1);}pid fork();if (pid -1) {perror(fork());exit(1);}if (pid 0) {/* this is child. */close(pipefd[1]);printf(Child pid is: %d\n, getpid());// 读if (read(pipefd[0], buf, BUFSIZ) 0) {perror(write());exit(1);}printf(%s\n, buf);} else {/* this is parent */close(pipefd[0]);printf(Parent pid is: %d\n, getpid());snprintf(buf, BUFSIZ, Message from parent: My pid is: %d, getpid());// 写if (write(pipefd[1], buf, strlen(buf)) 0) {perror(write());exit(1);}wait(NULL);}exit(0);
}命名管道 命名管道在底层的实现跟匿名管道完全一致区别只是命名管道会有一个全局可见的文件名以供别人open打开使用。再程序中创建一个命名管道文件的方法有两种一种是使用mkfifo函数。另一种是使用mknod系统调用
fifo示例
client端
/* 这是一个命名管道的实现demo实现两个进程间聊天功能* */
#includestdio.h
#includeunistd.h
#includesys/stat.h
#includeerrno.h
#includefcntl.h
#includestring.hint main()
{char *file ./test.fifo;umask(0); //设置umask仅在当前进程有效。if(mkfifo(file,0663)0){if(errno EEXIST){printf(fifo exist\n);}else {perror(mkfifo\n);return -1;}}int fd open(file,O_WRONLY);if(fd0){perror(open error);return -1;}printf(open fifo success!!!\n);while(1){printf(input: );fflush(stdout);char buff[1024]{0};scanf(%s,buff);write(fd,buff,strlen(buff));}return 0;
}
server端
#includestdio.h
#includeunistd.h
#includesys/stat.h
#includeerrno.h
#includefcntl.h
#includestring.hint main()
{char *file ./test.fifo;umask(0); //设置umask仅在当前进程有效。if(mkfifo(file,0663)0){if(errno EEXIST){printf(fifo exist\n);}else {perror(mkfifo\n);return -1;}}int fd open(file,O_RDONLY);if(fd0){perror(open error);return -1;}printf(open fifo success!!!\n);while(1){char buff[1024] {0};int ret read(fd,buff,1024);if(ret0){printf(peer say:%s\n,buff);}}return 0;
}然后在子目录下编译
g ./server.cpp -o server
g ./client.cpp -o client然后在两个终端页面上分别运行
./server
./client即可进行单向通信一般工程中两个进程建立两个fifo就可以进行双向通信了