一、Linux文件系統
Linux系統提供的文件系統,是樹形層次結構的系統,最頂層是根“/”,然後在下層創建其他目錄,所有的文件最終都歸屬到最頂層的根目錄“/”,Linux支持多種文件系統。
每個文件都具有特定的屬性,Linux系統的文件屬性比較複雜,主要包括文件類型和文件權限兩個方面。
在Linux中,最常見的文件類型有五種,他們是普通文件,目錄文件,銜接文件,管道文件,和設備文件。
點擊打開鏈接http://blog.csdn.net/muge0913/article/details/7339671博文中由對此五種文件類型的講解。
1、Linux文件權限
對於Linux系統中的文件來說,它的權限可以分爲 四種:可讀,可寫,可執行和無權限,分別用“r”,“ w”, “x”, “-”表示。
在Linux中,對於任意一個文件來說,它都有一個特定的所有者,就是這個文件的創造者,同時,Linux系統是按照組分類的,一個用戶屬於一個或多個組,文件所有者意外的用戶,又可以分爲文件所有者的同組用戶和其他用戶,因此Linux按照文件所有者,文件所有者同組用戶和其他用戶三類規定不同的文件訪問權限。
在查看文件的所有詳細信息時,可以看到文件顯示的作爲文件權限的10個字符,可分爲四部分:
clx@think:/etc$ ls -al
drwxr-xr-x 2 root root 12288 2013-05-09 23:37 alternatives
-rw-r--r-- 1 root root 395 2010-03-05 10:29 anacrontab
lrwxrwxrwx 1 root root 15 2013-04-29 23:18 blkid.tab -> /dev/.blkid.tab
第一位:表示文件類型.(“-” 普通文件,“l” 鏈接文件 “b”設備文件 “p” 管道文件,“d” 目錄文件)
第一位到第四位:(rwx)表示文件所有者的訪問權限。
第五位到第七位:(rwx)表示文件所有者同組用戶的訪問權限。
第八位到第十位:(rwx)表示文件所有者同組用戶的訪問權限。
2 、文件的相關信息
在Linux系統中,與文件相關的信息有三項,他們是:文件的目錄結構,索引節點和數據本身。
1.文件的目錄結構:
系統中的每一個目錄都處於一定的目錄結構中,該結構含有目錄中所有的目錄項的列表,每一個目錄項都含有一個名稱和索引節點,藉助於名稱可以訪問目錄項的內容,而索引節點號則提供了所需引用文件自身的信息。
2.索引節點
在Linux系統中,所有的文件都有一個與之相連的索引節點(inode),索引節點是用來保存文件信息的,它包含以下信息:
文件使用的設備號;
索引節點號;
文件訪問權限;
銜接到此文件的目錄樹;
所有者用戶識別號;
組識別號;
設備文件的設備號;
以字節爲單位的文件容量
包含該文件的磁盤塊的大小;
該文件所佔的磁盤塊數;
最後一次訪問該文件的時間;
最後一次修改該文件的時間;
最後一次改變了文件狀態的時間。
在系統中定義了stat結構體來存放這些信息,stat結構的定義如下:
Struct stat{
dev_t st_dev; /*文件使用的設備號*/
ino_t st_ino; /*節點號 */
mode_t st_mode; /*文件權限*/
nlink_t st_nlink; /*銜接到此文件的目錄樹*/
size_t st_size /*文件大小*/
uid_t st_uid; /*用戶ID*/
gid_t st_gid; /*組ID*/
dev_t st_rdev; /*設備類型*/
off_t st_pff; /*文件字節數*/
unsigned long st_blksize; /*塊大小*/
unsigned long st_blocks; /*塊數*/
time_t st_atime; /*最後一次訪問時間*/
time_t st_mtime; /*最後一次修改時間*/
time_t st_ctime; /*最後一次屬性改變時間*/
}
二、Linux中文件編程
Linux中文件編程可用兩種方法:Linux系統調用和C語言庫函數。前者依賴於Linux系統,後招與操作系統是相互獨立的,在任何操作系統下,使用C語言函數庫函數操作文件的方法都是相同的。
1、系統調用
在進程裏調用內核中用於實現各種功能的內核函數的通信方式就叫系統調用。所謂系統調用是指操作系統提供給用戶程序調用的一組“特殊”接口,用戶程序可以通過這組“特殊”接口來獲得操作系統內核提供的服務。例如用戶可以通過進程控制相關的系統調用來創建進程、實現進程調度、進程管理等。
Linux系統調用部分是非常精簡的系統調用(只有 250個左右) ,它繼承了 UNIX系統調用中最基本和最有用的部分。這些系統調用按照功能邏輯大致可分爲進程控制、進程間通信、文件系統控制、系統控制、存儲管理、網絡管理、socket 控制、用戶管理等幾類。
Linux下的每一個程序其真正的實現都是通過系統調用實現的。那麼我們程序該如何使用系統調用呢?其實很簡單,內核給我們提供了API,讓我們可以向調用普通函數那樣調用系統調用。唯一的區別就是系統調用由操作系統核心提供,運行於核心態;而普通的函數調用由函數庫或用戶自己提供,運行於用戶態。
1.1、文件描述符
在Linux環境下,內核如何區分和引用的特定文件呢?這裏面就用到一個重要概念,文件描述符,對於Linux而言,所有的設備和文件的操作都是使用文件描述符來進行的,文件描述符是一個非負的整數,它是一個索引的值,並指向內核中每個進程打開文件的記錄表,當打開一個現存或創建一個文件時,內核就返回一個文件描述符,當需要讀寫文件時,就需要把文件描述符作爲參數傳遞給相應的函數。
一個進程啓動時,默認打開了3個文件,標準輸入、標準輸出、標準錯誤,對應文件描述符是0(STDIN_FILENO)、1(STDOUT_FILENO)、2(STDERR_FILENO),這些常量定義在unistd.h頭文件中。
1.2、底層文件訪問
在Linux應用開發中,有着著名的五大系統調用:這五大系統調用是學習Linux應用程序開發的根基。他們是open,close ,read,write 和ioctl!。這些函數的特點是不帶緩存,直接對文件(包括設備)進行讀寫操作,這些不帶緩存的I/O函數不是ANSI C的組成部分,但是是POSIX.1和XPG3的組成部分。
(1)open系統調用
#include <sys/types.h> //提供類型p_id的定義
#include <sys/stat.h> //提供文件屬性的定義
#include <fcntl.h> //提供文件控制的相關定義
int open(const char *pathname,int flag)
int open(const char *pathname,int flag,mode_t mode)
函數參數
path :要打開的文件名(包含路徑,缺省爲當前路徑,是一個字符串)
flag :打開方式,必須制定文件訪問方式
O_RDONLY 只讀方式打開
O_WRONLY 可寫方式打開
O_RDWR 讀寫方式打開
O_CREAT 如果文件不存在,那麼創建這個文件,並且用第三個參數爲其設置權限
O_EXCL 如果文件使用O_CREAT時文件存在,則可返回錯誤消息,這一參數可用於測試文件是否存在
O_NOCTTY 如果文件爲終端,那麼終端不可以作爲調用open系用調用的那個進程的控制終端
O_TRUNC 如果文件已存在,並且以只讀或只寫方式成功打開,那麼會先刪除文件中原有的數據
O_APPEND 以添加方式打開,在打開文件的同時,文件指針指向文件的末尾
前三個方式必須制定,可通過“|”組合與後面的可選模式組合
mode :用來規定對該文件的所有者,文件的用戶組及系統中其他用戶的訪問權限
S_IRUSR 文件所有者的讀權限位
S_IWUSR 文件所有者的寫權限位
S_IXUSR 文件所有者的執行權限位
S_IRWXU S_IRUSR | S_IWUSR | S_IXUSR
S_IRGRP 文件用戶組的讀權限位
S_IWGRP 文件用戶組的寫權限位
S_IXGRP 文件用戶組的執行權限位
S_IRWXG S_IRGRP | S_IWGRP | S_IXGRP
S_IROTH 文件其他用戶的讀權限位
S_IWOTH 文件其他用戶的寫權限位
S_IXOTH 文件其他用戶的執行權限位
S_IRWXO S_IROTH | S_IWOTH | S_IXOTH
也可用二進制的形式表示
返回值 :打開成功,返回文件描述符;打開失敗,返回-1
(2)、close系統調用
爲了重新利用文件描述符,用close()系統調用釋放打開的文件描述符
#include <unistd.h>
int close(int fd);
函數參數:
fd :要關閉的文件的文件描述符
返回值:如果出現錯誤,返回-1;調用成功返回0
(3)、read系統調用
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
參數:
fd :想要讀的文件的文件描述符
buf : 指向內存塊的指針,從文件中讀取來的字節放到這個內存塊中
count : 從該文件複製到buf中的字節個數
返回值:
如果出現錯誤,返回-1;讀文件結束,返回0;否則返回從該文件複製到規定的緩衝區中的字節數
read()函數用於將從指定的文件描述符中讀出的數據放到緩存區中,並返回實際讀入的字節數。若返回 0,則表示沒有數據可讀,即已達到文件尾;返回-1,表示read調用出現錯誤。讀操作從文件的當前指針位置開始。當從終端設備文件中讀出數據時,通常一次最多讀一行。
(4)、write系統調用
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
函數參數:
fd:要寫入的文件的文件描述符
buf: 指向內存塊的指針,從這個內存塊中讀取數據寫入 到文件中
count: 要寫入文件的字節個數
返回值:如果出現錯誤,返回-1;如果寫入成功,則返回寫入到文件中的字節個數
write()函數用於向打開的文件寫數據,寫操作從文件的當前指針位置開始。對磁盤文件進行寫操作,若磁盤已滿或超出該文件的長度,則 write()函數返回失敗。
(5)、ioctl系統調用
ioctl用於向設備發控制和配置命令,有些命令也需要讀寫一些數據,但這些數據是不能用read/write讀寫的,稱爲Out-of-band數據。也就是說,read/write讀寫的數據是in-band數據,是I/O操作的主體,而ioctl命令傳送的是控制信息,其中的數據是輔助的數據。例如,在串口線上收發數據通過read/write操作,而串口的波特率、校驗位、停止位通過ioctl設置,A/D轉換的結果通過read讀取,而A/D轉換的精度和工作頻率通過ioctl設置。
#include <sys/ioctl.h>
int ioctl(int fd, int request, ...);
fd是某個設備的文件描述符。request是ioctl的命令,可變參數取決於request,通常是一個指向變量或結構體的指針。若出錯則返回-1,若成功則返回其他值,返回值也是取決於request。
此函數在LCD背光應用程序中有使用ioctl(fd, turn)來給引腳制定參數。根據特定設備所支持操作的不通,他可能還有可選的第三個參數。
2、系統調用綜合實例
文件拷貝程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#define BUFFER_SIZE 1024
int main(int argc,char **argv)
{
int from_fd,to_fd;
int bytes_read,bytes_write;
char buffer[BUFFER_SIZE];
char *ptr;
if(argc!=3)
{
fprintf(stderr,"Usage:%s fromfile tofile/n/a",argv[0]);
exit(1);
}
/* 打開源文件 */
if((from_fd=open(argv[1],O_RDONLY))==-1) //argv[1]表示要打開的文件
{
fprintf(stderr,"Open %s Error:%s/n",argv[1],strerror(errno));
exit(1); //無條件地停止剩下的所有操作,參數1表示出現錯誤進程非正常結束
}
/* 創建目的文件 */
if((to_fd=open(argv[2],O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR))==-1)
{
fprintf(stderr,"Open %s Error:%s/n",argv[2],strerror(errno));
exit(1);
}
/* 以下代碼是一個經典的拷貝文件的代碼 */
while(bytes_read=read(from_fd,buffer,BUFFER_SIZE))
{
/* 一個致命的錯誤發生了 */
if((bytes_read==-1)&&(errno!=EINTR)) break;
else if(bytes_read>0)
{
ptr=buffer;
while(bytes_write=write(to_fd,ptr,bytes_read))
{
/* 一個致命錯誤發生了 */
if((bytes_write==-1)&&(errno!=EINTR))break;
/* 寫完了所有讀的字節 */
else if(bytes_write==bytes_read) break; //沒有讀完跳出本while程序,轉而繼續執行while循環後的語句,即繼續上面的while循環內的判斷
/* 只寫了一部分,繼續寫 */
else if(bytes_write>0)
{
ptr+=bytes_write;
bytes_read-=bytes_write;
}
}
/* 寫的時候發生的致命錯誤 */
if(bytes_write==-1)break;
}
}
close(from_fd);
close(to_fd);
exit(0); //無條件地停止剩下的所有操作,參數,0表示正常結束
}
編譯測試:
clx@think:/work/armlinux/codetest$ gcc -o file_cp file_cp.c
clx@think:/work/armlinux/codetest$ ./file_cp backlight_test.c backlight_test1.c
clx@think:/work/armlinux/codetest$ ls
backlight_test backlight_test1.c backlight_test.c file_cp file_cp.c