pipe函数

pipe函数可用于创建一个管道,以实现进程间通信。

#include<unistd.h>

/* Create a one-way communication channel (pipe).
If successful, two file descriptors are stored in PIPEDES;
bytes written on PIPEDES[1] can be read from PIPEDES[0].
Returns 0 if successful, -1 if not. */
extern int pipe (int __pipedes[2]) __THROW __wur;

pipe函数的参数是-一个包含两个int 型整数的数组指针。该函数成功时返回0,并将一对打开的文件描述符值填入其参数指向的数组。如果失败,则返回-1并设置errmo。

通过pipe函数创建的这两个文件描述符fd[0]和fd[]分别构成管道的两端,往fd[1]写人的数据可以从fd[0] 读出。并且,fd[0] 只能用于从管道读出数据,fd[1] 则只能用于往管道写入数据,而不能反过来使用。如果要实现双向的数据传输,就应该使用两个管道。默认情况下,这一对文件描述符都是阻塞的。此时如果我们用read系统调用来读取一个空的管道,则read将被阻塞,直到管道内有数据可读;如果我们用write系统调用来往一个满的管道中写人数据,则write亦将被阻塞,直到管道有足够多的空闲空间可用。但如果应用程序将fd[0]和fd[1]都设置为非阻塞的,则read和write会有不同的行为。关于阻塞和非阻塞的讨论。如果管道的写端文件描述符fd[1]的引用计数减少至0,即没有任何进程需要往管道中写人数据,则针对该管道的读端文件描述符fd[0]的read操作将返回0,即读取到了文件结束标记(End Of File,EOF); 反之,如果管道的读端文件描述符fd[0]的引用计数减少至0,即没有任何进程需要从管道读取数据,则针对该管道的写端文件描述符fd[1]的write操作将失败,并引发SIGPIPE信号。

管道内部传输的数据是字节流,这和TCP字节流的概念相同。但二者又有细微的区别。应用层程序能往一个TCP连接中写人多少字节的数据,取决于对方的接收通告窗口的大小和本端的拥塞窗口的大小。而管道本身拥有一个容量限制,它规定如果应用程序不将数据从管道读走的话,该管道最多能被写人多少字节的数据。自Linux 2.6.11内核起,管道容量的大小默认是65536字节。我们可以使用fentl函数来修改管道容量。

此外,socket 的基础API中有-一个socketpair函数。它能够方便地创建双向管道。其定义如下:

#include<sys/types.h>
#include<sys/socket.h> /* Create two new sockets, of type TYPE in domain DOMAIN and using
protocol PROTOCOL, which are connected to each other, and put file
descriptors for them in FDS[0] and FDS[1]. If PROTOCOL is zero,
one will be chosen automatically. Returns 0 on success, -1 for errors. */
extern int socketpair (int __domain, int __type, int __protocol,
int __fds[2]) __THROW;

socketpair前三个参数的含义与socket系统调用的三个参数完全相同,但domain只能使用UNIX本地域协议族AF_ _UNIX,因为我们仅能在本地使用这个双向管道。最后一个参数则和pipe系统调用的参数一样,只不过socketpair创建的这对文件描述符都是既可读又可写的。socketpair 成功时返回0,失败时返回-1并设置ermno。

dup函数和dup2函数

#include<unistd.h>

/* Duplicate FD, returning a new file descriptor on the same file.  */
extern int dup (int __fd) __THROW __wur; /* Duplicate FD to FD2, closing FD2 and making it open on the same file. */
extern int dup2 (int __fd, int __fd2) __THROW;

dup函数创建一个新的文件描述符,该新文件描述符和原有文件描述符file_descriptor 指向相同的文件、管道或者网络连接。并且dup返回的文件描述符总是取系统当前可用的最小整数值。dup2 和dup类似,不过它将返回第一个不小于file_descriptor_two 的整数值。dup 和dup2系统调用失败时返回-1并设置errno。

readv函数和writev函数

readv函数将数据从文件描述符读到分散的内存块中,即分散读; writev 函数则将多块分散的内存数据一并写人文件描述符中,即集中写。它们的定义如下:

#include<sys/uio.h>

