1、pipe函数(创建管道)
(1)pipe函数的定义:
#include<unistd.h>
int pipe(int fd[2]);
通过pipe函数创建的这两个文件描述符fd[0],fd[1]分别构成管道的两端,并且fd[1]只能用于往管道中写数据,fd[0]只能从管道中读取数据,不能反过来使用。默认情况下,这一对文件描述符都是阻塞的。如果管道的写端文件描述符fd[1]的引用计数是0,那就证明没有任何进程往管道中写数据,则针对管道的读端文件描述符fd[0]的操作将返回0,即读取到了文件结束标记EOF。反之,如果管道的读端文件描述符fd[0]的引用计数为0,则代表没有任何进程从管道中读取数据,则针对管道的写端文件描述符fd[1]的write操作将失败,并引发SIGPIPE信号。
(2)请问管道内部传输的数据数据是什么形式?
字节流
(3)请问该字节流跟TCP字节流的概念相同吗?
相同,但是又有细微的差别。应用层能往一个TCP连接中写多少数据,取决于对方的接收窗口和本端的拥塞窗口的大小,而管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道中读走的话,该管道最多能被写入多少字节的数据,Linux 2.6.11内核起,管道容量的大小默认是65536字节。
(4)请问管道的结构?
打开管道,在内存中分配一块空间,写入的数据存放在这块内存中,但是有名管道的管道文件放在磁盘上
2、dup函数和dup2函数(这两个函数是用于复制文件描述符的函数)
通过dup函数和dup2函数,可以实现将标准输入重定向到一个文件或者把标准输出重定向到一个网络连接。
#include<unistd.h>
int dup(int file_descriptor)
int dup2(int file_descriptor_one, int file_descriptor_two)
dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor指向相同的文件、管道或者网络连接,并且dup返回的文件描述符总是取系统当前可用的最小整数值。
注:dup和dup2创建的文件描述符并不继承源文件描述符的属性。
二、用于读写数据的函数
1、sendfile函数
该函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。
(1)sendfile函数原型
#include<sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t* offset,size_t count);成功时返回传输的字节数
in_fd必须时一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道;而out_fd则必须是一个socket。
(2)请问这种读写数据的函数和read、write的区别在哪?
首先分析用read/write函数来实现网络上的文件传输。
int n = read(fd, tmp_buf,len);
write(socketfd, tmp_buf,n);
这段代码看起来很简单,好像并没有产生太多的开销,但实际上这两个函数在调用的过程中,目标数据被拷贝了4次,同时,发生了4次用户态和内核态的空间切换。
a:调用read函数,从用户态切换到内核,DMA模块将文件数据从磁盘拷到了内核空间
b:read函数返回,从内核态切换到用户态,内核空间数据拷贝到用户空间
c:write函数调用,从用户态切换到内核态,用户数据拷贝到与socket相关的内核空间
d:write函数返回,从内核态切换到用户态,DMA模块将内核空间数据拷贝到协议引擎上
而sendfile函数调用只需要两步就可以完成文件的发送
a:sendfile函数通过DAM模块将文件数据从磁盘空间拷贝到内核缓冲区
b:文件数据并不需要从内核缓冲区复制到socket关联的缓冲区,而是将记录数据位置和长度的描述符加入到socket缓冲区,DAM(直接存储器读写)模块直接将数据从内核缓冲区传递给协议引擎
虽然数据实际上仍然是由磁盘复制到内存,再由内存复制到发送设备,但是,从操作系统的角度来看,这就是“零拷贝”,因为内核空间不存在冗余数据,应用“零拷贝”特性,除了避免复制之外,还有更少的上下文切换,更少的CPU cache污染以及CPU没有必要计算校验和。
总结:区别就是sendfile函数可以避免复制,减少上下文的切换,更少的CPU cache污染以及CPU没有必要计算校验和
2、mmap函数和munmap函数
Mmap函数用于申请一段内存空间,可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap函数则释放由mmap创建的这段内存空间。
3、splice函数
用于再两个文件描述符之间移动数据,也是零拷贝操作
4、fcntl函数
提供了对文件描述符的各种控制操作。具体功能有
有时我们希望把标准输入重定向到一个文件,或者把标准输出重定向到一个网络连接(比如CGI编程),可以通过复制文件描述符函数dup或dup2实现: 如下实现了一个基本的CGI服务器: 在此例中,我们先关闭标准输入文件描述符STDOUT_FILENO(其值为1),然后复制socket文件描述符connfd。因为dup总是返回系统中最小的可用文件描述符,所以他的返回值实际上是1,即关闭的标准输出文件描述符...
缓冲 1)全缓冲:缓冲区填满了才触发系统调用。例如fputc() 2)行缓冲:出现换行或缓冲区填满才出发系统调用。例如stdout 3)无缓冲:立即系统调用,没有缓冲区。例如stderr open() 打开或创建一个文件。成功返回新分配的文件描述符,失败返回-1并设置errno 文件描述符:一个整型,值为0表示stdin标准输入,值为1表示stdout标准输出,值为2表示stderr标准错误 op...
函数原型: 参数: oldfd:要复制的文件描述符 dup调用成功:有两个文件描述符指向同一个文件 返回值:取最小的且没被占用的文件描述符 函数原型: 分析: 假设newfd已经指向一个文件,首先断开close与那个文件的链接,newfd指向oldfd指向的文件。(文件描述符重定向) 假设调用函数dup2前oldfd与newfd指向同一个文件,调用函数什么也不做。 假设newfd没被占用,newf...
#include <unistd.h> int dup(int oldfd); int dup2(int oldfd, int newfd); 作用:dup函数实现对一个文件的文件描述符进行复制,复制之后该进程就会新增加一一个文件描述符指向该文件(即实现同一个文件对应多个文件描述符)。dup2函数:如果new是一个被打开的文件描述符(与old不是同一个文件),则在拷贝前先关闭new(n...
前言 在linux下,一切皆文件。当文件被打开时,会返回文件描述符用于操作该文件,从shell中运行一个进程,默认会有3个文件描述符存在(0、1、2);)0表示标准输入,1表示标准输出,2表示标准错误。一个进程当前有哪些打开的文件描述符可以通过/proc/进程ID/fd目录查看。 今天,我们主要说两个用于复制文件描述符的函数dup()和dup2()。 dup()、dup2()函数 函数原形...
dup和dup2,它们的作用都是用来复制一个文件的描述符。它们经常用来重定向进程的stdin、stdout和stderr 这两个函数的原形如下: dup()函数 利用函数dup,我们可以复制一个描述符。传给该函数一个既有的描述符,它就会返回一个新的描述符,这个新的描述符是传给它的描述符的拷贝。这意味着,这两个描述符共享同一个数据结构。例如,如果我们对一个文件描述符执行lseek操作,得到的第一个文...
dup unix-liked操作系统中,复制文件描述符的函数dup和dup2 由于题目要求不能使用fcntl函数,所以考虑使用dup函数来实现,思路如下: 首先对于参数fd2,应当大于等于当前未使用的最小文件描述符,那么如何确定这个最小值呢?想到dup函数的返回值就是dup函数被调用前最小的未使用的文件描述符,设它为fd_min。 如果fd2大于fd_min,只要使从fd_min到fd2这个区间内...
1.dup/dup2(复制文件描述符);可以用来复制一个已经存在的文件描述符,使两个文件描述符都指向同一个file 结构体中。如果两个文件描述符都指向同一个file结构体,文件状态标志和读写位置只保存一份file 结构体中,并且file 结构体的引用计数为2. 可以使用dup/dup2将标准输入重定向到一个文件,或是把标准输出重定向到一个网络连接中 2. dup/dup2函数说明: dup函数创建...
两个函数都可以用来复制一个现有的文件描述符。 由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。对于dup2,可以用fd2参数指定新描述符的值。如果fd2已经打开,则先将其关闭。如若fd等于fd2,则dup2 返回fd2,而不关闭它。否则,fd2的FD_CLOXEC文件描述符标志就被清除,这样fd2在进程调用exec时是打开状态。 复制一个文件描述符的另一种方法是使用 fcntl 函...
今天这里对两个能够重定向文件描述符的函数进行解析dup和dup2,其实这两个函数很简单。就是根据一个文件描述符产生一个新的文件描述符。 1、dup函数 原型:int dup(int oldfd); 形参:一个现有的文件描述符 返回值:根据现有的文件描述符产生一个新的文件描述符,新的文件描述符与现有文件描述符执行同一个文件; 假设有一个oldfd=3,现在执行一个newfd = dup(oldfd)...