IO学习四(系统调用IO(文件IO))

系统调用IO(文件IO),文件描述符在文件IO中贯彻始终(fd)
每个文件都有一个唯一的inode。
打开一个文件也会产生一个结构体
文件描述符的概念:
在这里插入图片描述

实质是一个整形数,是一个数组下标。
每次执行一个打开的动作,都会关联一个结构体,这个结构体包含了这个文件的所有属性,将结构体的指针保存在数组当中,所有最后给用户的是一个整形数(数组的下标),操作文件就使用这个整型数,通过这个整数,可以找到指针,这个指针可以知道结构体,通过这个结构体我们操作的就是这个文件。

比如当我们fopen打开一个文件的时候,会产生一个关联FILE的结构体,也会产生一个文件IO的结构体所以说,在FILE关联的结构体里面肯定会有fd这个整形数,因为fopen一定依赖于open产生。所以我们在销毁回事释放FLIE*关联的结构体的时候(调用fclose),会把他所依赖的open所产生的结构体
也会调用close,也把结构体销毁。

存放指针的数组有多大?
目前是1024

ulimit-a之前我们查看了文件最多可以打开的个数,如果通过ulimit改变,那么其实改变的就是这个数组的大小,这个数组决定了可以打开多少个文件。

数组的下标0、1、2所关联的分别是标准输入,标准输出,标准出错,所以会提前打开三个设备,这三个设备会分别存在0、1、2的下标位置。所以标准的输入、输出、出错可以正常使用。

stream对应的标准输入、输出、出错:就是stdin,stdou,stderr
fd:就是0、1、2

文件描述符优先使用当前可用范围内最小的那个。
假设数组中0~6号都被占用了,但是3关闭了,那么再打开文件就不会占用7,而是占用3

数组是存在在一个进程空间里面的,每一个进程都会有一个这样的数组。如果两个进程,同时打开同一个文件,不会拿到同一个结构体,是一式两份的那种。

同一个文件在一个进程中打开两份,每次打开都会产生一个结构体,这两个结构体关联的是同一个文件。现在就有两个文件描述符,那么如果对文件进行操作,如果没有竞争的情况下是没有问题的。打开多少次就会产生多少个结构体关联到文件上,close一次就会销毁掉一个结构体。
在这里插入图片描述

假设现在下标4,关联一个结构体,打开了一个文件。若将下标4里的地址复制一份,放到数组中,假设放到下标6处
那么6关联的是和4同样的结构体。如果这个时候close(4),那么6还可以使用这个文件吗?
在这里插入图片描述

在实际中,如果关掉4,这个结构体还是存在的,要不然6就会变成野指针,所以可以推断出,fd中还会有计数器,记录文件打开的次数,反映出当前的结构被几个指针引用,在关闭4的时候就会给计数器减减,减完之后发现不是0,就继续保存这个结构体,这块空间不执行free。只有4关闭后6也关闭了,那么这时候count为0了,这个时候就会free掉这块空间。

文件IO的操作:open,close,read,write,lseek(系统调用IO时支持标准IO的,标准IO依赖于系统调用IO,fopen依赖open等等)
相关函数:
open();
-----------open and possibly create a file or device

  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
     int open(const char *pathname, int flags(权限信息));//第二个参数:是一个位图
 	 int open(const char *pathname, int flags, mode_t mode);
	 int creat(const char *pathname, mode_t mode);

返回值:
open(), openat(), and creat() return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).

关于第二个参数flags:
The argument flags must include one of the following access modes:O_RDONLY, O_WRONLY, or O_RDWR. These request opening the file read-only, write-only, or read/write, respectively.

说明:

   In addition, zero or more file creation flags and file status flags(文件的创建选项和文件的状态选项) can
   be bitwise-or'd in flags.   The  file  creation  flags  are  O_CLOEXEC,
   O_CREAT,  O_DIRECTORY,  O_EXCL,  O_NOCTTY,  O_NOFOLLOW,  O_TMPFILE, and
   O_TRUNC.  The file status flags are all of the remaining  flags  listed
   below.   The  distinction between these two groups of flags is that the
   file status flags can be retrieved and (in some  cases)  modified;  see
   fcntl(2) for details.

