UNIX高级环境编程读书笔记(chapter14)
来源:互联网 发布:淘宝客推广在哪里设置 编辑:程序博客网 时间:2024/06/05 16:45
第十四章 高级IO
一、引言
本章内容包括非阻塞IO、记录锁、系统V流机制、I/O多路转接(select 和 poll函数)、readv和writev函数以及存储映射I\O,这些都称为高级I/O。
二、非阻塞I/O
非阻塞I/O使我们可以调用open、read和write这样的I/O操作,并使这些操作不会永远阻塞。如果这种操作不能完成,则调用立即出错返回,表示该操作如继续执行将阻塞。
对于一个给定的描述符有两种方法对其指定非阻塞I/O:
(1)如果调用open获得描述符, 则可指定O_NONBLOCK标志;
(2)对于一个已经打开的描述符,则可调用fcntl,由该函数打开O_NONBLOCK文件状态标志。
实例:长的非阻塞write
#include "apue.h"#include <errno.h>#include <fcntl.h>void set_fl(int fd, int flags);void clr_fl(int, int);char buf[50000];intmain(void){ int ntowrite, nwrite; char *ptr; ntowrite = read(STDIN_FILENO, buf, sizeof(buf)); fprintf(stderr, "read %d bytes\n", ntowrite); set_fl(STDOUT_FILENO, O_NONBLOCK); ptr = buf; while (ntowrite > 0) { errno = 0; nwrite = write(STDOUT_FILENO, ptr, ntowrite); fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno); if (nwrite > 0) { ptr += nwrite; ntowrite -= nwrite; } } clr_fl(STDOUT_FILENO, O_NONBLOCK); exit(0);}void set_fl(int fd, int flags){ int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0) { err_sys("fcntl F_GETFL error"); } val |= flags; if (fcntl(fd, F_SETFL, val) < 0) { err_sys("fcntl F_SETFL error"); }}void clr_fl(int fd, int flags){ int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0) { err_sys("fcntl F_GETFL error"); } val &= ~flags; if (fcntl(fd, F_SETFL, val) < 0) { err_sys("fcntl F_SETFL error"); }}
此程序是一个非阻塞I/O的实例,它从标准输入读500000字节,并试图将它们写到标准输出上。该程序现将标准输出设置为非阻塞的,然后用for循环进行输出,每次write调用的结果都在标准出错上打印。函数clr_fl类似于set_fl,但与set_fl的功能相反,它清除一个或多个标志位。
三、记录锁
进程有时需要确保它正在单独写一个文件,为了向进程提供这种功能,商用UINIX系统提供了记录锁机制。
记录锁的功能是:当一个进程正在读或修改文件的某个部分时,它可以组织其他进程修改同意文件区。
四、fantl记录锁
int fcntl(int fileds, int cmd, ... /*struct lock *flockptr*/);
对于记录锁,cmd是F_GETLK, F_SETLK,或F_SETLKW,第三个参数是一个指向flock结构的直针:
struct flock{
short l_type;
off_t l_start;
short l_whence;
off_t l_len;
pid_t l_pid;
};
多个进程在一个给定的自字节上可以有一把共享的读锁,但是一个给定的字节上只能有一个进程独用的一把写锁,进一步而言,如果在一个给定字节上已经有一把或多把读锁,则不能在该字节上再加写锁;如果在一个字节上已经有一把独占性的写锁,则不能对它再加任何读锁。
F_GETLK:判断由flockkptr所描述的锁是否会被另外一把锁排斥(阻塞)。
F_SETLK:设置由flockptr所描述的锁。
F_SETLKW:这是F_SETLK的阻塞版本。
实例:加锁或解锁一个文件区域的函数:
&&#include "apue.h"#include <fcntl.h>intlock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len){ struct flock lock; lock.l_type = tupe; lock.l_start = offset; lock_whence = whence; lock_l.len = len; return (fcntl(fd, cmd, &lock));}&&
实例:测试一个锁状态的函数
&&#include "apue.h"#include <fcntl.h>pid_tlock_test(int fd, int type, off_t offset, int whence, off_t len){ struct flock flock; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; if (fcntl(fd, F_GETLK) < 0) { err_sys("fcntl error"); } if (lock.l_type == FUNLCK) { return 0; } return (lock.l_pid);}&&
实例:死锁检测实例
如果两个进程相互等待对方持有并且锁定的资源时,则这两个就处于死锁状态。
&&#include "apue.h"#include <fcntl.h>#include "tellwait.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; return (fcntl(fd, cmd, &lock));}static void lockabyte(const char *name, int fd, off_t offset){ if (writew_lock(fd, offset, SEEK_SET, 1) < 0) { err_sys("%s: writew_lock error", name); } printf("%s: got the lock, byte %ld\n", name, offset);}int main(void){ int fd; pid_t pid; if ((fd = creat("templock", FILE_MODE)) < 0) { err_sys("creat error"); } if (write(fd, "ab", 2) != 2) { err_sys("write error"); } TELL_WAIT(); if ((pid = fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { lockabyte("child", fd, 0); TELL_PARENT(getppid()); WAIT_PARENT(); lockabyte("child", fd, 1); } else { lockabyte("parent", fd, 1); TELL_CHILD(pid); WAIT_CHILD(); lockabyte("parent", fd, 0); } exit(0);}&&
关于记录锁的自动继承和释放有三条规则:
(1)锁与进程和文件两方面有关。
(2)由fork产生的子进程不继承父进程所设置的锁。
(3)在执行exec后,新程序可以继承原执行程序的锁。
实例:在文件整体上加锁
&&#include <unistd.h>#include <fcntl.h>int lockfile(int fd){ struct flock fl; fl.l_type = F_WRLCK; fl.l_start = 0; fl.l_whence = SEEK_SET; fl.l_len = 0; return(fcntl(fd, f_SETLK, &fl));}&&
六:在文件尾端处加锁
负的长度值表示在指定偏移量之前的字节数。
七、建议性锁和强制性锁
考虑数据库访问例程库。如果该库中所有函数都以一致的方法处理记录锁,则称使用这些函数访问数据库的任何进程集为合作进程。
强制性锁使内核对每一个open、read和write系统调用都进行检查,检查调用进程对正在访问的文件受否违背了某一把锁的作用。
实例:确定是否支持强制性锁
&&#include "apue.h"#include <errno.h>#include <fcntl.h>#include <sys/wait.h>#include "tellwait.h"int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len);void set_fl(int fd, int flags);void clr_fl(int fd, int flags);int main(int argc, char *argv[]){ int fd; pid_t pid; char buf[5]; struct stat statbuf; if (argc != 2) { fprintf(stderr, "uasge: %s filename\n", argv[0]); exit(1); } if ((fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0) { err_sys("open error"); } if (write(fd, "abcdef", 6) != 6) { err_sys("write error"); } if (fstat(fd, &statbuf) < 0) { err_sys("fstat error"); } if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0) { err_sys("fchmod error"); } TELL_WAIT(); if ((pid == fork()) < 0) { err_sys("fork error"); } else if (pid == 0) { WAIT_PARENT(); set_fl(fd, O_NONBLOCK); if (read_lock(fd, 0, SEEK_SET, 0) != -1) { err_sys("child: read_lock succeeded"); } printf("read_lock of already-locked region returns %d\n", errno); if (lseek(fd, 0, SEEK_SET) == -1) { err_sys("lseek error"); } if (read(fd, buf, 2) < 0) { err_ret("read failed (mandatory locking works)"); } else { printf("read OK (no mandatory locking), buf = %2.2s\n", buf); } } else { if (write_lock(fd, 0, SEEK_SET, 0) < 0) { err_sys("write_lock error"); } TELL_CHILD(pid); if (waitpid(pid, NULL, 0) < 0) { err_sys("waitpid error"); } } exit(0);}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; return (fcntl(fd, cmd, &lock));}void set_fl(int fd, int flags){ int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0) { err_sys("fcntl F_GETFL error"); } val |= flags; if (fcntl(fd, F_SETFL, val) < 0) { err_sys("fcntl F_SETFL error"); }}void clr_fl(int fd, int flags){ int val; if ((val = fcntl(fd, F_GETFL, 0)) < 0) { err_sys("fcntl F_GETFL error"); } val &= ~flags; if (fcntl(fd, F_SETFL, val) < 0) { err_sys("fcntl F_SETFL error"); }}&&
八、STREAMS
流是系统V提供的构造内核设备驱动程序和网络协议包的一种通用方法。
流在用户进程和设备驱动程序之间提供了一条全双工通路。流无需和实际硬件设备直接会话,流也可以用来构造伪设备驱动程序。
在流首之下可以压入处理模块。这可以用ioctl命令实现。用第三章说明的函数访问流,它们是:open、close、read、write和ioctl。在VR3内核中增加了3个支持流的新函数(getmsg、putmsg和poll),在SVR4中又加了两个处理流内不同优先级波段休息的函数(getpsmg和putpmasg)。
(1)STREAMS的所有输入和输出都基于消息。流首和用户进程使用read、write、ioctl、getmsg、getpmsg、 putpmsg和putmsg交换消息。在流首、各处理模块和设备驱动程序之间,消息可以顺流而下,也可以逆流而 上。控制信息和数据由strbuf结构指定:
struct strbuf
{
int maxlen;
int len;
char *buf;
};
在我们所使用的函数中,只涉及三种消息类型,它们是:
M_DATA (I/O的用户数据);
M_PROTO(协议控制信息);
M_PCPROTO(最高优先级协议控制信息)。
(2)putymsg和putpmsg
用于将STREAMS消息写至流中,这两个函数的区别是:后者允许对指定的消息指定一个优先级波段。
(3)STREAMS ioctl操作
ioctl函数能做其它I/O函数不处理的事情,STREAMS系统就继承了这种传统。
实例:isastream函数
#include <stropts.h>&&#include <unistd.h>int isastream(int fd){ return(ioctl(fd, I_CANPUT, 0) != 1);}&&
实例:测试isastream函数
&&#include "apue.h"#include <fcntl.h>#include <stropts.h>#include <unistd.h>int isastream(int fd){ return(ioctl(fd, I_CANPUT, 0) != -1);}int main(int argc, char *argv[]){ int i, fd; for (i = 1; i < argc; i++) { if ((fd = open(argv[i], O_RDONLY)) < 0) { err_ret("%s: can't open", argv[i]); continue; } if (isastream(fd) == 0) { err_ret("%s: not a stream", argv[i]); } else { err_msg("%s: streams device", argv[i]); } } exit(0);}&&
编译APUE:apue.2ehttp://www.cnblogs.com/feiling/archive/2012/02/15/2353286.html
实例:列表流中的模块名
如果ioctl的参数request是L_LIST, 则系统返回已压入该流所有模块的名字,包括最顶端的驱动程序。
&&#include "apue.h"#include <fcntl.h>#include <stropts.h>int main(int argc, char *argv[]){ int fd, i, nmods; struct str_list list; if (argc != 2) { err_quit("usage: %s <pathname>", argv[0]); } if ((fd = open(argv[1], O_RDONLY)) < 0) { err_sys("can't open %s", argv[i]); } if (isastream(fd) == 0) { err_quit("%s is not a stream", argv[1]); } if ((nmods = ioctl(fd, I_LIST, (void *) 0)) < 0) { err_sys("I_LIST error for nmods"); } printf("#modules = %d\n", nmods); list.sl_modlist = calloc(nmods, sizeof(struct str_mlist)); if (list.sl_modlist == NULL) { err_sys("calloc error"); } list.sl_nmods = nmods; if (ioctl(fd, I_LIST, &list) < 0) { err_sys("I_LIST error for list"); } for (i = 1; i <= nmods; i++) { printf(" %s: %s\n", (i == nmods) ? "driver" : "module", list.sl_modlist++->l_name); } exit(0);}&&
(4)写(write)至STREAMS设备
(5)写模式
可以用两个ioctl命令取得和设置一个流的写模式。如果将request设置为I_GWRPORT,第三个参数设置为指向一 个整型变量的指针,则该流的当前写模式在该整型量中返回。
(6)getmsg和getpmsg函数
使用read、getmsg和getpmsg函数从流首读STREAMS消息。
(7)读模式
实例:用getmsg将标准输入复制到标准输出
&&#include "apue.h"#include <stropts.h>#define BUFFSIZE 4096int main(void){ int n, flag; char ctlbuf[BUFFSIZE], datbuf[BUFFSIZE]; struct strbuf ctl,dat; ctl.buf = ctlbuf; ctl.maxlen = BUFFSIZE; dat.buf = datbuf; dat.maxlen = BUFFSIZE; for ( ; ; ) { flag = 0; if ((n = getmsg(STDIN_FILENO, &ctl, &dat, &flag)) < 0) { err_sys("getmsg error"); } fprintf(stderr, "flag = %d, ctl.len = %d, dat.len = %d\n", flag, ctl.len, dat.len); if (dat.len == 0) { exit(0); } else if (dat.len > 0) { if (write(STDOUT_FILENO, dat.buf, dat.len) != dat.len) { err_sys("write error"); } } }}&&
九、I/O多路转接
poll、pselect和select这三个函数使我们能够执行I/O多路转接。
在所有依从POSIX的平台上,select函数使我们可以执行I/O多路转接。
poll函数类似于select,但是其程序员接口则有所不同。
十、readv和writev函数
readv和write函数用于在一次函数调用中读、写多个非连续缓冲区。
十一、readn和writen函数
readn和writen的功能是读、写指定的n字节数据,并处理返回值小于要求值的情况。这两个函数只是按需多次调用read和write直至读、写了n字节数据。
实例:readn和writen函数
&&#include "apue.h"ssize_treadn(int fd, void *ptr, size_t n){ size_t nleft; ssize_t nread; nleft = n; 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);}ssize_twriten(int fd, const void *ptr, size_t n){ size_t nleft; ssize_t nwritten; nleft = n; while(n < 0) { if ((nwritten = write(fd, ptr, nleft)) < 0) { if (nleft == n) { return(-1); } else { break; } } nleft -= nwritten; ptr += nwritten; } return(n - left);}&&
十二、存储映射I/O
存储映射I/O使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区中取数据,就相当于读文件中的相应字节。与此类似,将数据存入缓冲区,则相应字节就自动的写入文件。这样就可以在不使用read和write的情况下执行I/O.
实力:用存储映射I/O复制一个文件(类似于cp(1)命令)
&&#include "apue.h"#include <fcntl.h>#include <sys/mman.h>int main(int argc, char *argv[]){ int fdin, fdout; void *src, *dst; struct stat statbuf; if (argc != 3) { err_quit("usage: %s <fromfile> <tofile>", argv[0]); } if ((fdin = open(argv[1], O_RDONLY)) < 0) { err_sys("can't open %s for reading", argv[1]); } if ((fdout = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0) { err_sys("can't create %s for writing", argv[2]); } if (fstat(fdin, &statbuf) < 0) { err_sys("fstat error"); } if (lseek(fdout, statbuf.st_size - 1, SEEK_SET) == -1) { err_sys("lseek error"); } if (write(fdout, "", 1) != 1) { err_sys("write error"); } if ((src = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, fdin, 0)) == MAP_FAILED) { err_sys("mmap error"); } if ((dst = mmap(0, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fdout, 0)) == MAP_FAILED) { err_sys("mmap error for output"); } memcpy(dst, src, statbuf.st_size); exit(0);}&&
- UNIX高级环境编程读书笔记(chapter14)
- 《Unix环境高级编程》读书笔记(1)
- Unix环境高级编程读书笔记(一)
- UNIX高级环境编程读书笔记(chapter4)
- UNIX高级环境编程读书笔记(chapter5)
- UNIX高级环境编程读书笔记(chapter7)
- UNIX高级环境编程读书笔记(chapter8)
- UNIX高级环境编程读书笔记(chapter9)
- UNIX高级环境编程读书笔记(chapter10)
- UNIX高级环境编程读书笔记(chapter11)
- UNIX高级环境编程读书笔记(chapter12)
- UNIX高级环境编程读书笔记(chapter13)
- 《unix环境高级编程》 读书笔记 (1)
- 《unix环境高级编程》 读书笔记 (2)
- 《unix环境高级编程》 读书笔记 (3)
- 《unix环境高级编程》 读书笔记 (4)
- 《unix环境高级编程》 读书笔记 (5)
- 《unix环境高级编程》 读书笔记 (6)
- Bochs使用[练习自己动手写操作系统]
- C中字符串处理
- [ACM] hdu 2602 Bone Collector(01背包)
- 【转】通过Python脚本自动发布ArcGIS服务
- Hadoop2.2.0伪分布式安装及测试笔记
- UNIX高级环境编程读书笔记(chapter14)
- strmiids.lib 导致的链接错误
- 一个朋友对游戏的建议
- android 下拉刷新
- Dimensional Object的ROLAP Implementation和MOLAP Implementation的区别以及Deployment Option的说明
- 浙大PAT 3-08. 堆栈模拟队列 (解题思路)
- 归并排序—java实现
- JQuery EasyUI Combobox联动
- 宽字节 多字节 单字节 的问题