标准IO与文件IO

标准IO

标准IO:标准IO是指 ANSI C中定义的用于I/O操作的一系列函数。只要系统中安装了C库,标准IO就可以调用。也就是说标准IO是建立在系统调用基础之上的,对用户提供统一的编程接口,本质上就是在不同系统上调用各自系统的系统调用,而对用户的接口不变,这样使其有了更好的移植性。除此之外,使用标准IO可以减少系统调用的次数,提高系统效率。
标准IO函数在执行时也会用到系统调用,在执行系统调用时,Linux系统必须从用户态切换到内核态,处理相应的请求,然后再返回到用户态。如果频繁的执行系统调用会增加系统的开销,为了避免这种情况,标准IO使用时在用户空间创建缓冲区,在合适的时机在通过系统调用访问实际的文件,从而减少了使用系统调用的次数。

标准IO操作对象为-普通文件
文件:保存在磁盘上的一组相关数据的有序集合。有序集合的名字叫文件名
Linux系统上的文件分为七种:

  • 普通文件 -
  • 管道文件 p
  • 链接文件 l
  • 目录文件 d
  • 套接字文件 s
  • 块设备文件 b
  • 字符设备文件 c
    :当使用标准IO打开一个文件时,就会创建一个FILE结构体描述该文件,我们把这个FILE结构体形象的称为流,标准IO函数都是基于流进行各种操作。
    文件指针:指向一个打开文件的指针(硬盘中的文件被拷贝到内存中之后,会以FILE结构体的形态存在,要操作该文件必须使用文件指针)
    标准IO中流的缓冲类型
  • 行缓冲:\n (1024 byte)
  • 全缓冲:缓存区填满内容才会溢出 (4096 byte)
  • 不缓存:stderr

标准IO函数

函数名函数功能函数参数函数返回值
fopen打开文件const char* path(要打开的文件路径及文件名), const char *mode(打开方式)失败返回NULL,成功返回FILE*
fclose关闭文件FILE* stream(已打开的流指针)失败返回EOF,成功返回0
fread读文件void* ptr(保存读到的数据),size_t size(读取的元素大小),size_t nmemb(读取元素的个数),FILE* stream(要读取的文件流)失败返回EOF,成功返回实际读取的个数
fwrite写文件void* ptr(保存要写入的数据),size_t size(写入的元素大小),size_t nmemb(写入元素的个数),FILE* stream(要写入的文件流)失败返回EOF,成功返回实际写入的个数
feof检测是否到达文件结尾FILE *stream(要检测的流指针)到达文件结尾返回非零,否则返回0
fseek设置光标位置FILE *stream(要定位的文件流),long offset(相对于基准的偏移量),int whence(基准值)失败返回EOF,成功返回0
ftell获取文件当前读写位FILE *stream(要定位的文件流)失败返回EOF,成功返回当前读写位置

文件IO

文件IO遵循POSIX相关标准,任何兼容POSIX标准的操作系统上都支持文件IO。
特点

  1. 不带缓存
  2. 通过文件描述符来访问文件
    在Linux系统中一切皆文件,Linux操作系统是基于文件概念的。Linux的文件系统由两层结构构建:第一层是虚拟文件系统(VFS),第二层是各种不同的具体的文件系统。VFS就是把各种具体的文件系统的公共部分抽取出来,形成一个抽象层,是系统内核的一部分。它位于用户程序和集体的文件系统之间,它对用户程序提供了标准的文件系统调用接口,可以接受用户层的系统调用。此外还支持多种具体文件系统之间的相互访问,接受内核其他子系统的操作请求。
    通过以下命令可以查看系统中支持的文件系统:cat /proc/filesystems

文件描述符:文件描述符是一个非负整数,它是一个索引值,并指向在内核中每个进程打开文件的记录表。
通常一个进程启动时就会打开三个流:

  1. stdout: 标准输出流(默认情况下stdout是行缓冲,它的输出会放在一个buffer里面,只用到换行的时候才会输出到屏幕)
  2. stdin: 标准输入流
  3. stderr: 标准错误(无缓冲)
    这三个流分别对应文件描述符0、1、2(对应的宏依次为:STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO)

文件IO相关函数

