文件I/O

岁酱吖の 提交于 2020-01-26 03:02:09

目录:

1、文件I/O

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

1.2 ---- PCB概念

1.3 ---- open/close

1.4 ---- read/write

1.5 ---- 阻塞和非阻塞

1.6 ---- lseek

1.7 ---- fcntl

1.8 ----ioctl





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

#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.

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

再看一下如何关闭文件

#include <unistd.h>

int close(int fd);

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

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

1.4 read/write

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

#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来人为操控文件指针偏移位置。

上代码:

#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函数来实现的。

#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时表示从起点开始直至最大可能位置为止。

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

#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 error\n");
		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 fd\n");
		}
		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来进行读写的,如串口线啊之类的。

#include <sys/ioctl.h>

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

//d是某个设备的文件描述符,request 是ioctl的命令。可变参数取决于request。
//成功返回一个值,也是取决于request,失败返回-1并设置而errno。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!