天天看点

嵌入式Linux _ 文件I/O

一、如何理解文件IO(了解)

在Linux下有两种文件操作方法    标准IO   文件IO 

标准IO与文件IO的区别:

标准IO : 遵循C标准 、带缓冲 、 流(FILE结构体)

文件IO:  POSIX规范 、无缓冲 、 文件描述符(fcl)

什么是文件IO:

— posix(可移植操作系统接口) 定义的一组函数。

— 不提供缓冲机制,每次读写操作都引起系统调用。

— 核心概念是 文件描述符。

— 访问各种类型文件。(除了普通文件,终端文件,Linux下7中文件类型都可以访问)

— Linux下,标准IO基于 文件IO。(基于文件IO 封装了一个 缓冲机制)

二、文件IO描述符的含义(了解)

  • 每个打开的文件都对应一个文件描述符。
  • 文件描述符是一个非负整数。Linux为程序中每打开的文件分配一个文件描述符。
  • 文件描述符从0开始分配,依次递增,且每次分配都是先找最小非负整数分配。

   例如:打开了0,1,2三个文件,关闭1后再打开一个文件,分配的文件描述符为1.

  •  不同函数中 相同文件描述符 代表的是不同的文件,互不影响。
  • 文件IO操作通过文件描述符来完成。
  • 0,1,2的含义?

每打开一个文件,系统自动创建三个文件(标准输入stdin(0),标准输出stdout(1),标准错误stderr(2))使用这三个文件描述符表示。

三、文件的打开和关闭(了解)

1、文件IO  ------  open

  •  Open  :函数用来创建或打开一个文件。

#include <fcntl.h>

Int open(const char *path,into flag,……..);

Path: 要打开文件的路径。

Flag: 要打开的模式。

  • 成功时返回文件描述符:出错时返回EOF
  • 打开文件时使用两个参数。
  • 创建文件时第三个参数指定新文件的权限。
  • 只能打开设备文件,不能用来创建设备文件,设备文件需要使用专门的函数创建。
嵌入式Linux _ 文件I/O

示例:

           以只写的形式打开文件test.txt ,如果文件不存在则创建,如果文件存在则清空。

           以只写的形式打开文件test.txt ,如果文件不存在则创建,如果文件存在则报错。

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, const char *argv[])
{
	int fp;


	if(argc < 2)
	{
		printf("Usage : %s <src_file> <dst_file> \n",argv[0]);
		return -1;
	}

	if((fp = open(argv[1],O_WRONLY|O_CREAT|O_EXCL,0666))<0)
	{
		printf("%s:%s \n",argv[1],strerror(errno));
	//error("argv[1])");
		return -1;
	}

	close(fp);
	return 0;
}
           

2、文件IO  ------  close

#include <unistd>

Int close(int fd)

  • 成功返回0 ;失败返回EOF
  • 程序结束时会自动关闭打开的文件。
  • 文件关闭后,其对应的文件描述符将与该文件无关。
  • 系统打开的文件个数有限(Linux 1024个),所以打开的文件使用完后应该及时关闭。

四、文件的读取、写入及定位(了解)

1、读取文件(熟练)

#include <unistd>

ssize_t  read(int fd , int *buf ,int count);

  • Fd : 文件描述符  buf : 存放读出数据的缓冲区  count : 需要读出的字节数
  • 成功时 返回实际读出的字节大小, 失败返回 EOF ; 文件的数据少于指定读出的大小,有多少读出多少并返回实际读出大小;读到文件末尾 返回0;
  • Buf 是接收数据的缓冲区。
  • Conunt的大小不应该 大于 buff的大小。

实例: 从指定的文件(文本文件)中,读取文件并统计大小。

2、写入文件(熟练)

#include <unistd>

ssize_t  write(int fd , int *buf ,int count);

  • Fd : 文件描述符  buf : 存放写入数据的缓冲区  count : 需要写入的字节数
  • 成功时 返回实际写入的字节大小, 失败返回 EOF ;
  • Buf 是发送数据的缓冲区。
  • Conunt的大小不应该 大于 buff的大小。