函数名函数功能函数参数函数返回值
open/creat打开或创建一个文件const char* pathname(被打开的文件名),int flags(文件打开的方式),int perms(新建文件的存取权限)调用成功返回文件描述符,失败返回-1,并设置errno
close关闭文件int fildes(文件描述符)调用成功返回0,出错返回-1,并设置errno
read读文件int fd(文件描述符),void* buf(读出数据的缓存区),size_t count(读出的字节数)出错返回-1,成功返回读取的字节数,返回0表示到达文件末尾
write写文件int fd(文件描述符),void* buf(读出数据的缓存区),size_t count(读出的字节数)出错返回-1,成功返回实际写入字节数
lseek设置光标位置int fd(文件描述符),off_t offset(相对于基准的偏移量),int whence(基准值)失败返回-1,成功返回当前读写位置

文件IO与标准IO的区别

  1. 文件IO又被称为低级磁盘IO,遵循POSIX相关标准,任何兼容POSIX标准的操作系统上都支持文件IO。标准IO被称为高级磁盘IO,遵循ANSI C标准,只要开发环境中有标准C库,就可以使用标准IO。
  2. 文件IO读写文件时,每次操作都会执行相关的系统调用,这样可以直接读取实际文件,但是频繁的系统调用会增加系统的开销。标准IO可以看做是在文件IO的基础上封装了缓冲机制,从而减少了系统调用的次数。
  3. 文件IO中用文件描述符表示一个打开的文件,可以访问不同类型的文件。而标准IO使用流表示一个打开的文件,只能访问普通文件。

文件锁

文件IO中的五个基本函数实现了文件的打开、读写操作。但是在文件被共享的情况下(及多个程序共同操作一个文件的情况)这个文件应该怎么进行操作而不会使文件混乱,Linux中通常采用的方法是给文件上锁。
文件锁包括建议性锁和强制性锁
建议性锁 :要求每一个相关的程序在访问文件之前检查是否有锁存在,一般不建议使用建议性锁,因为无法保证每个程序都自动检查是否有锁。
强制性锁:是由内核执行的锁,当文件被锁上时,内核将阻止其它任何程序对该文件进行读写操作。
在Linux中,实现文件上锁的函数有 lockf() 和 fcntl()。
lockf():用于对文件施加建议性锁。
fcntl():不仅可以施加建议性锁还可以施加强制性锁,同时还可以对文件的某一记录上锁,也就是文件锁。具体的功能可以查看fcntl手册。
fcntl()函数

//所需头文件
#include <sys/type,h>
#include <unistd.h>
#include <fcntl.h>
/*
函数原型:int fcntl(int fd, int cmd, ...);
fd: 文件描述符
cmd: F_GETLK: 检测文件锁状态
	F_SETLK:设置lock描述符的文件锁
	F_GETLKW:F_GETLK的阻塞版本
...: 如果cmd和锁的操作有关,第三个参数类型为 struct *flock
struc flock{
	short l_type;F_WRLCK(读取锁、共享锁)、F_RDLCK(写入锁、排斥锁)、F_UNLCK(解锁)
	off_t l_start; 加锁区域在文件中的相对位移量(字节)
	short l_whence; 相对位移量的起点,同lseek的whence
	off_t l_len; 加锁区域长度
	pid_t l_pid; 具有阻塞当前进程的锁,其持有进程的进程号放在这里,仅由F_GETLK返回。
	...
}
在无法获取锁时,会进入睡眠状态,如果可以获取锁或捕捉到信号则会返回。
*/
int FileLock(int fd, int type)
{
	//给flock结构体赋值,要加锁整个文件,可以将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0。
	struct flock lock;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_type = type;
	lock.l_pid = -1;
	
	//判断文件是否可以上锁
	fcntl(fd, F_GETLK, &lock);
	if (lock.l_type != F_UNLCK)
	{
		//判断不能上锁的原因
		if (lock.l_type == F_RDLCK)
		{
			printf("read lock already set by %d\n", lock.l_pid);
		}
		else if (lock.l_type == F_WRLCK)
		{
			printf("write lock already set by %d\n", lock.l_pid);
		}
	}
	lock.l_type = type;
	
	//根据不同的type值进行阻塞式(F_SETLKW)上锁或解锁
	if ( fcntl(fd, F_SETLKW, &lock)<0 )
	{
		printf("lock failed:%d", lock.l_type);
	}
	switch(lock.l_type)
	{
		case F_RDLCK:
		printf("read lock set by %d\n", gitpid());
		break;
		case F_WDLCK:
		printf("write lock set by %d\n", gitpid());
		break;
		case F_UNLCK:
		printf("release lock set by %d\n", gitpid());
		break;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值