O_CREAT:
If the file does not exist, it will be created.
有则清空,无则创建

O_TRUNC:截断或者截短

O_EXCL:
Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname already exists,then open() will fail.必须打开一个新的文件,如果打卡一个不是新的文件,当前就会报错(通常会和创建连用)

O_APPEND:
The file is opened in append mode
追加

O_ASYNC:
Enable signal-driven I/O: generate a signal (SIGIO by default,but this can be changed via fcntl(2)) when input or output becomes possible on this file descriptor
信号驱动IO

O_DIRECT:
Try to minimize cache effects of the I/O to and from this file.
最小化cache的作用

cache:可以理解为读的缓冲区,读内容读到cache里
buf:写的缓冲区

O_DIRECTORY:
If pathname is not a directory, cause the open to fail.
强调打开的文件一定是一个目录文件,如果不是,就会报错

O_LARGEFILE:(如果打开的文件比较大,最好在属性中加上这个
Allow files whose sizes cannot be represented in an off_t (but can be represented in an off64_t) to be opened. The _LARGEFILE64_SOURCE macro must be defined (before including any header files) in order to obtain this definition. Setting the _FILE_OFFSET_BITS feature test macro to 64 (rather than using O_LARGEFILE) is the preferred method of accessing large files on 32-bit systems (see feature _test_macros(7)).

O_NOATIME:
Do not update the file last access time (st_atime in the inode)when the file is read(2).
提高性能时间,不需要更新文件的访问时间。ATIME指的是文件最后读的时间,比如说你偷偷读别人的文件,但是又不希望别人看到读的时间是自己的偷偷读的那个时间。

O_NOFOLLOW:
If pathname is a symbolic link, then the open fails
如果给的文件名是一个符号链接文件的话,就会打开失败

O_NONBLOCK or O_NDELAY:非阻塞方式打开

O_SYNC:(同步)

r->O_RDONLY
r±>O_RDWR
w->O_WRONLY|O_CREATE|O_TRUNC(如果这个文件存在就截断,不存在就创建,而且是只写的形式)
w±>O_RDWR|O_TRUNC|O_CREATE

如果flags里有O_CREATE,那一定要用三参数的形式。如果没有,就用两参数的形式。
open是用变参函数实现的,不是通过重载实现的
辨别函数是否是用重载还是变参实现的方法:比说如果open里面传多个参数,如果编译,如果报语法错误,就表示是定参不是变参实现的。不报错就说明是变参实现的。

close();
-----------------close a file descriptor

   #include <unistd.h>
   int close(int fd);

read();
----------------read from a file descriptor(读一个文件描述符)

#include <unistd.h>
   ssize_t read(int fd, void *buf, size_t count);

返回值:
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.
On error, -1 is returned, and errno is set appropriately.

write();
------------------- write to a file descriptor

   #include <unistd.h>
   ssize_t write(int fd, const void *buf, size_t count);

返回值:

On success, the number of bytes written is returned (zero indicates nothing was written).
On error, -1 is returned, and errno is set appropriately.

lseek();
------------------ reposition read/write file offset
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

  SEEK_SET
          The offset is set to offset bytes.

   SEEK_CUR
          The offset is set to its current location plus offset bytes.

   SEEK_END
          The offset is set to the size of the file plus offset bytes.

返回值:
Upon successful completion, lseek() returns the resulting offset location as measured in bytes from the beginning of the file.

小例子:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFSIZE 1024


int main(int argc,char *argv[])
{	int len;
	int ret;

	int sfd,dfd;
	char buf[BUFSIZE];
	if(argc < 3)
	{
		fprintf(stderr,"Usage....\n");
		exit(1);
	}

	sfd=open(argv[1],O_RDONLY);
	if(sfd < 0)
	{
		perror("open():");
		exit(1);
	}
	dfd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(dfd<0)
	{	
		close(sfd);
                perror("open():");
                exit(1);
        }



	while(1)
	{
		len=read(sfd,buf,BUFSIZE);
		if(len<0)
		{
			perror("read():");
			break;
		}
		if(len==0)
		{
			break;
		}
		ret=write(dfd,buf,len);
		if(ret<0)
		{
			perror("write()");
			break;
		}
		
	}

	close(dfd);
	close(sfd);
	exit(0);
}

上面代码有个问题就是,如果读到了10个字节,但是却只写进去3个字节,那就会丢掉7个字节
改良版本:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUFSIZE 1024


int main(int argc,char *argv[])
{	int len;
	int ret,pos;

	int sfd,dfd;
	char buf[BUFSIZE];
	if(argc < 3)
	{
		fprintf(stderr,"Usage....\n");
		exit(1);
	}

	sfd=open(argv[1],O_RDONLY);
	if(sfd < 0)
	{
		perror("open():");
		exit(1);
	}
	dfd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0600);
	if(dfd<0)
	{	
		close(sfd);
                perror("open():");
                exit(1);
        }



	while(1)
	{
		len=read(sfd,buf,BUFSIZE);
		if(len<0)
		{
			perror("read():");
			break;
		}
		if(len==0)
		{
			break;
		}
		pos=0;
		while(len>0)
		{
		ret=write(dfd,buf+pos,len);
		if(ret<0)
		{
			perror("write()");
			exit(1);
		}
		pos+=ret;
		len-=ret;
		}
		
	}

	close(dfd);
	close(sfd);
	exit(0);
}