嵌入式Linux _ 文件I/O

3、定位文件(熟练)

Lseek() 函数用来定位文件

#include <unistd.h>

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

  • 成功时返回当前的文件读写的位置;出错时返回EOF。
  • 参数offset和参数wehence 同fseek 完全一样。

—   思考与练习

  •  利用文件IO实现文件的复制。

      文件名通过命令行参数的拟定

     打开文件的方式?

     如何判断是否读到了源文件的末尾?

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>


int main(int argc, const char *argv[])
{
	int fp;
	int fpd;
	int buff[20];
	int ch;
 
	if(argc<3) //判断一下输入的参数 是否小于三个
	{
		printf("Usage : %s <src_file><dst_file>\n",argv[0]);
		return -1;
	}
	if((fp = open(argv[1],O_RDONLY)) == -1) // 以只读 方式打开必须存在的文件
	{
		perror("fopen src file ");
		return -1 ;
	}
	if((fpd = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666)) == -1) //以只写 清空方式打开文件,不存在则 创建
	{
		perror("fopen dst file ");
		return -1 ;
	}
	while((ch = read(fp,buff,20))>0) // 从文件中读出数据放入 buffhu缓冲中
	{
		fwrite(fpd,buff,20);  //  将buff缓冲中的 数据 写入 文件fpd中
	}
	fclose(fp);     //关闭文件 fp
	fclose(fpd);    //关闭文件 fp
	return 0;
}
           

五、文件目录的操作 

1、读取目录

opendir  函数用打开一个目录文件。

#include <dirent.h>

DIR *opendir(const  char *  name);

  • DIR是用来描述一个打开的目录文件的结构体类型。
  • 成功是返回目录的流指针,出错时返回NULL.

readdir 函数用来读取目录流中的内容。

struct dirent * readdir(DIR * dirp);

  • struct dirent 是用来描述目录流中一个目录项的结构体类型。
  • 包含成员 char  d_name[256]  等   (具体参考帮助文档)
  • 成功时返回目录流dirp中下一个目录项。
  • 出错或到末尾时 返回NULL。
On Linux, the dirent structure is defined as follows:

           struct dirent {

               ino_t          d_ino;       /* inode number */

               off_t          d_off;       /* not an offset; see NOTES */

               unsigned short d_reclen;     /* length of this record */

               unsigned char  d_type;      /* type of file; not supported

                                              by all filesystem types */

               char           d_name[256];  /* filename */

           };
           

示例:打印指定的目录下所有文件名称。

Int main(argc,char *argv[])
{
	DIR *dirp;
	struct dirent *dp;
	if(argc <2 )
    {
		printf(“Usage:%s <directory> \n”,argv[0]);
		return -1;
    }
    if((dirp = opendir(argv[1] ))==NULL)
    {
	    perror(“opendir”);
    	return -1;
    }
    while(dp = readdir(dirp) !=NULL)
    {
	    printf(“%s \n”,dp->d_name);
    }
    closedir(dirp);
}
           

 注意: 如果该目录中 还有 子目录文件,该函数是读取不出来的,该函数只能读取当前目录下的所以有内容。

2、修改文件访问权限

Chmod / fchmod  函数用来修改文件的访问权限

#include <sys/stat.h>

Int chmod (const char *path,mode_t mode);

Int fchmod(int fp, mode_t mode );

  • 成功时返回0,出错时返回EOF.
  • Root 和文件的拥有者能修改文件的访问权限(其他人不能修改)

示例:

       Chmod (“test.txt”,0666); //以八进制的形式

3、获取文件属性

stat / lstat/fstat  函数 用来获取文件属性。

         int stat (const  char*  path, struct stat *buf);

         int lstat(const  char*  path, struct stat *buf);

         int fstat(        int    pd, struct stat *buf);

  • 成功时返回0;失败时返回EOF。
  • 如果path是符号链接符,stat函数获取的是 目标文件的属性,而lstat 获取的是链接文件的属性。 (一般使用 lstat函数)
  • struct stat 结构体定义如下:
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; // 设备ID(如果是特殊文件) 
         off_t st_size; // 总体尺寸,以字节为单位 
         blksize_t st_blksize; // 文件系统 I/O 块大小
         blkcnt_t st_blocks; // 已分配 512B 块个数
         time_t st_atime; // 上次访问时间 
         time_t st_mtime; // 上次更新时间 
         time_t st_ctime; // 上次状态更改时间 
};
           

