栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Linux c语言中的IO -- 文件IO

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Linux c语言中的IO -- 文件IO

文章目录
  • 1. 文件IO
  • 2. 文件描述符
      • 什么是文件描述符?文件描述符何时产生?
  • 3. 文件IO常用函数
      • open()
      • close()
      • 对比fopen()与open()打开文件权限
      • read()
      • write()
      • memset / bzero 设置一片内存区域的值
      • lseek()
      • stat() 获取文件信息
      • opendir() / readdir() / closedir() 对目录操作

1. 文件IO

文件IO不涉及缓冲区,每执行一次,都会调用Linux内核的系统调用,然后操作硬件设备。

与标准IO不同,文件IO对文件操作时,使用的是文件描述符。

当程序运行时,操作系统会自动为当前程序创建三个文件描述符

  • 0 标准输入文件描述符
  • 1 标准输出文件描述符
  • 2 标准错误输出文件描述符
2. 文件描述符 什么是文件描述符?文件描述符何时产生?

在进程中,当使用open函数打开一个文件时,就会产生一个文件描述符。而这个文件描述符其实是一个数组的下标,所以文件描述符一定是一个非负整数,且会按照顺序分配。它被内核用以标识一个特定进程正在访问的文件。

文件描述符是从小到大依次递增分配,但是如果有文件描述符关闭,则新创建的文件描述符的值会先等于最小的没有使用的值,然后在依次递增创建,所以最后创建的文件描述符不一定是最大的

在进程中找到文件描述符

	struct task_struct{  //进程的结构体
		volatile long state; //进程运行状态
		unsigned int  cpu;   //cpu的序号
		int			  prio;  //进程运行的优先级
		pid_t		  pid;   //进程号
		struct task_struct 	*real_parent;
		struct list_head		children;
		struct list_head		sibling;
		
		struct files_struct		*files;
	}

首先,看进程相关的结构体的部分内容,其中有一个结构体指针为struct files_struct *,用来保存该进程打开文件的信息,下面截取结构体struct files_struct部分内容

	struct files_struct {
		struct fdtable  *fdt;
		struct file  * fd_array[32];
	};

可以看到该结构体中,又有一个struct file结构体指针数组,意味着该结构体中可以存放32个指向struct file结构体的指针,而struct file中保存着打开文件时的所有信息
我们使用open函数所获得的文件描述符就是这个数组的下标,使用open()的同时,就会创建struct file结构体,前32个文件描述符会静态分配,保证效率,再多就需要动态分配。而这里设定为32个也是考虑时间空间等因素后的经验值。
open()→fd→fd_array[fd]→struct file

struct file结构体中还有一个重要属性atomic_long_t f_count,同一struct file可以被引用多次,这个用来记录该结构体被struct file *同时指向的个数。当struct file *指向该结构体的指针减少一个,f_count就会减一,当f_count为0时,该结构体才会被释放。

	struct file {//该结构体的部分信息
		struct path		f_path;  //路径
		unsigned int f_flags;    //打开文件的方式,读,写,阻塞,非阻塞
		atomic_long_t f_count //这个是一个相对来说比较重要的参数,表示打开文件的引用计数,如果有多个文件指针指向它,就会增加f_count的值
		const struct file_operations *f_op; //操作方法结构体
		fmode_t		f_mode;      //权限
		loff_t		f_pos;       //文件的光标的位置
		void		*private_data; //私有数据
		struct inode *f_inode;//指向inode的结构指针
	}

在不同的进程中,会有不同或者相同的文件描述符指向同一个文件,在同一进程中,也会有不同的文件描述符指向同一个文件,而同一个文件只有一个struct inode结构体,所以这些文件描述符最终都是指向同一个struct inode结构体。Linux中每个文件都有唯一的indoe号

struct inode{
	umode_t	i_mode; //文件的权限
	kuid_t	i_uid;  //用户
	kgid_t	i_gid;  //组
	unsigned long i_ino;//inode号
	dev_t  i_rdev;  //设备号
	union {
		struct block_device	*i_bdev;
		struct cdev		*i_cdev;
	};
}

inode的功能

  1. 在文件系统中,inode结构体用来描述这个文件的信息。
  2. 如果这个文件是设备文件,它会唯一关联一个设备驱动。
  3. 在应用层调用open函数,会使用inode会作为参数,传递给驱动的open函数。
3. 文件IO常用函数 open()
#include 
#include 
#include 

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

功能:
	打开或者创建一个文件
	
参数:
    pathname:文件名,不添加路径默认为当前路径
    flags:标志位
        三者必须有一个:
            O_RDonLY 只读
            O_WRonLY 只写
            O_RDWR   读写
        其他标志位:如果有多个属性,需要用 位逻辑或 连接
            O_APPEND  文件存在以追加方式打开文件,此时写操作指针永远在末尾,读操作不影响
            O_CREAT   文件不存在则创建
            O_EXCL    一般与O_CREAT一起使用 O_EXCL|O_CREAT,
                      如果文件存在则报错,文件不存在则创建
            O_TRUNC   如果文件存在则清空
     mode:如果第二个参数指定了O_CREAT,就需要这个参数,否则不需要
          这个参数主要用于设置当前文件的访问权限,一般使用一个三位
          的八进制数来设置,分别设置用户主、用户组以及其他用户对当
          前文件的操作权限,分别设置读、写、执行权限,
          一般设置为0664 → 0 代表8进制,6110 4100
                   
返回值:
    成功:文件描述符
    失败:-1
close()
#include 
int close(int fd);
功能:
	关闭文件描述符
	
参数:
    fd:文件描述符
    