文件IO和标准IO的区别:
举个例子:
传达室老大爷跑邮局:
1、一个人来了,给老大爷一封信,老大爷跑到邮局把信寄走,然后回去又来一个人,要寄一封信,大爷又去邮局一趟。

2、老大爷一天攒着信然后去邮局。
但是如果这个时候有5封信,然后第六个人说,信件很急,那么老大爷拿着这六封信到邮局,如果有急件,就相当于fflush

标准IO具有缓冲这样的机制,而系统调用IO是每调用一次,就会从user态切换到kernel态去执行一次,相当于实时性非常的高。标准IO就是放到文件缓冲区当中.

区别:响应速度&吞吐量
如何使一个程序变快?
可以从两个方面来作答。

标准IO和文件IO不可以混用:
FILE *fp;
fputc(fp)->pos++(文件指针使向后移动的)
fputc(fp)->pos++(又向后移动一下)
就是pos加2
但是文件描述符所关联的结构体中的pos没有加2
标准IO有一个缓冲区,实际上pos+2,往文件当中放入两个字节但是并没有写到文件的实处或者说没有直接写到磁盘上,比如说在使用word,如果关闭就会出现是否保存的提示,如果点击保存实际上就是刷新文件,更新了文件。如果没有保存其实就是刚刚做的操作不会保存到文件中。标准IO不是直接写到文件当中,而是写到缓冲区。

如果用标准IO,进行文件的读取,原本想的就是从文件当中读一个字符,pos向后加1,再用一个fgetc又会加1,不是的,是会放到缓冲区当中。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main()
{

        putchar('a');
        write(1,"b",1);


        putchar('a');
        write(1,"b",1);

        putchar('a');
        write(1,"b",1);


        exit(0);
}

运行结果:bbbaaa

strace ./ab
strace 可以看到一个可执行文件的系统调用使如何发生的
可执行文件名字为ab
在这里插入图片描述

转换函数:
fileno();
int fileno(FILE *stream);//把标准IO的操作转换成系统调用IO
fdopen();
FILE *fdopen(int fd, const char *mode);//把系统调用IO转换成标准IO

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值