APUE高级IO

pag355
低速系统调用
1、某些文件类型( 管道、终端设备、网络设备)的数据并不存在,则 读操作可能会使调用者永远阻塞。
2、数据不能立即被上诉同样类型的文件接受(由于在 管道中无空间、网络流控制等),则 写操作也会使调用者永远阻塞
3、在某种条件发生之前,打开某些类型的文件会被阻塞
4、对已加上强制性记录锁的文件进行读写
5、某些ioctl操作
6、某些进程间通信函数

虽然读写磁盘文件会使调用者在短暂时间内阻塞,但并不能将与磁盘IO有关的系统调用视为低速。

指定一个给定描述符为非阻塞IO:
1、open获得描述符,并指定O_NONBLOCK
2、调用fcntl,打开O_NONBLOCK,先获取,在或,再改变

记录锁( 字节范围锁):当一个进程正在读或修改文件的某个部分时,他可以阻止其他进程修改同一文件区。(锁定的只是文件中的一个区域)

不同进程提出锁请求:
多进程在一个给定的字节上可以有一把共享的读锁,但是在一个给定字节上只能有一个进程独用的一把写锁。
如果在一个给定的字节上已有一把或多把读锁,则不能在该字节上再加写锁。如uozai一个字节上已经有一把独占性的写锁,则不能再对他加任何读锁。
单个进程提出多个锁请求情况:
如果一个进程对一个文件区间已经有了一把锁,后来该进程又企图在同一文件区间再加一把锁,那么新锁将替换老锁。(调用进程绝不会阻塞在自己持有的锁上)
加读锁时,该描述符必须时读打开;加写锁时,该描述符必须时写打开。

应先用F_GETLK测试能否建立一把锁,然后用F_SETLK和F_SETLKW试图建立一把锁。
F_GETLK是检测其他进程是否有现存锁,不能报告调用进程自己的锁。

