《APUE》Chapter 3 File I/O(学习笔记加上自己的代码)

时间:2022-05-18 22:50:38

File I/O


文件I/O

在Unix系统中,文件I/O大多数的使用五大函数来实现:open,read,write,lseek 和close.

术语unbuffered的意思是每个read或者write函数向内核请求一个系统调用


对于内核而言,每一个打开的文件都有相应的file descriptors(非负整数).


open 函数

#include<fcntl.h>

int open(const char* pathname,int oflag,......,/*  mode_t mode  */);

如果正确返回file descriptor,否则返回 -1

pathname是文件名的路径,oflag是option flag,选项标签这里的选项是宏定义

oflag宏:

O_RDONLY    只可读

O_WRONLY  只可写

O_RDWR        可读写

O_APPEND    对于每个write函数的写入点从当前文件末尾开始

O_CREAT      如果文件不存在,就创建这个文件。

当然这个对于open函数使用的选项需要和函数的第三个参数mode一起,mode用于描述新建文件的权限

O_EXCL      如果使用了O_CREAT宏,并且这个文件已经存在了,于是 产生一个错误

O_TURNC   如果文件存在了并且被每一个write或者read函数成功的打开了,这个选项将“命令“open函数去清空所有文件内容,使之文本长度为0.

O_NOCTTY  如果pathname提及的是terminal设备,不允许设备被作为controling terminal 

O_DSYNC   使得write等待每一个物理I/O完成自己的工作,但是不等待那种不影响读取刚写入的数据的文件

O_RSYNC     在任何write操作对于某一文件的同一部分写入操作完成之前,让被read操作的file descriptor所指的文件保持等待

/***************
just a demo
***************/
#include"apue.h"
#include"fcntl.h"
#include"stdio.h"

int main()
{
	int file_descriptor = 0;

	if(file_descriptor = open("./text.t",O_RDONLY) < 0)
	{
		printf("open error\nprocess end\n");
		return 0;
	}
	else
	{
		printf("open success\n");
		close(file_descriptor);//if there is not close function ,it would be OK...
	}
	return 0;
}


creat 函数

#include<fcntl.h>

int creat(const char* pathname,mode_t mode);

如果函数成功就返回打开的文件的file descriptor,否则返回-1

mode :

 S_IRUSR   用户可读

 S_IWUSR   用户可写

 S_IXUSR   用户可执行

#include<apue.h>
#include<unistd.h>
#include<fcntl.h>

#define RWX  (S_IRUSR | S_IWUSR | S_IXUSR)

int main()
{
	int file_descriptor = 0;
	
	if((file_descriptor = creat("./hello.t",RWX)) < 0)
	{
		printf("creat fail\nprocess end");
		return 0;
	}
	else
	{
		printf("creat successful\n");
	}
	
	close(file_descriptor);
	return 0;
}



close函数

#include<unistd.h>

int  close(int filedes)

如果成功返回0,否则返回-1


每个文件都有一个相关的“当前文件偏置”(current file offset),通常是一个非负整数。它测量表示的意义是从文件开头到结束的地方这段区域内,所含字节数。

read函数write函数的读写操作通常从这个offset开始

通常(default情况),offset被初始化为0.


lseek函数

#include"unistd.h"//unistd.h - standard symbolic constants and types

off_t lseek(int files,  off_t offset,   int whence)

如果成功返回file offset,否则返回-1

whence宏:

SEEK_SET 将文件的offset设置为文件开头

SEEK_CUR将文件的offset设置为当前值加上lseek的第二参数offset的值(offset必须为正数)

SEEK_END将文件的offset设置为文件的大小加上lseek的第二个参数offset的值(offset可正可负)

/*
just a demo for lseek
*/
#include"apue.h"
#include"stdio.h"
#include"unistd.h"
#include"fcntl.h"

int main()
{
	off_t file_off_set = 0;
	int file_descriptor = 0;
	
	if((file_descriptor = open("./text.t",O_RDONLY)) < 0)
	{
		printf("open error\nProcess end\n");
		return 0;
	}
	
	if((file_off_set = lseek(file_descriptor,file_off_set,SEEK_CUR)) < 0)
	{
		printf("lseek error\n");
		return 0;
	}
	else
	{
		printf("lseek execute successful\nCurent file off-set is %d\n",file_off_set);
	}
	return 0;
}







atomic operation微操作

#inlcude<unistd.h>
ssize_t pread(int filedes, void * buf,size_t nbytes,off_t offset);
ssize_t pwrite(int filedes,const void*buf,size_t nbytes,off_t offset);

调用pread函数就相当于调用lseek函数之后紧接着调用read函数。这两者几乎是等价的,除了下面两点情况

1.调用pread的时候, 没有什么办法可以打断这两者(lseek和read)操作

2.文件指针没有更新

pwrite同理


          简单的说,atomic operation就是指的一种操作。这种操作可能由其他多种操作组成(multiple step)。

这个atomic operation有个特点,就是这个atomic operation包含的操作步骤(step)要么都完成,要么都不

会开始进行。这个atomic operation中不会有其他的操作“夹杂着"发生。

下面是man pwrite 的结果:

pwrite()  writes  up  to  count  bytes from the buffer

starting at buf to the file descriptor  fd  at  offset     

The file offset is not changed.


         值得注意的是这里的offset 是个定值而不是动态获得的,也就是说可能覆盖之前的文本内容(亲测,有这个情况)

如果要动态更新offset达到,每次都在 “新的文本末尾" 写入数据的话,那么则需要lseek和SEEK_END来实现更新offset

