嵌入式Linux C編程 04

5.1.一、虛擬文件系統

第一層、虛擬文件系統(VFS)node

第二層、各類不一樣的具體的文件系統緩存

VFS就是把各類具體的文件系統的公共部分抽取出來,造成一個抽象層,是系統內核的一部分。bash

查看系統支持哪些文件系統:異步

# cat /proc/filesystems

5.1.二、通用文件模型

VFS的主要目的在於引入了一個通用的文件模型(Common File Model),這個模型的核心是4個對象類型,即超級塊對象(Superblock Object)、索引節點對象(Inode Object)、文件對象(File Object)和目錄項對象(Dentry Object)。socket

  • 超級塊:Super Block,存放系統中已安裝文件系統的有關信息。
  • 索引節點:Inode,對應於存放在磁盤上的文件控制塊(File Control Block)。文件->索引節點->索引節點號->惟一標識
  • 文件對象:File Object,存放打開文件與進程之間進行交互的有關信息,是進程與文件系統的橋樑。
  • 目錄項:Dentry,存放目錄項與對應文件連接的信息。

一、超級塊對象函數

用來描述整個文件系統信息。VFS超級塊是由各類具體的文件系統在安裝時創建的,只存在於內存中。ui

超級塊對象經過alloc_super()函數建立並初始化。在文件系統安裝時,內核會調用該函數以便從磁盤讀取文件系統超級塊,而且將其信息填充到內存的超級塊對象中。spa

二、索引節點對象指針

文件系統中處理文件所須要的信息都放在索引節點對象裏。文件名能夠隨時更改,可是索引節點是惟一的。code

類型:

  • 磁盤文件:狹義的磁盤上存儲的文件、數據文件、進程文件
  • 設備文件:
  • 特殊節點

inode有一個惟一的i_ino節點號。每一個文件都有一個文件主,指文件的建立者,可改變。每一個用戶都有用戶組,所以inode結構中應有i_uid、i_gid,指明身份權限。

inode中還有兩個設備號i_dev和i_rdev,分別表明主設備號和從設備號。

節點的管理:

  • 未使用索引節點的鏈表:用變量inode_unused表示。
  • 已使用索引節點的鏈表
  • 髒索引節點列表:Hash表。

5.1.三、Linux下的設備文件

  • 字符設備
  • 塊設備

字符設備:以字節爲單位逐個進行I/O操做的設備。不支持隨機訪問。

塊設備:利用一塊系統內存做爲緩衝區。先緩衝區,後實際I/O操做。

 

5.2.一、不帶緩存的文件I/O操做

一、文件描述符,非負整數。

二、open 和 close

#include <fcntl.h>

int open(const char *pathname,    /*被打開的文件名*/
         const char flags,        /*文件打開的方式*/
         int perms)               /*被打開文件的存取權限,八進制*/

// 成功:返回文件描述符
// 失敗:-1

#include <unistd.h>

int close(int fd);    /*fd爲文件描述符*/

// 成功:0
// 出錯:-1
// 調用示例

int fd = open("/tmp/hello.c", O_WRONLY | O_CREATE | O_TRUNC, 0666);

close(fd);

三、read、write和lseek

#include <unistd.h>

size_t read/write (int fd,          /*文件描述符*/
                   void *buf,       /*緩衝區*/
                   size_t count);   /*字節數*/

// 成功:實際字節數
// 已達文件尾(讀):0
// 出錯:1
#include <unistd.h>
#include <sys/types.h>

off_t lseek(int fd,          /*描述符*/
            off_t offset,    /*偏移量,單位是字節,可正可負*/
            int whence);     /*基點*/

// 成功:文件的當前位置
// 出錯:-1

char buf_write[] = "abcdefg";
char buf_read[10];
int fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR, 0666);
int len = strlen(buf_write) + 1;
int size = write(fd, buf_write, len);
lseek(fd, 0, SEEK_SET);
size = read(fd, buf_read, len);

四、fcntl

複製現有描述符,得到/設置文件描述符標記,得到/設置文件狀態標記。得到/設置異步I/O全部權及得到/設置記錄鎖。

文件鎖包括建議性鎖和強制性鎖。

鎖函數,lockf 和 fcntl,lockf施加建議性鎖,而fcntl不只能夠施加建議性鎖,還能夠施增強制性鎖。還能夠實現記錄鎖。

記錄鎖:

  • 讀取鎖(共享鎖)
  • 寫入鎖(排斥鎖)
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd,           /*文件描述符*/
          int cmd,          /*不一樣的命令*/
          struct flock *lock);     /*設置記錄鎖的具體狀態*/

// 成功:0
// 出錯:-1

/*lock結構*/

struct flock
{
    short l_type;
    off_t l_start;
    short l_whence;
    off_t l_len;
    pid_t l_pid;
}

加鎖整個文件經常使用的方法是將l_start說明爲0,l_where說明爲SEEK_SET,l_len說明爲0。

五、select

fcntl函數解決了文件的共享問題,接下來處理I/O多路複用。

I/O處理模型:

  • 阻塞I/O模型:進程睡眠
  • 非阻塞模型:不睡眠,返回一個錯誤。一般採用輪詢處理多路I/O,耗費CPU資源。
  • I/O多路複用模型:把須要處理的多路I/O交由內核監控後睡眠。就緒時,被喚醒處理。
  • 信號驅動I/O模型:不輪詢也不阻塞。I/O就緒時,內核發送SIGIO信號給進程,進程在信號處理函數中完成操做。
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>

