天天看點

嵌入式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;
}