返回值:
    成功:0
    失败:-1
对比fopen()与open()打开文件权限
fopen()open()含义
"r"O_RDONLY以只读方式打开文件,如果文件不存在则报错
"r+"O_RDWR以读写方式打开文件,如果文件不存在则报错
"w"O_WRonLY | O_CREAT | O_TRUNC, 0664以只写方式打开文件,如果文件不存在则创建,如果文件存在则清空
"w+"O_RDWR | O_CREAT | O_TRUNC, 0664以读写方式打开文件,如果文件不存在则创建,如果文件存在则清空
"a"O_WRonLY | O_CREAT | O_APPEND, 0664以只写追加方式打开文件,如果文件不存在则创建,如果文件存在则追加
"a+"O_RDWR | O_CREAT | O_APPEND, 0664以读写追加方式打开文件,如果文件不存在则创建,如果文件存在则追加
read()
#include 
ssize_t read(int fd, void *buf, size_t count);

功能:
	从文件中读取内容
	
参数:
    fd:文件描述符
    buf:保存读取的数据的首地址,可以是数组或者结构体等
    conut:要读取的字节数
    
返回值:
    成功:实际读取的字节数
    失败:-1
    如果文件读完,返回0

注意:
read读取文件内容时,如果文件内容足够,第三个参数是n,就会读取n个字节,不会预留的位置,也不会说像fgets一样遇到行结束符就结束。一定要注意read的返回值表示实际读取的个数,往往文件最后一次读取的数据都小于第三个参数设置的值

write()
#include 
ssize_t write(int fd, const void *buf, size_t count);

功能:
	向文件写入数据

参数:
    fd:文件描述符
    buf:要写入的数据, 可以是数组或者结构体等
    conut:写入的字节数

返回值:
    成功:写入的字节数
    失败:-1
memset / bzero 设置一片内存区域的值

memset

#include 

void *memset(void *s, int c, size_t n);

功能:
	设置指定内存区域的为指定值

参数:
	s  指定区域的首地址
	c  要设置的指定值
	n  指定区域的大小,单位字节Bytes

返回值:
	返回指定区域的首地址。

bzero

#include 

void bzero(void *s, size_t n);

功能:
	将指定内存区域的值清空,设置为0

参数:
	s 指定区域的首地址
	n 指定区域的大小,单位字节Bytes

返回值:
	无 None
lseek()
#include 
#include 
off_t lseek(int fd, off_t offset, int whence);

功能:
	设置或者获取文件定位的偏移值

参数:
    fd:文件描述符
    offset:偏移值,可正可负
    whence:相对位置
        SEEK_SET 起始位置
        SEEK_CUR 当前位置
        SEEK_END 末尾位置

返回值:
    成功:当前修改之后的文件的偏移值
    失败:-1
stat() 获取文件信息
#include 
#include 
#include 
int stat(const char *pathname, struct stat *statbuf);
功能:
	获取指定文件的信息
	
参数:
    pathname:文件名
    statbuf:保存获取的信息
        struct stat {
	       dev_t	 st_dev;     文件所在设备的ID
	       ino_t	 st_ino;     inode节点号
	       mode_t	 st_mode;    模式(文件类型、访问权限)
	       nlink_t	 st_nlink;   链向此文件的连接数(硬连接)
	       uid_t	 st_uid;     用户id
	       gid_t	 st_gid;     组id
	       dev_t	 st_rdev;    设备号,针对设备文件
	       off_t	 st_size;    文件大小,字节为单位
	       blksize_t st_blksize; 系统块的大小
	       blkcnt_t  st_blocks;  文件所占块数
	       time_t	 st_atime;   最近存取时间
	       time_t	 st_mtime;   最近修改时间
	       time_t	 st_ctime;   文件创建的时间
	   };
	   
返回值:
    成功:0
    失败:-1

使用时,自己定义一个struct stat结构体,然后取地址传给stat()函数,执行成功后,文件信息就会写入自己定义的struct stat结构体体。

opendir() / readdir() / closedir() 对目录操作

标准IO和文件IO相关函数可以操作普通文件、设备文件、管道文件、软链接文件、套接字文件,但是无法操作目录文件

opendir

#include 
#include 
DIR *opendir(const char *name);

功能:
	打开指定目录

参数:
    name:目录名

返回值:
    成功:目录流指针
    失败:NULL

先用DIR *定义一个目录流指针,来接opendir()函数的返回值

readdir()

#include 
struct dirent *readdir(DIR *dirp);

功能:
	读取目录信息

参数:
    dirp:目录流指针,opendir的返回值

返回值:
    成功:保存目录中文件信息的结构体
        struct dirent {
               ino_t          d_ino;       inode节点号
               off_t          d_off;       
               unsigned short d_reclen;    
               unsigned char  d_type;      文件类型
               char           d_name[256]; 文件名
           };

    失败:NULL
    如果目录中信息读取完毕,也返回NULL

打开目录流指针成功后,定义struct dirent *目录文件信息结构体指针,来接readdir()返回值,获取目录中文件信息的结构体。
注意,每次只能获取一个文件信息,获取成功后,再次执行会获取下一个文件信息,当目录信息读取完毕会返回NULL

我的环境为Ubuntu18.04,文件类型测试结果是

d_type类型
1管道文件 p
2字符设备文件 c
4目录 d
6块设备 b
8普通文件 -
10符号链接文件 l
12套接字文件 s

closedir()

#include 
#include 
int closedir(DIR *dirp);

功能:
	关闭目录流指针

参数:
    dirp:目录流指针,opendir的返回值

返回值:
    成功:0
    失败:-1
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/296349.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号