/* Read data from file descriptor FD, and put the result in the
buffers described by IOVEC, which is a vector of COUNT 'struct iovec's.
The buffers are filled in the order specified.
Operates just like 'read' (see <unistd.h>) except that data are
put in IOVEC instead of a contiguous buffer. This function is a cancellation point and therefore not marked with
__THROW. */
extern ssize_t readv (int __fd, const struct iovec *__iovec, int __count)
__wur; /* Write data pointed by the buffers described by IOVEC, which
is a vector of COUNT 'struct iovec's, to file descriptor FD.
The data is written in the order specified.
Operates just like 'write' (see <unistd.h>) except that the data
are taken from IOVEC instead of a contiguous buffer. This function is a cancellation point and therefore not marked with
__THROW. */
extern ssize_t writev (int __fd, const struct iovec *__iovec, int __count)
__wur;

fd参数是被操作的目标文件描述符。vector 参数的类型是iovec结构数组。该结构体描述一块内存区。 count 参数是vector数组的长度,即有多少块内存数据需要从fd读出或写到fd。readv和writev在成功时返回读出1写入fd的字节数,失败则返回-1并设置ermno。它们相当于简化版的recvmsg和sendmsg函数。

sendfile

sendfile函数在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了内核缓冲区和用户缓冲区之间的数据拷贝,效率很高,这被称为零拷贝。sendfile 丽数的定义如下:

#include<sys/sendfile.h>

/* Send up to COUNT bytes from file associated with IN_FD starting at
*OFFSET to descriptor OUT_FD. Set *OFFSET to the IN_FD's file position
following the read bytes. If OFFSET is a null pointer, use the normal
file position instead. Return the number of written bytes, or -1 in
case of error. */
extern ssize_t sendfile (int __out_fd, int __in_fd, off_t *__offset,
size_t __count) __THROW;

in_ fd 参数是待读出内容的文件描述符,out_fd参数是待写入内容的文件描述符。offset参数指定从读人文件流的哪个位置开始读,如果为空,则使用读人文件流默认的起始位置。count参数指定在文件描述符in_fd 和out_fd 之间传输的字节数。sendfile 成功时返回传输的字节数,失败则返回-1并设置errmo。该函数的man手册明确指出,in_fd 必须是一个支持类似mmap函数的文件描述符,即它必须指向真实的文件,不能是socket和管道;而out fd则必须是一个socket。由此可见,sendfile 几乎是专门为在网络上传输文件而设计的。

mmap函数和munmap函数

mmap函数用于申请一段内存空间。我们可以将这段内存作为进程间通信的共享内存,也可以将文件直接映射到其中。munmap 函数则释放由mmap创建的这段内存空间。它们的定义如下:

#include<sys/mman.h>

/* Map addresses starting near ADDR and extending for LEN bytes.  from
OFFSET into the file FD describes according to PROT and FLAGS. If ADDR
is nonzero, it is the desired mapping address. If the MAP_FIXED bit is
set in FLAGS, the mapping will be at ADDR exactly (which must be
page-aligned); otherwise the system chooses a convenient nearby address.
The return value is the actual mapping address chosen or MAP_FAILED
for errors (in which case `errno' is set). A successful `mmap' call
deallocates any previous mapping for the affected region. */ extern void *mmap (void *__addr, size_t __len, int __prot,
int __flags, int __fd, __off_t __offset) __THROW; /* Deallocate any mapping for the region starting at ADDR and extending LEN
bytes. Returns 0 if successful, -1 for errors (and sets errno). */
extern int munmap (void *__addr, size_t __len) __THROW;
  • start参数允许用户使用某个特定的地址作为这段内存的起始地址。如果它被设置成NULL,则系统自动分配一个地址。length 参数指定内存段的长度。

  • prot 参数用来设置内存段的访问权限。它可以取以下几个值的按位或:

    • PROT_READ,内存段可读。
    • PROT_WRITE,内存段可写。
    • PROT_EXEC,内存段可执行。
    • PROT_NONE,内存段不能被访问。
  • flags参数控制内存段内容被修改后程序的行为。它可以被设置为下表中的某些值(这里仅列出了常用的值)的按位或(其中MAP_ SHARED和MAP_PRIVATE 是互斥的,不能同时指定)。

  • fd参数是被映射文件对应的文件描述符。它一般通过open系统调用获得。

  • offset 参数设置从文件的何处开始映射(对于不需要读入整个文件的情况)。

