文件I/O

2020-08-26 10:29:24 浏览数 (1)

1.1 C标准函数与系统函数的区别

有一定编程基础的小伙伴应该都接触过文件编程吧,file. 在C语言里面是包一个<file.h>的头

每一个文件都有一个缓冲区,C和系统函数的区别也不想说太多,系统函数可以实现不同进程共享一个缓冲区,而C函数不行。

1.2 PCB的概念

PCB(process control block),进程控制块。 Linux的进程控制块为一个由结构task_struct所定义的数据结构,task_struct存/include/ linux/sched.h 中,其中包括管理进程所需的各种信息。 在创建一个新进程时,系统在内存中申请一个空的task_struct区,即空闲PCB块,并填入所需信息。

1.3 open/close

首先了解一下文件描述符,和文件描述符表。 注意:以下内容记住基于进程,所以文件描述符和符表都存在PCB里面了。 文件描述符表:纪录文件描述符使用情况的表。 文件标书符:在一个进程创建时吗,默认自动打开三个文件,即生成了三个文件描述符: STDINFILENO —>0 STDOUT_FILENO —>1 STDERR_FILENO —>2 标准输入输出流和标准错误流

之后再开辟新文件就会生成新的文件描述符,默认使用空闲的最小的文件描述符。 这里就可以将输入输出重定向:关闭输入输出流,而后重新打开文件,就可以将输入输出重定向到新开文件中。

好,我们来看怎么打开文件z

代码语言:javascript复制
#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);

//一般用fd接收返回值

//返回值 :成功返回重新分配的文件描述符,出错则返回-1并设置errno

参数释义: pathname:要打开或创建的文件名,既可以是绝对目录,也可以是相对目录。 flags:打开模式: O_RDONLY 以只读形式打开 O_WRONLY 以只写形式打开 O_RDWR 可读可写形式打开 O_APPEND 表示追加,从文件末尾添加内容,而不覆盖原有内容 O_CREAT 若文件不存在则创建,仅此处会用到第三个参数,赋予文件权限 O_EXCL 和 O_CREAT 共用,如果文件已存在则出错返回 O_TRUNC 这个咱也没用过,如果文件已存在,并且有可写模式打开,则将其长度截断为0字节 O_NONBLOCK 对于设备文件,做非阻塞I/O.

第三个参数指定权限,以八进制数表示。

再看一下如何关闭文件

代码语言:javascript复制
#include <unistd.h>

int close(int fd);

//返回值:成功返回0,失败返回-1并设置errno

参数释义: fd为要关闭的文件描述符。 在进程结束时,系统自动调用close关闭所有文件。

1.4 read/write

read函数从打开的文件中读取数据 write函数向打开的文件中写入数据

代码语言:javascript复制
#include <unistd.h>

ssize_t read(int fd,void *buf,size_t count);
ssize_t write(int fd,void *buf,size_t count);

//返回值:成功返回读取/写入的字节数,失败返回-1并设置errno。

参数释义: fd:文件描述符 buf:缓存,一般用char数组 count:要读取/写入的字节数

ssize_t:表示有符号的size_t。

有些情况下,count可能不会那么刚好。 从终端设读,通常以行为单位,读到换行符就返回了 从网络读后面socket部分会再说

1.5 阻塞和非阻塞

读常规文件是不会阻塞的 从终端设备或网络读取就不一定了 如果终端输入的数据没有换行符,调用read的终端设备就会阻塞 如果网络上没有收到数据包,调用read从网络读就会阻塞 至于阻塞多久那就不确定了 如果一直没有数据到就一直阻塞在那里

解决阻塞的一个办法叫轮询

1.6 lseek

每个打开的文件都会纪录当前读写的位置,不过那个O_APPEND比较特殊点。 也可以通过lseek来人为操控文件指针偏移位置。

上代码:

代码语言:javascript复制
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd,off_t offset,int whence);

//这里允许偏移超过文件末尾,中间空出来的位置都是0.

参数释义:fd文件描述符 offset:偏移量 whence:偏移的起始位置

whence: SEEK_SET:从文件开始处计算 SEEK_CUR:从当前文件偏移处计算 SEEK_END:从文件结束处计算

若lseek成功执行,返回一个新的偏移量。

注意:偏移之后写入一个空值,不然会偏移不成功。

1.7 fcntl

可以用fcntl对一个已打开的文件进行修改属性,而不必重新open一个文件 不过这个我是没试过了

不过文件锁需要用到这个

Linux中文件记录锁可以对文件某一区域进行文件记录锁的控制。它是通过fcntl函数来实现的。

代码语言:javascript复制
#include <unistd.h>
#include <fcntl.h>

int fcntl (int fd,int cmd,struct flck *lock);
//功能说明:管理文件记录锁的操作
//返回值:调用成功返回0,失败返回-1

参数释义: fd:文件描述符; cmd:功能符号; (F_SETLK用来设置或释放锁; F_GETLK用来获得锁信息;) lock:存储锁信息的结构体指针;

struct flock { short l_type; /* 锁的类型 / short l_whence; / 偏移量的起始位置: / off_t l_start; / 从l_whence的偏移量 / off_t l_len; / 从l_start开始的字节数 / pid_t l_pid; / 锁所属进程ID(一般不用) */ } l_type有F_RDLCK读锁、F_WRLCK写锁及F_UNLCK空锁。 l_whence有SEEK_SET、SEEK_CUR和SEEK_END。 l_len为0时表示从起点开始直至最大可能位置为止。

因为这个我也不太会,但是我想会,那就上代码

代码语言:javascript复制
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(){
	int fd;
	struct flock lock; //声明锁变量
	
	if((fd = open("example",O_CREAT | O_TRUNC | O_RDWR, S_IRWXU)) == -1)
	{
		printf("open file errorn");
		return -1;
	}
	
	memset(&lock,0,sizeof(struct flock));//清空锁变量

	//设置锁变量
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	
	//拿到锁则对文件执行后续操作
	if(fcntl(fd,F_GETLK,&lock) == 0)
	{
		//判断该锁是否空锁
		if(lock.l_type != F_UNLCK)
		{
			printf("lock can not by set in fdn");
		}
		else
		{
			lock.l_type = F_WRLCK;//要上锁时才给出锁的类型
			if(fcntl(fd,F_SETLK,&lock) == 0)
				printf("set write lock success!n");
			else
				printf("set write lock fail!n");
			getchar();
			lock.l_type = F_UNLCK;//在释放锁之前将锁置空
			fcntl(fd,F_SETLK,&lock);//释放锁
		}
	}
	close(fd);//关闭文件描述符
	return 0;
}
1.8 ioctl

ioctl用于向设备发送控制和配置命令,有些命令也需要读写一些数据,但是这些数据是不能用write/read来进行读写的,如串口线啊之类的。

代码语言:javascript复制
#include <sys/ioctl.h>

int ioctl(int d,int request,······);

//d是某个设备的文件描述符,request 是ioctl的命令。可变参数取决于request。
//成功返回一个值,也是取决于request,失败返回-1并设置而errno。

0 人点赞