
目录一、文件描述符复制1、dup函数复制文件描述符编辑 2、dup2函数复制文件描述符编辑 二、无名管道pipe1、概述2、无名管道的创建3、无名管道读写的特点4、无名管道ps -A | grep bash实现三、有名管道FIFO命名管道1、概述2、创建有名管道 mkfifo3、有名管道读写的特点一、文件描述符复制让新的文件描述符 指向 旧的文件描述符。新旧文件描述符指向同一个文件 使用的函数dup、dup2。1、dup函数复制文件描述符#includeunistd.hint dup(int oldfd);dup函数的功能从系统中寻找最小可用的文件描述符 作为oldfd的副本。 新文件描述符 通过dup的返回值返回。2、dup2函数复制文件描述符#includeunistd.hint dup2(int oldfd, int newfd);dup2的功能将newfd作为oldfd的副本。 如果newfd事先存在 dup2会先close(newfd)然后将newfd作为oldfd的副本。二、无名管道pipe1、概述管道(pipe)又称无名管道。 无名管道是一种特殊类型的文件在应用层体现为两个打开的文件描述符读端和写端。管道的特点半双工数据在同一时刻只能在一个方向上流动。数据只能从管道的一端写入从另一端读出。写入管道中的数据遵循先入先出的规则。管道所传送的数据是无格式的这要求管道的读出方与写入方必须事先约定好数据 的格式 如多少字节算一个消息等。管道不是普通的文件不属于某个文件系统其只存在于内存中。管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。从管道读数据是一次性操作数据一旦被读走它就从管道中被抛弃释放空间以便写更多的数据。管道没有名字只能在具有公共祖先的进程之间使用2、无名管道的创建#includeunistd.hint pipe(int filedes[2]);功能经由参数filedes返回两个文件描述符参数 filedes为int型数组的首地址其存放了管道的文件描述符fd[0]、fd[1]。filedes[0]为读而打开filedes[1]为写而打开管道filedes[0]的输出是filedes[1]的输 入。返回值 成功返回 0 失败返回-1注意在使用无名管道的时候 必须事先确定谁发谁收的问题。一旦确定不可更改。#include stdio.h #include unistd.h #include string.h #include sys/wait.h int main(int argc, char const *argv[]) { //创建一个无名管道 int fd[2]; pipe(fd); //创建一个子进程 //父进程发 子进程收 pid_t pidfork(); if(pid0)//子进程 { //子进程的写端无意义可以事先关闭 close(fd[1]); //子进程接收父进程消息 unsigned char buf[128]; printf(子进程%d正在等待父进程的消息\n, getpid()); read(fd[0],buf,sizeof(buf)); printf(子进程%d读到的消息为:%s\n, getpid(), buf); //子进程读完数据 应该关闭读端 close(fd[0]); //显示退出 _exit(-1); } if(pid0) //父进程 { //父进程的读端无意义可以事先关闭 close(fd[0]); //写端写入数据 printf(3秒后父进程%d写入数据hello pipe\n,getpid()); sleep(3); write(fd[1],hello pipe,strlen(hello pipe)); printf(父进程:%d完成写入\n, getpid()); //通信完成 应该关闭写端 close(fd[0]); //等待子进程退出 wait(NULL); } return 0; }3、无名管道读写的特点1、默认用read函数从管道中读数据是阻塞的。2、调用write函数向管道里写数据当缓冲区已满时write也会阻塞。3、通信过程中读端口全部关闭后写进程向管道内写数据时写进程会收 到SIGPIPE信号退出。4、无名管道ps -A | grep bash实现#include stdio.h #include unistd.h #include string.h #include sys/wait.h int main(int argc, char const *argv[]) { //创建一个无名管道 int fd[2]; pipe(fd); //创建两个子进程 int i0; for(i0;i2;i) { pid_t pidfork(); if(pid0) break;//防止子进程继续创建子进程 } if(i0)//子进程1 { //ps ‐A 写端 close(fd[0]); //1作为fd[1]的副本 dup2(fd[1],1); //执行ps ‐A execlp(ps,ps,-A,NULL); _exit(-1); } else if(i1)//子进程2 { //grep bash 读端 close(fd[1]); //0作为fd[0]的副本 dup2(fd[0],0); //执行grep bash execlp(grep,grep,bash,NULL); } if(i2) //父进程 { //关闭管道读写端 close(fd[0]); close(fd[1]); while(1) { pid_t pidwaitpid(-1,NULL,WNOHANG); if(pid0) printf(子进程%d退出了\n, pid); else if(pid0) continue; else if(pid0) break; } } return 0; }三、有名管道FIFO命名管道1、概述主要用于没有血缘关系的进程间通信。特点:半双工数据在同一时刻只能在一个方向上流动。写入FIFO中的数据遵循先入先出的规则。FIFO所传送的数据是无格式的这要求FIFO的读出方与写入方必须事先 约定好数据的格式如多少字节算一个消息等。FIFO在文件系统中作为一个特殊的文件而存在但FIFO中的内容却存放在内存中。管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。从FIFO读数据是一次性操作数据一旦被读它就从FIFO中被抛弃释 放空间以便写更多的数据。当使用FIFO的进程退出后FIFO文件将继续保存在文件系统中以便以后使用。FIFO有名字不相关的进程可以通过打开命名管道进 行通信(重要2、创建有名管道 mkfifoFIFO文件的创建#includesys/type.h#includesys/stat.hint mkfifo( const char *pathname, mode_t mode);参数pathnameFIFO的路径名文件名。modemode_t类型的权限描述符。返回值 成功返回 0 失败如果文件已经存在则会出错且返回-1fifo_write.c:#include stdio.h #include sys/types.h #include sys/stat.h #include sys/fcntl.h #include unistd.h #include string.h int main() { //创建有名管道(确保两个进程识别相同目录) mkfifo(my_fifo,0666); //open以写的方式的打开 有名管道(阻塞 到 对方 以读的方式打开) int fpopen(my_fifo,O_WRONLY); if(fp0) { perror(open); return 0; } printf(写端open成功\n); while(1) { //获取键盘输入 unsigned char buf[128]; printf(请输入需要发送的数据:); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1]0; //发送数据 write(fp,buf,sizeof(buf)); //退出循环 if(strcmp(buf,Bye)0) break; } close(fp); }fifo.read.c#include stdio.h #include sys/types.h #include sys/stat.h #include sys/fcntl.h #include unistd.h #include string.h int main() { //创建有名管道(确保两个进程识别相同目录) mkfifo(my_fifo,0666); //open以读的方式的打开 有名管道(阻塞 到 对方 以写的方式打开) int fpopen(my_fifo,O_RDONLY); if(fp0) { perror(open); return 0; } printf(读端open成功\n); //循环的读取数据 while(1) { //接收数据 unsigned char buf[128]; read(fp,buf,sizeof(buf)); printf(收到数据:%s\n, buf); //退出循环 if(strcmp(buf,Bye)0) break; } close(fp); }3、有名管道读写的特点阻塞方式打开管道1、open以只读方式打开FIFO时要阻塞到某个进程为写而打开此FIFO2、open以只写方式打开FIFO时要阻塞到某个进程为读而打开此FIFO。3、open以只读、只写方式打开FIFO时会阻塞调用read函数从FIFO里读数据时read也会阻塞。4、通信过程中若写进程先退出了则调用read函数从FIFO里读数据时不阻塞若写进程又 重新运行则调用read函数从FIFO里读数据时又恢复阻塞。5、通信过程中读进程退出后写进程向命名管道内写数据时写进程也会收到 SIGPIPE信号退出。6、调用write函数向FIFO里写数据当缓冲区已满时write也会阻塞。非阻塞方式打开管道1、先以只读方式打开如果没有进程已经为写而打开一个FIFO, 只读open成功并且open不阻塞。2、先以只写方式打开如果没有进程已经为读而打开一个FIFO只写open 将出错返回-1。3、read、write读写命名管道中读数据时不阻塞。4、通信过程中读进程退出后写进程向命名管道内写数据时写进程也会收到 SIGPIPE信号退出。注意open函数以可读可写方式打开FIFO文件时的特点1、open不阻塞。2、调用read函数从FIFO里读数据时read会阻塞。3、调用write函数向FIFO里写数据当缓冲区已满时write也会阻塞