mmap函数成功时返回指向目标内存区域的指针,失败则返回MAP_FAILED((void*)-1)并设置errno。munmap函数成功时返回0,失败则返回-1并设置ermo。

splice函数

splice函数用于在两个文件描述符之间移动数据,也是零拷贝操作。splice 函数的定义如下:

#include <fcntl.h>

ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);

fd_in参数是待输人数据的文件描述符。如果fd_in是一个管道文件描述符,那么off_in参数必须被设置为NULL。如果fd_in 不是一个管道文件描述符(比如socket),那么off_in表示从输人数据流的何处开始读取数据。此时,若off_in 被设置为NULL,则表示从输人数据流的当前偏移位置读人;若off_in 不为NULL,则它将指出具体的偏移位置。fd_out/off_out参数的含义与fd_in/off_in相同,不过用于输出数据流。len 参数指定移动数据的长度;flags参数则控制数据如何移动,它可以被设置为下表中的某些值的按位或。



使用splice函数时,fd_in和fd_out必须至少有一个是管道文件描述符。splice函数调用成功时返回移动字节的数量。它可能返回0,表示没有数据需要移动,这发生在从管道中读取数据(fd_in是管道文件描述符)而该管道没有被写人任何数据时。splice 丽数失败时返回-1并设置erro。常见的ermno如下表所示。

tee函数

tee函数在两个管道文件描述符之间复制数据,也是零拷贝操作。它不消耗数据,因此源文件描述符上的数据仍然可以用于后续的读操作。tee 函数的原型如下:

#include<fcntl.h>

ssize_t tee(int fd_in, int fd_out, size_t len, unsigned int flags);

该函数的参数的含义与splice相同(但fd_in 和fd_out必须都是管道文件描述符)。tee函数成功时返回在两个文件描述符之间复制的数据数量(字节数)。返回0表示没有复制任何数据。tee 失败时返回-1并设置errno。

fcntl函数

fcntl函数,正如其名字(file control)描述的那样,提供了对文件描述符的各种控制操作。另外一个常见的控制文件描述符属性和行为的系统调用是ioctl,而且ioctl比fentI能够执行更多的控制。但是,对于控制文件描述符常用的属性和行为,fentl函数是由POSIX规范指定的首选方法。fentl 函数的定义如下:

#include<fcntl.h>

/* Do the file control operation described by CMD on FD.
The remaining arguments are interpreted depending on CMD. This function is a cancellation point and therefore not marked with
__THROW. */
extern int fcntl (int __fd, int __cmd, ...);

fd参数是被操作的文件描述符,cmd参数指定执行何种类型的操作。根据操作类型的不同,该函数可能还需要第三个可选参数arg。fentl 函数支持的常用操作及其参数如下表所示。





fentl丽数成功时的返回值如表最后一列所示,失败则返回-1并设置ermo。

在网络编程中,fcntl 函数通常用来将-一个文件描述符设置为非阻塞的,如代码所示:

int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL); /*获取文件描述符旧的状态标志*/
int new_option = old_option | O_NONBLOCK; /* 设置非阻塞标志*/
fcnt1(fd, F_SETFL, new_option); /*返回文件描述符旧的状态标志,以便日后恢复该状态标志*/
return old_option;
}

此外,SIGIO和SIURG这两个信号与其他Linux信号不同,它们必须与某个文件描述符相关联方可使用:当被关联的文件描述符可读或可写时,系统将触发SIGIO信号;当被关联的文件描述符(而且必须是一个socket)上有带外数据可读时,系统将触发SIGURG信号。将信号和文件描述符关联的方法,就是使用fcntl 函数为目标文件描述符指定宿主进程或进程组,那么被指定的宿主进程或进程组将捕获这两个信号。使用SIGIO时,还需要利用fcntl设置其O_ASYNC 标志(异步I/0标志,不过SIGIO信号模型并非真正意义上的异步IO模型)。

