APUE 学习笔记(九) 高级I/O

1. 非阻塞I/O

低速系统调用时可能会使进程永远阻塞的一类系统调用,包括以下调用:

(1)某些文件类型你(网络socket套接字、终端设备、管道)暂无可使用数据,则读操作可能会使调用者永远阻塞

(2)如果数据不能立即被(1)中文件类型接受,则写操作会使调用者永远阻塞

(3)某些进程间通信函数

非阻塞I/O使我们可以调用open、read、write这样的I/O操作,并使这些操作不会永远阻塞,如果这种操作不能完成,则调用立即出错返回

对于一个给定的文件有两种方法对其指定非阻塞I/O:

(1)调用open打开文件时,指定 O_NONBLOCK标志

(2)对于一个打开的描述符,可以调用fcntl函数,将文件设置为非阻塞I/O

2. fcntl记录锁

当一个进程正在读或修改文件的某个部分时,它可以组织其它进程修改同一文件区,它锁定的只是文件的一个区域(也可能是整个文件)


struct flock {
short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
off_t l_start; /* offset in bytes, relative to l_whence */
short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
off_t l_len; /* length in bytes, 0 means lock to EOF */
pid_t l_pid; /* returned with F_GETLK */
};


#include <fcntl.h>
int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len)
{
struct flock lock;
lock.l_type = type;
lock.l_start = offset;
lock.l_whence = whence;
lock.l_len = len;
int ret = fcntl(fd, cmd, &lock);
return ret;
}

如果两个进程相互等待对方持有并且锁定的资源时,则这两个进程处于死锁状态

3. select

#include <select.h>
int select(int maxfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);

中间三个参数 readfds, writefds,
exceptfds是指向描述符集的指针,每个描述符集存放在一个fd_set中:

这三个参数指针任意一个可以为空指针,表示对相应状态并不关心。如果三个指针都是空指针,则select提供了比sleep更精确的计时器,因为sleep只能等待整数秒,

而select的struct timeval可以精确到微秒

int  FD_ISSET(int fd, fd_set* set);
void FD_CLR(int fd, fd_set* set);
void FD_SET(int fd, fd_set* set);
void FD_ZERO(fd_set* set);

select的第一个参数maxfds是三个描述符集中最大的fd数值加1,也可以将此参数设置为FD_SETSIZE,表明最大的描述符数


fd_set readset, writeset;
FD_ZERO(&readset);
FD_ZERO(&writeset);
FD_SET(0, &readset);
FD_SET(3, &readset);
FD_SET(1, &writeset);
FD_SET(2, &writeset);
select(4, &readset, &writeset, NULL, NULL);

因为描述符编号从0开始,所以要在最大描述符编号值加1,第一个参数实际上就是要检查的描述符数(从描述符0开始)

select有3个可能的返回值:

(1)返回值-1表示出错

(2)返回值0表示没有描述符准备好或者超时

(3)返回值为正数表示已经准备好的描述符数,该值是三个描述符集中已准备好的描述符之和,如果同一描述符已经准备好读和写,那么返回值中计为2

4. readn和writen


int readn(int fd, char* ptr, size_t n)
{
size_t nleft = n;
int nread = 0;
while (nleft > 0) {
if ((nread = read(fd, ptr, nleft)) < 0) {
if (nleft == n) {
return -1;
} else {
break;
}
} else if (nread == 0) {
break;
}
nleft -= nread;
ptr += nread;
}
return n - nleft;
}


int writen(int fd, char* ptr, size_t n)
{
size_t nleft = n;
int nwrite = 0;
while (nleft > 0) {
if ((nwrite = write(fd, ptr, nleft)) < 0) {
if (nleft == n) {
return -1;
} else {
break;
}
} else if (nwrite == 0) {
break;
}
nleft -= nwrite;
ptr += nwrite;
}
return n - nleft;
}

5. 存储映射I/O mmap

Memory-mapped I/O
使一个外存磁盘文件与内存空间中的一个缓冲区相映射,当从缓冲区中取数据,就相当于读磁盘文件中的相应字节,写情况亦如此

#include <sys/mman.h>
void* mmap(void* addr, size_t len, int prot, int flag, int fd, off_t off);

addr参数指定映射存储区的起始位置,通常设置为0,表示由内核选择该映射区的起始地址

fd指定被映射文件的描述符,len是映射的字节数,off是映射字节在文件中的起始偏移量

addr和off的值通常应当是系统虚拟页长度的倍数,因为addr和off常常指定为0,所以这要求一般不重要

因为映射文件的起始偏移量受系统虚拟页大小限制。假定文件长12字节,系统页长512字节,则系统通常提供512字节的映射区,其中后500字节被设置为0,对该500字节的任何修改都不会在文件中反映出来。


#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
int fdin = 0;
int fdout = 0;
char* src = NULL;
char* dst = NULL;
struct stat statbuf;
if (argc != 3) {
fprintf(stderr, "usage: %s <fromfile> <tofile>\n", argv[0]);
return 1;
}
if ((fdin = open(argv[1], O_RDONLY)) < 0) {
fprintf(stderr, "cannot open %s for reading\n", argv[1]);
}
if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC)) < 0) {
fprintf(stderr, "cannot creat %s for writing\n", argv[2]);
}
if (fstat(fdin, &statbuf)) {
fprintf(stderr, "fsat error\n");
}