pag363
锁的隐含继承和释放:
1、当一个进程终止时,他所建立的锁全部释放;任何时候关闭一个描述符时,则该进程( 仅限于调用进程)通过 这一描述符可以引用的文件上的任何一把锁都被释放。(也就是说作用 对象变成了文件i节点,而 不是文件描述符,或者文件表项
如:
fd1 = open( pathname, ... );
read_lock( fd1, ... );
fd2 = dup( fd1 ); (指向同一文件表项)  //fd2 = open(pathname, ... );(指向新建文件表项)
close( fd2 );
在close(fd2)后,fd1上设置的锁被释放
2、由fork产生的子进程不继承父进程所设置的锁。(记录锁本身就是为了进程间能独立写文件)
3、在执行exec后,新进程可以继承原执行程序的锁(没有设置close-on-exec标志)

锁的释放:
关闭进程中的相应描述符,内核会从该描述符所关联的i节点开始,逐个检查lockf链表中各项,并释放由 调用进程持有的各把锁。( 文件i节点会为每个进程维护一个锁结构

对一个特定文件打开其设置组ID位并关闭其组执行位,则对该文件开启了强制性锁机制。
fstat( fd, &statbuf );
fchmod( fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID );

pag376
处理文件描述符和文件状态标志:
先取当前写模式值,然后修改他(与或并),而不只是将写模式设置为某个绝对值。

pag382
IO多路转换:
先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个已准备好进行IO时,该函数才返回。在返回时,它告诉进程哪些描述符已准备好进行IO。
传向select的参数告诉内核:
1、我们所关心的描述符
2、对于每个描述符我们所关心的状态(是否读一个给定的描述符?是否想写一个给定的描述符?是否关心一个描述符的异常状态?)
3、愿意等待多长时间(可以永远等待,等待一个固定量时间,或完全不等待)
从select返回告诉我们:
1、已准备好的描述符的数量
2、对于读写或异常这三个状态中的每一个,哪些描述符已准备好

int selet( int maxfdp1, fd_set*restrict readfds, fd_set *restrict writefds, fd_set *restrictexceptfds, struct timeval *restrict tvptr );(maxfdp1为最大描述符+1)
int pselet( int maxfdp1, fd_set*restrict readfds, fd_set *restrict writefds, fd_set *restrictexceptfds, const struct timespec *restrict tvptr, const sigset_t*restrict sigmask ); //多了信号屏蔽这以块功能
对fd_set处理函数:
int FD_ISSET( int fd, fd_set *fdset);//测试指定位是否设置
void FD_CLR( int fd, fd_set *fdset);//清除指定位
void FD_SET( int fd, fd_set *fdset);//设置指定位
void FD_ZERO( fd_set *fdset);//所有位清零
常用方式:
fd_set rset;
int        fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(STDIN_FILENO, &rset);
从select返回后:
if (FD_ISSET(fd, &rset)) {
}

int poll( struct pollfd fdarray[],nfds_t nfds, int timeout );
函数构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。
struct pollfd {
int fd;
short events;
short revents;
}
通过events告诉内核我们对该描述符关心的是什么。返回时,内核设置revents成员,以说明对于该描述符已经发生了什么事件。

ssize_t readv( int filedes, conststruct iovec *iov, int iovcnt );
ssize_t writev( int filedes, conststruct iovec *iov, int iovcnt );
在一次函数调用中读写多个非连续缓冲区。

管道、FIFO以及某些设备,特别时终端、网络和STREAMS设备有下列两种性质:
1、一次read操作所返回的数据可能少于所要求的数据,即使还没达到文件尾端也可能是这样的。这不是一个错误,应当继续该设备。
2、一次write操作的返回值也可能少于指定输出的字节数。这不是一个错误,应当继续该设备。

pag390
存储映射IO
使一个磁盘文件与存储空间中的一个缓冲区相映射。于是当从缓冲区取数据,就相当于读文件中的相应字节。将数据存入缓冲区,则相应字节就自动写入文件。
void * mmap( void *addr, size_t len,int prot, int flag, int fileds, off_t off ); //将给定文件映射到存储区域
通常将addr设置为0,表示由系统选择该映射区的起始地址。
off和addr通常指定为0.

不能用其在网络设备或终端设备等一些设备之间进行复制。

在调用fork之后,子进程继承存储映射区(存储映射区是父进程地址空间的一部分,子进程将复制),同样,因为exec之后子进程采用全新的地址空间,所以不继承存储空间。

int mprotect( void *addr, size_t len,int prot );//更改一个现存映射存储区
int msync( void *addr, size_t len, intflags );//将修改的页冲洗到被映射的文件中
int munmap( caddr_t addr, size_t len); //解除存储区映射(关闭文件描述符并不解除映射区)调用munmap不会使映射区的内容写到磁盘文件上。
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>

#define NLOOPS		1000
#define SIZE		sizeof(long)

static int
updata( long *ptr )
{
	return( (*ptr)++ );
}

int
main( void )
{
	int		fd, i = 0, counter;
	pid_t	pid;
	void 	*area;

	if( (fd = open("/dev/zero", O_RDWR)) < 0 )
		perror( "open" );
	if( (area = mmap(0, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED )
		perror( "mmap error" );
	close( fd );

	TELL_WAIT();

	if( (pid = fork()) < 0 )
		perror( "fork" );
	else if( pid > 0 )
	{
		for( i=0; i < NLOOPS; i+=2 )
		{
			if( (counter = updata((long *)area)) != i ) {
				printf( "p: e %d, g %d\n", i, counter );
				exit(1);
			}
			printf("p area: %ld\n", *(long *)area);
			TELL_CHILD( pid );
			WAIT_CHILD();
			sleep(1);
		}
	}
	else
	{
		for( i=1; i < NLOOPS + 1; i += 2 )
		{	
			WAIT_PARENT();
			if( (counter = updata((long *)area)) != i ) {
				printf( "c: e %d, g %d\n", i, counter );
				exit(1);
			}
			printf("c area: %ld\n", *(long *)area);
			TELL_PARENT( getppid() );	
			sleep(1);
		}
	}
	exit( 0 );
}
输出:
p area: 1
c area: 2
p area: 3
c area: 4
p area: 5
c area: 6
p area: 7
c area: 8
p area: 9
c area: 10
...




你可能感兴趣的:(APUE高级IO)