Linux I/O函数的更多相关文章

  1. Linux C 创建目录函数mkdir相关(转-清新居士)

    I.Linux C 创建目录函数mkdir的mode设置问题 函数原型: #include <sys/stat.h> int mkdir(const char *path, mode_t ...

  2. linux下syscall函数,SYS_gettid,SYS_tgkill

    出处:http://blog.chinaunix.net/uid-28458801-id-4630215.html     linux下syscall函数,SYS_gettid,SYS_tgkill  ...

  3. 对于linux下system()函数的深度理解(整理)

    原谅: http://blog.sina.com.cn/s/blog_8043547601017qk0.html 这几天调程序(嵌入式linux),发现程序有时就莫名其妙的死掉,每次都定位在程序中不同 ...

  4. Linux下c函数dlopen实现加载动态库so文件代码举例

    dlopen()是一个强大的库函数.该函数将打开一个新库,并把它装入内存.该函数主要用来加载库中的符号,这些符号在编译的时候是不知道的.这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了. ...

  5. Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞

    漏洞名称: Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞 CNNVD编号: CNNVD-201312-041 发布时间: 2013- ...

  6. Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘qeth_snmp_command’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-423 发布时间: 2013-11-29 更新时间: 201 ...

  7. Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞

    漏洞名称: Linux kernel ‘aac_send_raw_srb’函数输入验证漏洞 CNNVD编号: CNNVD-201311-422 发布时间: 2013-11-29 更新时间: 2013- ...

  8. Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞

    漏洞名称: Linux kernel ‘lbs_debugfs_write’函数数字错误漏洞 CNNVD编号: CNNVD-201311-421 发布时间: 2013-11-29 更新时间: 2013 ...

  9. Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘xfs_attrlist_by_handle()’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-392 发布时间: 2013-11-29 更新 ...

  10. Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞

    漏洞名称: Linux kernel ‘uio_mmap_physical’函数缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-154 发布时间: 2013-11-13 更新时间: 201 ...

随机推荐

  1. windows 上 cmake 添加 vcpkg 选项

    使用 cmake 编写相关的工程时,工程有时会使用 vcpkg 添加的第三方库,比如 zip 库 查看一些案例后,我发现有些回答不太准确,遂记录下 现在,我们需要在工程中使用 zip_open 函数执 ...

  2. virtualapp启动流程源码分析

    virtualapp启动流程分析 1. 首先是启动本身,执行Vpp 的attachBaseContext @Override protected void attachBaseContext(Cont ...

  3. [BUUCTF][Web][极客大挑战 2019]Havefun 1

    打开靶机的URL,看到一个页面 右键查看源代码,看到有用信息 <html> ... <!-- $cat=$_GET['cat']; echo $cat; if($cat=='dog' ...

  4. django中update_or_create()

    update_or_create()方法中有一个defaults参数 模型字段会根据查询条件进行查询,如果查询到了,那么就用defaults对应的值去更新字段,如果没有查到就用defaults对应的值 ...

  5. 【Azure Function App】遇见无法加载Microsoft.Azure.WebJobs.ParameterBindingData的问题

    问题描述 新部署Azure Funciton代码,遇见无法加载 "Microsoft.Azure.WebJobs.ParameterBindingData" 问题 错误消息:Mic ...

  6. 【Azure 媒体服务】使用编码预设文件(Preset.json)来自定义编码任务 -- 创建视频缩略图

    问题描述 在Azure门户上创建Transform Encoding时候,只能选择 Built-in Preset 编码方式(如:H265ContentAwareEncoding) 在创建编码任务时, ...

  7. Android 安装手机程序有问题/点击runAPP 程序安装不了手机

    可以在 gradle.properties 里添加   android.injected.testOnly=false   点击同步  就可以运行了 如下:

  8. 掌握pandas cut函数,一键实现数据分类

    pandas中的cut函数可将一维数据按照给定的区间进行分组,并为每个值分配对应的标签.其主要功能是将连续的数值数据转化为离散的分组数据,方便进行分析和统计. 1. 数据准备 下面的示例中使用的数据采 ...

  9. 获取一段时间内,以月/季度为单位,第N天在各个月/季度是几几年几月几号

    /** * 获取一段时间内(可跨年),以季度为单位,第N天在各个季度是几月几号 * @param $sTime 时间戳 * @param $eTime 时间戳 * @param $number 第N天 ...

  10. python AI应用开发编程实战 大模型实战基础(数据存储类型列表与字典)(二)

    大模型开发中,需要和自己的业务融合,我们要对自己的数据处理,熟悉外理excle  word  pdf 数据然后处理后可以放到向量数据库,或者直接Assistants API传到大模型引用,不管怎么样数 ...