if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) {
fprintf(stderr, "lseek error\n");
}
if (write(fdout, "", 1) != 1) {
fprintf(stderr, "write error\n");
}

if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) {
fprintf(stderr, "mmap error for input\n");
}

if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED) {
fprintf(stderr, "mmap error for output\n");
}
memcpy(dst, src, statbuf.st_size);
munmap(src, statbuf.st_size);
munmap(dst, statbuf.st_size);
return 0;
}

APUE 学习笔记(九) 高级I/O,布布扣,bubuko.com

时间: 2024-12-24 23:23:32

APUE 学习笔记(九) 高级I/O的相关文章

APUE 学习笔记(十) 高级I/O

1. Unix IPC(InterProcess Communication) 同一主机的各个进程间的IPC:管道.FIFO.消息队列.信号量.共享存储器 不同主机上的各个进程间IPC:socket套接字 2. 管道 管道进行IPC有两个局限: (1) 半双工,即数据只能在一个方向上流动 (2) 只能在具有公共祖先的进程之间使用.通常,一个管道由一个进程创建,然后该进程调用fork,此后 父子进程之间可以使用该管道 fstat函数对管道的每一端都返回一个FIFO类型的文件描述符,可以用S_ISF

Hadoop学习笔记(7) ——高级编程

Hadoop学习笔记(7) ——高级编程 从前面的学习中,我们了解到了MapReduce整个过程需要经过以下几个步骤: 1.输入(input):将输入数据分成一个个split,并将split进一步拆成<key, value>. 2.映射(map):根据输入的<key, value>进生处理, 3.合并(combiner):合并中间相两同的key值. 4.分区(Partition):将<key, value>分成N分,分别送到下一环节. 5.化简(Reduce):将中间结

APUE学习笔记:第九章 进程关系

9.1 引言 本章将更详尽地说明进程组以及POSIX.1引入的会话的概念.还将介绍登陆shell(登录时所调用的)和所有从登陆shell启动的进程之间的关系. 9.1 终端登陆 系统管理员创建通常名为/etc/ttys的文件,其中每个终端设备都有一行,每一行说明设备名传递给getty程序的参数.当系统自举时,内核创建进程ID为1的进程,依旧是init进程.init进程使系统进入多用户状态.init进程读文件/etc/ttys,对每一个允许登陆的终端设备,init调用一次fork,所生成的子进程则

python学习笔记九——文件与目录

1.python进行文件读写的函数是open或file类 mode:r  只读 r+   读写 w  写入,先删除原文件,再重新写入,如果文件没有则创建 w+  读写,先删除原文件,再重新写入,如果文件没有则创建(可写入和输出) a  写入,在文件末尾追加新的内容,文件不存在则创建 a+  读写,在文件末尾追加新的内容,文件不存在则创建 b  打开二进制文件,可与r,w,a,+结合使用 U  支持所有的换行符号,"\r","\n","\r\n"

APUE学习笔记:第七章 进程环境

7.1 引言 本章将学习:当执行程序时,其main函数是如何被调用的:命令行参数是如何传送给执行程序的:典型的存储器布局是什么样式:如何分配另外的存储空间:进程如何使用环境变量:各种不同的进程终止方式等:另外还将说明longjmp和setjmp函数以及它们与栈的交互作用:还将介绍研究进程的资源限制 7.2 main函数 C程序总是从main函数开始执行.当内核执行C程序时,在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址——这是由连接编辑器设置的,而连接编

APUE学习笔记:第四章 文件和目录

4.1 引言 本章将描述文件的特征和文件的性质 4.2 stat.fstat和lstat函数 #include<sys/stat.h> int stat(const char *restrict pathname,struct stat *restrict buf); int fstat(int filedes,struct stat *buf) int lstat(const char *restrict pathname,struct stat *restrict buf); 三个函数的返

angular学习笔记(九)-css类和样式3

再来看一个选择li列表的例子: 点击li中的任意项,被点击的li高亮显示: <!DOCTYPE html> <html ng-app> <head> <title>6.3css类和样式</title> <meta charset="utf-8"> <script src="../angular.js"></script> <script src="scri

angular学习笔记(九)-css类和样式2

在上一个例子中,元素的类名使用拼接的方法,这样,类名中就不得不带有true或false,并且不易维护,所以,angular使用ng-class属性来控制元素的类名: 我们来看一个小例子,点击error按钮,顶部提示错误框,点击warning按钮,顶部提示警告框. 错误框的类名是.err,警告框的类名是.warn: <!DOCTYPE html> <html ng-app> <head> <title>6.2css类和样式</title> <

APUE 学习笔记(二) 文件I/O

1. 文件I/O 对于内核而言,所有打开的文件都通过文件描述符引用,内核不区分文本文件和二进制文件 open函数:O_RDONLY  O_WRONLY  O_RDWR create函数: close函数:关闭一个文件时还会释放该进程加在该文件上的所有记录锁 lseek函数:显式地为一个打开的文件设置其偏移量 每个打开的文件都有一个与其相关联的 "当前文件偏移量",用以度量从文件开始处计算的字节数,通常,读.写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数 文件偏移量可以大于