2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > c语言系统编程六:Linux进程间通信之无名管道

c语言系统编程六:Linux进程间通信之无名管道

时间:2022-09-24 08:26:59

相关推荐

c语言系统编程六:Linux进程间通信之无名管道

Linux进程间通信之无名管道

一 文件描述符复制1.1 dup函数(复制文件描述符)1.2 dup2函数(复制文件描述符)二 无名管道的概述三 无名管道的特点四 无名管道的创建和使用4.1 无名管道创建函数pipe4.2 无名管道使用实例一4.3 无名管道的读写特点4.4 无名管道的默认大小4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash

一 文件描述符复制

让新的文件描述符指向旧的文件描述符,新旧文件描述符指向同一文件;

使用函数dup,dup2;

1.1 dup函数(复制文件描述符)

需要的头文件和函数原型

#include <unistd.h>int dup(int oldfd);int dup2(int fildes, int fildes2);

函数功能

从系统中寻找最小的可用文件描述符,作为已有文件描述符oldfd的副本,新文件描述符和旧文件描述符指向同一文件,新文件描述符提供dup的返回值返回。

dup函数案例

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>int main(int arg,char *args[]){int newfd = 0;newfd = dup(1);if(newfd == -1){perror("dup");return -1;}printf("新文件描述符是:%d\n",newfd);printf("向文件描述符1输出\n");write(newfd,"向新文件描述符输出\n",strlen("向新文件描述符输出\n"));while(1){sleep(1);}return 0;}

1.2 dup2函数(复制文件描述符)

需要的头文件和函数原型

#include <unistd.h>int dup2(int oldfd, int newfd);

功能

将newfd作为oldfd的副本。

如果newfd事先存在,dup2会先close掉newfd,然后将newfd作为oldfd的副本。

dup2函数实例

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <unistd.h>int main(int arg,char *args[]){int newfd = 0;newfd = dup2(1,100);if(newfd == -1){perror("dup");return -1;}printf("新文件描述符是:%d\n",newfd);printf("向文件描述符1输出\n");write(newfd,"向新文件描述符输出\n",strlen("向新文件描述符输出\n"));while(1){sleep(1);}close(newfd);return 0;}

二 无名管道的概述

管道(pipe)又称无名管道。无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符,实际上它是一块逻辑内存。

三 无名管道的特点

半双工,创建一个管道,这个管道上数据只能在一个方向上流动;数据只能从管道的一端写入,从另一端读出;写入管道中的数据遵循先入先出的规则;管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节一个消息等;管道不是普通文件,不属于某个文件系统,其只存在于内存中;管道在内存中对应一个缓冲区,不同的系统其大小不一定相同;从管道读取数据是一次性操作,数据一旦被读走,他就从管道中被抛弃,释放空间以便写更多的数据;管道没有名字,只能在有血缘关系的进程之间使用。

四 无名管道的创建和使用

4.1 无名管道创建函数pipe

需要的头文件和函数原型

#include <unistd.h?int pipe(int filedes[2]);

函数功能

经由参数filedes返回两个文件描述符

参数

filedes为int型数组的首地址,其中存放了管道文件描述符fd[0],fd[1];filedes[0]为读而打开;filedes[1]为写而打开管道。filedes[0]的输出是filedes[1]的输入。

返回值

成功:返回0失败:返回-1

注意

使用无名管道时,必须事先确定谁发,谁收的问题。

4.2 无名管道使用实例一

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>int main(int arg, char *argv[]){//创建无名管道int fd[2];pipe(fd);//创建一个子进程//父进程发;子进程收pid_t pid = fork();if(pid == 0) //子进程{//子进程写端无意义(可以事先关闭)close(fd[1]);//子进程接收父进程消息printf("子进程%d正在等待父进程的消息\n",getpid());unsigned char buf[128] = "";read(fd[0],buf,sizeof(buf));printf("子进程%d读到的消息为:%s\n",getpid(),buf);//子进程读完数据,应该关闭读端close(fd[0]);//显示退出_exit(-1);}else if(pid > 0) //父进程{//父进程的读端无意义(可以事先关闭)close(fd[0]);//写端写入数据printf("父进程:%d 3秒后写入数据hello pipe\n",getpid());sleep(3);write(fd[1],"hello pipe",strlen("hello pipe"));printf("父进程:%d完成写入\n",getpid());//通信完成,应该关闭写端close(fd[1]);//等待子进程退出wait(NULL);}return 0;}

4.3 无名管道的读写特点

默认用read函数从管道中读数据是阻塞的;用write函数向管道中写数据,当缓冲区已满时也是阻塞的;通信过程中如果读端口全部关闭后,写进程向管道写数据时,写进程会收到SIGPIPE信号而退出;可以通过fcntl函数来设置管道的阻塞特性:

设置为非阻塞:fcntl(fd,FSETFL, O_NONBLOCK);设置为阻塞:fcntl(fd,FSETFL, 0);

4.4 无名管道的默认大小

用命令ulimit -a 查看系统中无名管道的默认大小

验证无名管道有大小限制

4.5 无名管道使用实例二:写一个程序实现 ps -A | grep bash

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>int main(int arg, char *argv[]){//创建无名管道int fd[2];int res = 0;res = pipe(fd);if(res == -1){perror("pipe");return 1;}//创建两个子进程int i=0;for(i=0;i<2;i++){pid_t pid = fork();if(pid == 0)break;}if(i==0) //子进程1{//ps -A 写端//关闭管道的读端口close(fd[0]);//将管道的读端口的文件描述符复制给文件描述符0int res1 = dup2(fd[1],1);if(res1 == -1){perror("dup2");return 1;}int res2 = execl("/bin/ps","ps","-A",NULL);if(res2 == -1){perror("execl");}_exit(0);}else if(i == 1)//子进程2{//子进程2负责执行grep bash//关闭管道的读端口close(fd[1]);//将管道的写端口的文件描述符复制给文件描述符1int res1 = dup2(fd[0],0);if(res1 == -1){perror("dup2");return 1;}int res2 = execl("/bin/grep","grep","bash",NULL);if(res2 == -1)perror("exec");_exit(0);}else if(i == 2)//父进程{//关闭管道读写端口close(fd[0]);close(fd[1]);while(1){pid_t pid = waitpid(-1,NULL,WNOHANG);if(pid>0){printf("子进程%d被主进程回收\n",pid);}else if(pid == 0){continue;}else if(pid<0){break;}}}return 0;}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。