动态更新的在文本末尾写入数据用pwrite是没有必要的,lseek+write就可以了

my source code:

#include"unistd.h"
int dup(int fd);
int dup2(int fd,int fd2)
对于file descriptor的复制

              These  system calls create a copy of the file descriptor oldfd. dup() uses the lowest-numbered unused descriptor  for the new descriptor.  dup2() makes newfd be the copy of oldfd, closing newfd  first if necessary, but note the following: 
        *  If oldfd is not a valid file descriptor,  then  the call fails, and newfd is not closed.    
        *  If  oldfd is a valid file descriptor, and newfd has the same value as oldfd, then dup2() does  nothing,  and returns newfd.

#include"apue.h"
#include"unistd.h"
#include"fcntl.h"
int main()
{
	int file_descriptor = 0;
	int duped_fd = 0;
	if((file_descriptor = open("./text.t",O_RDONLY)) < 0)
	{
		printf("open error\nprocess end\n");
		return 0;
	}
	else
	{
		duped_fd = dup(file_descriptor);
		printf("The duped file descriptor is %d\n",duped_fd);
		printf("The file_descritor before dup:%d\n",file_descriptor);
	}
	return 0;
}

NAME
       fsync,  fdatasync - synchronize a file's in-core state  with storage device

#include <unistd.h>

int fsync(int fd);

#include<fcntl.h>
int fcntl(int filedes, int cmd,.../*int arg*/);

filedes文件标志符
cmd是参数宏,总共有十个,第三章只讨论前面七个,后面三个再第14章讨论
在秒速记录锁(record locking)的时候,第三个参数是一个指向结构体的指针
fcntl被用作五种不同的路径
第一,复制已经存在的文件标志符(descriptor)cmd  = F_DUP
第二,设置或者获得文件标识符cmd = F_GETFD或者 cmd = F_SETFD
第三,设置或者获得状态符(status flag) cmd = F_GETFL或则F_SETFL
status flag: 

     O_RDWR

     O_RDONLY
     O_WRONLY
     O_APPEND
     O_NONBLOCK
     O_SYNC
     O_DSYNC
     O_RSYNC
     O_FSYNC

     O_ASYNC

第四,设置或则获得异步I/O权限(asynchronous I/O ownership)cmd = F_GETOWN or cmd = F_SETOWN
第五,设置或者获得记录锁(record lock)(cmd = F_GETLK, or cmd = F_SETLK)


#include<apue.h>
#include<fcntl.h>
#include<stdio.h>
int main()
{
	int file_descriptor = 0;
	int copy_file_descriptor = 0;
	int file_flags = 0;
	int ownership = 0;
	int temp = 0;


	if((file_descriptor = open("./text.t",O_RDWR)) < 0)
	{
		printf("open error\nProcess end\n");
		return 0;
	}


	printf("file_descriptor is %d\n",file_descriptor);


	//cmd = F_DUPFD ,dupliacte the file descriptor
	if((copy_file_descriptor = fcntl(file_descriptor,F_DUPFD,0)) < 0)
	{
		printf("fcntl error\nProcess end");
		return 0;
	}
	else
	{
		printf("The return value is %d\n",copy_file_descriptor);
	}
	
	//cmd = F_GETFD, return file descriptor flass for fiedes as the vale of the fucntion
	if(temp = fcntl(file_descriptor,F_GETFD) < 0)
	{
		printf("fcntl error\nProcess end\n");
		return 0;
	}
	else
	{
		printf("file descriptor is %d\n",temp);
	}
	
	//cmd = F_GETFL,return file status flags for filedes.
	if(file_flags = fcntl(file_descriptor,F_GETFL) < 0)
	{
		printf("fcntl error\nProcess end\n");
		return 0;
	}
	else
	{
		switch(file_flags& O_ACCMODE) //& O_ACCMODE)
		{
			case O_RDWR  :
				printf("file flags is O_RDWR\n");
				break;
			case O_RDONLY:
				printf("file flags is O_RDONLY\n");
				break;
			case O_WRONLY:
				printf("file flags is O_WRONLY\n");
				break;
		}


	   	if(file_flags&O_APPEND)
		{
			printf("file flags is O_APPEND\n");
		}	
		if(file_flags&O_NONBLOCK)
		{
			printf("file flags is O_NONBLOCK\n");
		}
		
		#if defined(O_SYNC)
		if(file_flags&O_SYNC)
		{	
			printf("file flags is O_SYNC\n");
		}
		#endif
	
		#if defined(O_DSYNC)
		if(file_flags&O_DSYNC)
		{
			printf("file flags is O_DSYNC\n");
		}
		#endif
		
		#if defined(O_FSYNC)
		if(file_descriptor&O_FSYNC) 
		{
			printf("file flags is O_FSYNC\n");
		}
		#endif
		
		#if defined(O_ASYNC)
		if(file_descriptor& O_ASYNC)
		{
			printf("file flags is O_ASYNC\n");
		}		
		#endif
	
		#if defined (O_RSYNC)
		if(file_descriptor&O_RSYNC)
		{
			printf("file flags is O_RSYNC\n");
		}
		#endif
	}
	
	//cmd = F_GETOWN
	if(ownership = fcntl(file_descriptor,F_GETOWN) < 0)
	{
		printf("fcntl error!\nProcess end\n");
		return 0;
	}
	else
	{
		printf("file ownership is %d",ownership);
	}


	return 0;
}

在 /dev/fd文件夹目录下的文件,如果被打开,相当于复制该文件标志符。