Struct stat 是存放文件属性的结构体类型:

  • Mode_t   st_mode ;             类型和访问权限
  • Uid_t      st_uid;                  所有者id
  • Uid_t      st_gid;                  用户组id
  • Off_t       st_size;                  文件大小
  • Time_t    st_mtime;              最好修改时间

通过系统提供的宏来判断文件类型:

               St_mode & 0170000

  • S_ISREG(st_mode)                     0100000
  • S_ISDIR(st_mode)                      0040000
  • S_ISCHR(st_mode)                     0020000
  • S_ISBLK(st_mode)                     0060000
  • S_ISFIFO(st_mode)                    0010000
  • S_ISLNK(st_mode)                     0120000
  • S_ISSOCK(st_mode)                  0140000
S_IFMT	0170000	文件类型位域掩码
S_IFSOCK	0140000	套接口
S_IFLNK	0120000	符号链接
S_IFREG	0100000	普通文件
S_IFBLK	0060000	块设备
S_IFDIR	0040000	目录
S_IFCHR	0020000	字符设备
S_IFIFO	0010000	FIFO
S_ISUID	0004000	设置 UID 位
S_ISGID	0002000	设置 组ID 位 (看下面)
S_ISVTX	0001000	粘滞位(看下面)
S_IRWXU	00700	文件所有者权限掩码
S_IRUSR	00400	所有者有读权限
S_IWUSR	00200	所有者有写权限
S_IXUSR	00100	所有者有执行权限
S_IRWXG	00070	组权限掩码
S_IRGRP	00040	组有读权限
S_IWGRP	00020	组有写权限
S_IXGRP	00010	组有执行权限
S_IRWXO	00007	其他用户权限掩码(不在组内)
S_IROTH	00004	其他有读权限
S_IWOTH	00002	其他有写权限
S_IXOTH	00001	其他有执行权限
           

通过系统提供的宏来获取文件访问权限:

  • S_IRUSR         00400             bit:8
  • S_IWUSR        00200                7
  • S_IXUSR         00100                6
  • S_IRGRP          00040               5
  • S_IWGRP        00020                4
  • S_IXGRP         00010                3
  • S_IROTH         00004                2
  • S_IWOTH        00002                1
  • S_IXOTH         00001                0      

示例:获取并显示文件属性

   以下面格式打印指定文件的主要信息:

 $ ./a.out test.c

 -rw-r—r--  317  2019 – 11 – 18  test.c

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>


int main(int argc, const char *argv[])
{
	struct stat buf;
	struct tm *tp;
	int n;


	if(argc<2)
	{
		printf("Usage : %s <file>\n",argv[0]);
		return -1;
	}

	if(lstat(argv[1],&buf) < 0)
	{
		perror("lstat");
		return -1;
	}
	switch(buf.st_mode & S_IFMT)  //辨别 文件类型 
	{
		case S_IFREG:
			printf("-");
		case S_IFDIR:
			printf("d");
			break;
	}
	for(n=8;n>=0;n--)
	{
		if(buf.st_mode & (1<<n)) // 辨别 文件的 权限 (9位)
		{
			switch(n%3)
			{
			case 2:
				printf("r");
				break;
			case 1:
				printf("w");
				break;
			case 0:
				printf("x");
				break;
			}
		}
		else
		{
			printf("-");
		}
	}
	printf(" %lu",buf.st_size);  //输出文件的目录的大小
	tp = localtime(&buf.st_mtime);  //得到上次更新时间
	printf(" %d-%02d-%02d",tp->tm_year+1900,tp->tm_mon+1,tp->tm_mday);
	printf(" %s\n",argv[1]);


	return 0;
}