热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux:匿名管道的非阻塞属性

如何将文件描述符设置为非阻塞:#includeintfcntl(intfd,intcmd,…*arg*)fcntl函数有两个功能:查看属性设置非阻塞属性:O_NO



如何将文件描述符设置为非阻塞:

#include
int fcntl(int fd, int cmd, …/*arg*/)

fcntl函数有两个功能:



  1. 查看属性
  2. 设置非阻塞属性:O_NONBLOCK

参数:



  • fd:文件描述符
  • cmd:告诉fcntl函数做什么事情,由两个宏去告诉
  • arg:可变参数列表,在设置属性时要保留原属性


F_GETFL获取一个文件描述符的权限,忽略arg参数
F_SETFL设置一个文件描述符属性,新属性在第三个参数中设置新属性

返回值:



  • 参数如果是F_GETFL,返回文件描述符的属性。属性为0
  • 参数如果是F_SETFL,返回设置0成功,返回-1设置失败

#include
#include
int main ()
{
int fd[2];int ret=pipe(fd);//在内核中创建了一个管道
if(ret<0)
{
perror("pipe");
}

int flag=fcntl(fd[0], F_GETFL)
//查看读端属性,打印出的数值为0,这个0是属性不是保存的值
fcntl(fd[0], F_SETFL, flag | O_NONBLOCK)
//设置新属性时,要保留原属性
int flag=fcntl(fd[0], F_GETFL)
//再次查看f[0]的属性,数值变成2048
}

为什么读端阻塞属性为0,非阻塞属性是2048;因为文件打开方式的宏为位图的使用方式;例如在之前使用open函数,为什么能使用按位或的方式能表示既可以读写,文件不存在时又可以创建:

open(“./1.txt”, O_RDWR | O_CREAT, 0664)
//0表示八进制,664是权限

操作系统内核本质上也是由代码构成的,O_RDWR ,O_CREAT这些宏在内核中是有定义的;内核源码定义:

在这里插入图片描述

1.
先查看fd[0]的原属性:


验证代码:在这里插入图片描述
结果:在这里插入图片描述


结论:


  • fd[0]的属性信息是0
  • 在内核源码中,O_RDONLY对应的八进制是全0,转化成十进制也是全0,所以说f[0]的是只读属性
  • 我们说fd[0]是管道的读端是因为其对应的文件描述符的属性为只读属性

2.
接下来再给fd[0]添加非阻塞属性


代码:
在这里插入图片描述



  • 在增加属性的时候一定要用或 |保留原属性,不保留那就是重新设置了一个新属性,之前的属性就都没有了
  • 第二次的属性是2048,相对于0多了一个非阻塞属性;
  • 以上验证了文件打开方式的宏,在内核中都是使用位图的方式进行计算的

在这里插入图片描述

在这里插入图片描述


匿名管道的非阻塞特性:

代码1:

#include
#include
#include
int main ()
{
//创建管道
int fd[2];
int ret=pipe(fd);
//设置读文件描述符为非阻塞属性
if(ret<0)
{
perror("fork");
}
int flag=fcntl(fd[0], F_GETFL);
fcntl(fd[0], F_SETFL, flag|O_NONBLOCK);

//创建父子进程,父子进程谁读谁写不影响验证结果
//规定子进程读,父进程写
pid_t pid=fork();
if(pid<0)
{
perror(fork);
return 0;
}
else if(pid==0)//子进程
{
char buf[1024]={0};
size_t size=read(fd[0], buf,sizeof(buf)-1);
printf("size:%ld",size);
}
else//父进程
{
close(fd[0]);
sleep(1);
}
return 0;
}

读端设置成非阻塞

只需要父进程写端,子进程读端


1.父进程写端不关闭;子进程一直读
在这里插入图片描述 调用read函数后,返回值为-1


  • 含义1.表示未在管道中读到内容
  • 含义2.表示在读管道时调用错误
  • 通过错误码区分,将errno置为EAGAIN表示1成立,应循环继续读取,这就是非阻塞要搭配循环的一种体现

2.父进程写端关闭;子进程一直读
在这里插入图片描述


  • 含义1.表示没有读到内容
  • 含义2.因为没有写端而返回(该情况成立)
  • 返回0后就不用搭配循环了,因为该种情况表示管道没有写端

代码2:

#include
#include
#include
int main ()
{
int fd[2];
int ret=pipe(fd);
//设置读文件描述符为非阻塞属性
if(ret<0)
perror("fork");
int flag=fcntl(fd[1], F_GETFL);
fcntl(fd[1], F_SETFL, flag|O_NONBLOCK);

pid_t pid=fork();
if(pid<0)
{
perror(fork);
return 0;
}
else if(pid==0)//子进程
{
close(fd[0]);
int count=0;
while(1)
{
size_t size=write(fd[1], "1", 1);
if(size!=1)
{
printf("size:%d", size);
break;
}
printf("count:%ld\n", ++count);
}
}
else//父进程
{
close(fd[1]);
while(1)
{
sleep(2);
}
}
return 0;
}

写端设置成非阻塞

父进程读,子进程写
因为最后一个结果会导致一个进程崩溃,设置子进程写,父进程读


1.写端设置成非阻塞,子进程写,父进程读


  • 子进程只写一次,父进程读端不关闭,正常输出
  • 子进程一种写,在管道写满之后返回-1;写满管道后再次调用write函数,返回-1
    在这里插入图片描述

2.写端设置成非阻塞,读端关闭,


  • 调用写就会崩溃,产生僵尸进程
    所有读端都关闭,子进程刚往管道中一些写,就收到信号,导致子进程崩溃在这里插入图片描述

谁设置成非阻塞,谁就是因变量,将另一方设置成自变量(关不关闭)



推荐阅读
author-avatar
没有结局的梦z最痛
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有