int select(int numfds,         /*須要檢查的最大的文件描述符+1*/
           fd_set * readfds,   /*監視的讀文件描述符集合*/
           fd_set * writefds,  /*監視的寫文件描述符集合*/
           fd_set * exeptfds,  /*監視的異常處理文件描述符集合*/
           struct timeval *timeout);  /*等待時間*/

// 成功:已就緒的文件描述符的數量
// 出錯:-1

struct timeval
{
    long tv_sec;
    long tv_usec;
}

5.2.2 標準I/O

前述文件的I/O都是基於文件描述符的不帶緩存的操做。標準I/O都是基於流的,符合ANSI C的操做。

又稱高級磁盤I/O,是在文件I/O的基礎上進行了封裝。

  • 全緩衝
  • 行緩衝
  • 無緩衝

一、FILE指針

標準I/O爲每一個打開的文件在內存中開闢一個區域,用來存放文件的相關信息。這些信息被保存在一個由系統定義的結構體類型FILE中。在標準I/O中,流(stream)用FILE *來描述,全部的操做都是圍繞流來進行的。

in <stdio.h>
typedef struct _IO_FILE FILE;

in <libio.h>

struct _IO_FILE
{
    int _flags;
    ...
    int _fileno;
    ...
};

標準I/O庫中預約義了3個流:標準輸入(stdin)、標準輸出(stdout)和標準錯誤(stderr)。當一個程序執行時,系統自動打開這3個流,而且能夠在程序中直接使用。

二、打開流

打開文件有3個標準函數,分別爲fopen、fdopen和freopen。

fopen指定打開文件的路徑和模式;fdopen指定打開的文件描述符和模式;freopen還可指定特定的I/O流。

#include <stdio.h>

FILE * fopen(const char * path,
             const char * mode);

FILE * fdopen(int fd,
              const char * mode);

FILE * freopen(const char * path,
               const char * mode,
               FILE * stream);

// 成功:指向FILE的指針
// 失敗:NULL

#include <stdio.h>

int fclose(FILE * stream);

// 成功:0
// 失敗:EOF(-1)

四、按字符讀/寫文件

#include <stdio.h>

int fgetc(FILE * stream);
int getc(FILE * stream);
int getchar(void);
int fputc(int c, FILE * stream);
int putc(int c, FILE * stream);
int putchar(int c);

// 成功:字符
// 失敗:EOF

五、按行讀/寫文件

#include <stdio.h>

char *fgets(char *s, int n, FILE *stream);
int fputs(const char *s, FILE *stream);
int puts(const char *s);

// fgets 成功:緩衝區地址,失敗:NULL
// fputs 成功:字符串長度,失敗:EOF
// puts 成功:字符串長度+1,失敗:EOF

六、按指定格式讀/寫文件

一般處理二進制文件。

#include <stdio.h>

size_t fread/fwrite(void * ptr,       /*緩衝區*/
                    size_t size,      /*讀取的記錄大小*/
                    size_t nmemb,     /*讀取的記錄數*/
                    FILE * stream);   /*文件流*/

// 成功:實際對象數量
// 失敗:EOF

七、刷新流

#include <stdio.h>

int fflush(FILE * stream);

// 成功:0
// 失敗:-1

八、文件定位

#include <stdio.h>

void rewind(FILE * stream);
int fseek(FILE * stream,
          long offset,
          int whence);
long ftell(FILE * stream);

// fseek 成功:0,失敗:-1
// ftell 成功:文件當前位置,失敗:-1L

5.三、Linux下對文件和目錄的操做

5.3.一、文件類型

  • 目錄文件(directory file):
  • 普通文件(regular file):
  • 字符設備文件(character device file):
  • 塊設備文件(block device file):
  • FIFO:進程間通訊
  • 套接字(socket):
  • 符號鏈接(symbolic link):

5.3.三、獲取文件屬性

stat/fstat/lstat函數

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

int stat(const char *filename, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *filename, struct stat *buf);    /*支持符號連接*/

struct stat
{
    dev_t st_dev;   // 文件所在的設備名稱
    ino_v st_ino;   // 文件對應的i節點號
    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;  // 上次改變狀態時間
}

st_mode

// 成功:0
// 失敗:-1,並設置errno

struct stat buf;

fstat("./data", &buf);
switch(buf.st_mode & S_IFMT)
{
    case S_IFREG: printf("regular file\n"); break;
    case S_IFDIR: printf("directory\n"); break;
    default: printf("other file types\n); break;
}

5.3.四、修改文件訪問權限

chmod/fchmod函數

#include <sys/types.h>
#include <sys/stat.h>

int chmod(const char *filename, mod_t mode);
int fchmod(int fd, mode_t mode);

// 成功:0
// 失敗:-1,並設置errno
chmod("./data", S_IRWXU|S_IRGRP|S_IWGRP|S_IROTH);

5.3.五、建立目錄

mkdir函數

#include <sys/stat.h>

int mkdir(const char *filename, mode_t mode);

// 成功:0
// 失敗:-1,並設置errno

mkdir("mydir", 0755);

5.3.六、建立連接文件

#include <unistd.h>

int link(const char *path1, const char *path2);  /*建立硬連接path2*/
int symlink(const char *path1, const char *path2); /*建立符號連接path2*/

// 成功:0
// 失敗:-1,並設置errno

link("./data", "./data1");
symlink("./data", "./data2");

5.3.七、刪除文件

unlink/remove函數

#include <unistd.h>

int unlink(const char *filename);
int remove(const char *filename);

// 成功:0
// 失敗:-1,並設置errno

unlink("./data");

5.3.八、重命名文件

#include <unistd.h>

int rename(const char *old, const char *new);

// 成功:0
// 失敗:-1,並設置errno

rename("./data_old", "./data_new");
相關文章
相關標籤/搜索