特點與概念
linux中常見的文件類型如下:
普通文件 :磁盤文件,能夠進行隨即存取的數據存儲單位,他是面向字節的
管道文件 :有兩種類型的管道,有名管道和無名管道
目錄文件 :保存在目錄中的文件的列表
設備文件 :提供到物理設備的接口
符號鏈接 :包含到達另一個文件的路徑的文件
套接口 :類似於管道,但是通信距離可以不再同一臺機器上。
文件模式
文件訪問和修飾的位掩碼宏
S_ISUID 04000 set user ID on execution
S_ISGID 02000 set group ID on execution
S_ISVTX 01000 sticky bit
S_IRUSR 00400 read by owner
S_IWUSR 00200 write by owner
S_IXUSR 00100 execute/search by owner
S_IRGRP 00040 read by group
S_IWGRP 00020 write by group
S_IXGRP 00010 execute/search by group
S_IROTH 00004 read by others
S_IWOTH 00002 write by others
S_IXOTH 00001 execute/search by others
文件類型常量:
S_IFMT 00170000 所有文件類型
S_IFSOCK 00140000 套接口文件
S_IFLNK 00120000 符號連接
S_IFREG 00100000 普通文件
S_IFBLK 00060000 塊設備
S_IFDIR 00040000 目錄文件
S_IFCHR 00020000 字符設備
S_IFFIFO 00010000 FIFO文件
umask
在創建新的文件的時候,可以用umask屏蔽掉不需要的權限,其原型如下:
#include <sys/types.h>
#include <sys/stat.h>
mode_t umask(mode_t mask);
例如:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(void)
{
mode_t after = 0444; /*屏蔽掉所有的讀的權限*/
mode_t before;
system("touch before"); /*touch命令所建立的文件是644的*/
before = umask(after);
printf("before : %#o\nafter : %#o\n",before, after);
system("touch after"); /*更改權限後,再次建立文件*/
system("ls before after -l");
system("rm -f before after");
return 0;
}
輸出:
before : 022
after : 0444
--w--w--w- 1 gin vboxusers 0 08-27 10:14 after
-rw-r--r-- 1 gin vboxusers 0 08-27 10:14 before
在未調用umask之前的權限是644,之後的是222,很明顯將所有的讀的權限全部都去掉了。
文件描述符
文件描述符是一個很小的正整數,他是一個索引值,指向內核爲每一個進程所維護的此進程打開的文件的記錄表。例如:stdin是0,stdout是1,stderr是2,這是每個進程都會打開的,他們對應的宏是STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。
使用open()和creat()函數都能打開一個文件描述符,他們的原型爲:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open (const char *pathname, int flags);
int open (const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
注意,這裏open()有兩種形式,pathname是你要打開的文件的名字,flags是以什麼方去操作這個文件,只讀,只寫,或者是讀寫,如果按默認(umask())的文件模式就用地一種形式,即不指定mode,否則就使用第二種模式。如果成功就返回一個文件描述符,否則返回-1並設置errno變量。
flags:
O_RDONLY 只讀
O_WRONLY 只寫
O_RDWR 讀寫
O_CREAT 若文件不存在則創建該文件
O_EXCL 僅與O_CREAT連用,如果文件存在,則強制open失敗
O_NOCTTY 如果打開的是一個終端,就不會成爲打開其進程的控制終端。
O_TRUNC 如果文件存在,則將文件長度截至0
O_APPEND 將文件指針設置到文件結尾處(如果打開來寫)
O_NONBLOCK 如果讀操作沒有blocking(由於某種原因被拖延)則無法完成時,讀操作返回0字節
O_NODELAY 同上
O_SYNC 在數據被物理的寫入磁盤或其他設備之後操作才返回
creat()也能打開一個文件,當文件不存在的時候,則創建他,成功返回一個文件描述符,失敗返回-1,並設置errno變量。
關閉一個文件描述符的方法是調用close()函數,原型爲:
#include <unistd.h>
int close (int fd);
調用該文件之後,該進程對文件fd所加的鎖全部都會被釋放,如果關閉文件倒是他的連接數爲0,則該文件將會被刪除。
例如:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(void)
{
int fd;
if (0 > (fd = open("test", O_CREAT | O_RDONLY, 0644)))
{
perror("File test:");
return 0;
}
system("ls -l");
close(fd);
return 0;
}
輸出爲:
總計 36
-rwxr-xr-x 1 gin vboxusers 5073 08-27 10:55 a.out
-rw-r--r-- 1 gin vboxusers 293 08-27 10:55 open.c
-rw-r--r-- 1 gin vboxusers 0 08-27 10:54 test
-rw-r--r-- 1 gin vboxusers 542 08-27 10:14 umask.c
-rw------- 1 gin vboxusers 0 08-27 08:58 輸入輸出
文件描述符的讀寫操作
讀操作:
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
其中,fd是用open打開的文件描述符,buf是用來存放從fd讀出的count字節的內容的存放的地方。如果read調用成功,就會返回讀出的字節數,如果出錯就會返回-1,並設置errno變量。但是,假如有一個文件有10個字節,我們每次都從裏面讀出5個的話,第二次讀完之後,按理來說一共十個字節已經讀完了,但他還是又多讀了一次,這是爲什麼?誰能解釋解釋,是不是因爲他沒有讀到EOF標誌,就不算讀完?當我再用fopen函數在去讀的時候,就沒有再多讀一次,但是用feof函數卻檢測不出讀完了,是不是feof也沒遇到EOF,所以他會多讀一次然後遇到EOF才設置EOF標誌爲?
寫操作:
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
將buf中的count字節寫入到fd中。
例如:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fscr;
int fdst;
int size;
char scr[20];
char dst[20];
char buf[10];
puts("Input scr and dst file : \n");
scanf("%20s %20s",scr, dst);
if ((0 > (fscr = open(scr, O_RDONLY)))
|| (0 > (fdst = open(dst, O_WRONLY | O_CREAT,0644))
{
puts("File eror\n");
exit(EXIT_FAILURE);
}
while (size = read(fscr, buf, 10))
write(fdst, buf, size);
close(fscr);
close(fdst);
return 0;
}
上例中用read和write簡單的完成了文件的拷貝工作,當然,這是一個不安全的版本。
改變文件大小:
ftruncate()函數,原型爲:
#include <unistd.h>
int ftruncate (int fd, off_t length);
他可以將fd的長度固定到length大小,若fd本身的大小不足length則後面會填充一些東西,如果fd的長度超過了length,那麼,fd的長度將會被截短到length的長度。當length的值爲0的時候,該函數的就可以將fd清空,但是,文件在打開的時候必須以寫的模式打開。函數調用成功後返回0,失敗返回-1,並設置errno變量。
文件讀寫指針定位
函數lseek的作用和庫函數fseek的作用一樣,只是前者操作的是文件描述符後者操作的是文件指針。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
whence可以是下面三個宏中的任意一個:
SEEK_SET 表示以文件的開始爲相對位置
SEEK_CUR 表示以當前指針的位置作爲相對位置,此時的offset可以爲負數
SEEK_END 表示以文件末尾爲相對的位置
如果調用成功,則返回新的指針的位置,否則返回-1,並設置errno變量。
寫入到硬盤
使用fsync函數可以將所有的已經寫入文件描述符fd的數據寫道硬盤或者其他設備上去,
#include <unistd.h>
int fsync(int fd);
#ifdef _POSIX_SYNCHRONIZED_IO
int fdatasync(int fd);
#endif
成功返回0,否則的返回-1,並設置errno變量。fdatasync和fsync類似,但不寫入文件的索引節點信息,如修改時間等。
獲得文件信息
#include <sys/stat.h>
#include <unistd.h>
int fstat (int fd, struct stat * buf);
將文件描述符fd的信息保存到buf中去,成功返回0,失敗返回-1並設置errno變量。
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* number of blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
利用下面的一些宏可以方便的判斷文件類型:
S_ISREG(m) is it a regular file?
S_ISDIR(m) directory?
S_ISCHR(m) character device?
S_ISBLK(m) block device?
S_ISFIFO(m) FIFO (named pipe)?
S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)
S_ISSOCK(m) socket? (Not in POSIX.1-1996.)
其中,參數m可以用stat中的st_mode的填入;
例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main(void)
{
int fd;
char f[50];
struct stat *buf = (struct stat *)malloc(sizeof (struct stat));
puts("Input a file:\n");
scanf("%s",f);
if (0 > (fd = open(f,O_RDONLY)))
{
perror("File failed\n");
exit(EXIT_FAILURE);
}
if (0 > fstat(fd, buf))
{
perror("test error\n");
exit(EXIT_FAILURE);
}
if (S_ISREG(buf->st_mode))
puts("It's a common file.\n");
else if (S_ISDIR(buf->st_mode))
puts("It's a direction file.\n");
else if (S_ISCHR(buf->st_mode))
puts("It's a character device.\n");
else if (S_ISBLK(buf->st_mode))
puts("It's a block device.\n");
else if (S_ISLNK(buf->st_mode))
puts("It's a link file.\n");
else if (S_ISFIFO(buf->st_mode))
puts("It's a FIFO file.\n");
else if (S_ISSOCK(buf->st_mode))
puts("It's a socket file.\n");
else
puts("Unkown\n");
close(fd);
return 0;
}
文件權限的更改
更改所有權:
#include <sys/types.h>
#include <unistd.h>
int chown(const char *path, uid_t owner, gid_t group);
該函數和命令的使用方法一樣,成功返回0,否則返回-1並設置errno變量。
更改讀寫權限:
#include <sys/types.h>
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fildes, mode_t mode);
給文件上鎖
#include <sys/file.h>
int flock(int fd, int operation);
請求或刪除由文件描述符fd引用的文件上的一個建議性鎖。其中operation的取值如下:
LOCK_SH 共享性鎖
LOCK_EX 排斥性鎖
LOCK_UN 解鎖
LOCK_NB 於其他值進行或操作以防止上鎖。
在任意時刻,只能有一個進程加排斥性鎖,但可以有多個進程加共享性鎖。由flock施加的鎖不能和fcntl或者lockf加的鎖進行通訊,也不能和/var/lock下的UUCP鎖文件進行通信。
通常訪問一個上鎖文件的步驟如下:
1。檢查是否鎖
2。如果沒有,則自己建立。
3。打開文件
4。對文件作必要的處理
5。關閉文件
6。對文件進行解鎖
函數fcntl()
他能夠建立記錄鎖,所謂的記錄鎖是指僅對文件的一部分加鎖而不是整個文件,這樣就提高了文件的有效利用率。同時,他還能夠用於讀取鎖和寫入鎖,read lock也稱爲共享鎖(shared lock),因爲有多個進程能夠在同一文件上或者在文件的同一部分建立shared lock。而write lock也稱爲排斥鎖,因爲只在任何時刻只能有一個進程在文件的某個部分加write lock。當然,在建立write lock的部分也不能在建立read lock,當然,fcntl函數的作用不但但是給文件加鎖這麼簡單。
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock * lock);
其中,cmd的作用使用來說明fcntl他應該做什麼工作,他的取之如下:
F_DUPFD 複製文件描述符fd
F_GETFD 獲得fd的close-on-exec標誌,若標誌沒有設置,仍爲0,則文件愛你經過exec系列調用之後,仍然保持打開狀態
F_SETFD 設置close-on-exec標誌,以便在arg中傳送的值
F_GETFL 得到open設置的標誌
F_SETFL 改變open設置的標誌
F_GETLK 得到離散的文件鎖
F_SETLK 設置獲得離散的文件鎖,不等待
F_SETLKW 設置獲得離散的文件鎖,在需要時,等待
F_GETOWN 檢索將受到SIGIO和SIGURG信號的進程ID或者進程組號
F_SETOWN 設置進程ID或者進程組號
struct flock {
short l_type;
short l_whence;
off_t l_start;
off_t l_len;
pid_t l_pid;
__ARCH_FLOCK_PAD
};
#define F_RDLCK 0
#define F_WRLCK 1
#define F_UNLCK 2
此時僅僅討論文件加鎖的問題,要設置鎖,需要傳遞值爲F_SETLK或者F_SETLKW,設置locl.l_type的值爲F_RDLCK(read lock)或者F_WRLCK(用於寫入鎖),相反,要清除鎖,則設置lock.l_type的值爲F_UNLCK,成功設置之後,函數返回0,失敗返回-1,並設置errno的值爲EAGIN。要檢查鎖的狀態,可以用F_GETLK,如果進程已經設置了鎖,則會在flock結構中填滿相關信息。否則,lock.l_type將會等於F_UNLCK,表明沒有設置任何鎖。
例如:
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
int setlock(int fd, int type);
int main(void)
{
int fd;
if (0 > (fd = open("test", O_CREAT | O_RDWR, 0666)))
{
perror("open file failed\n");
exit(EXIT_FAILURE);
}
printf("Process ID : %d\n", getpid());
puts("set a read lock\n");
setlock(fd,F_RDLCK);
puts("done\n");
getchar();
puts("unlock the file\n");
setlock(fd, F_UNLCK);
puts("done\n");
getchar();
puts("set a write lock\n");
setlock(fd, F_WRLCK);
puts("done\n");
getchar();
close(fd);
return 0;
}
int setlock(int fd, int type)
{
struct flock l;
l.l_whence = SEEK_CUR;
l.l_start = 0;
l.l_len = 1;
while (1)
{ /*這裏是想不斷的嘗試設置鎖,直到他成功*/
l.l_type = type;
if (fcntl(fd, F_SETLK, &l))
return 0;
fcntl(fd, F_GETLK, &l);
if (l.l_type == F_UNLCK)
continue;
switch (l.l_type)
{
case F_RDLCK :
printf("read lock setted by %d\n",l.l_pid);
break;
case F_WRLCK :
printf("write lock setted by %d\n",l.l_pid);
break;
}
sleep(3);
}
}
在這裏main函數很簡單,主要用來顯示信息,而setlock函數作了決大多數工作,他將爲fd加鎖,當失敗時,會去查找原因,並延時3秒鐘後再次嘗試設置,直到文件被成功的加上鎖。當然如果你同時用兩個終端來運行他,那麼你就會更加明白其中的很多事情的。
文件描述符的複製dup和dup2函數
#include <unistd.h>
int dup (int oldfd);
int dup2 (int oldfd, int newfd);
複製一個現存的文件描述符,成功返回新的文件描述符,失敗返回-1,在dup2函數中,若newfd已經打開,則會現將其關閉,若新舊一樣的話,那麼就不會關閉,而是返回該值。此時,新老文件描述符共享文件偏移量,標誌,和鎖,但是,他們並不共享close-on-exec標誌。例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int ofd;
int nfd;
char *t1 = "1234567890";
char *t2 = "9876543210";
if (0 > (ofd = open("test",O_CREAT | O_WRONLY)))
{
perror("open test failed\n");
exit(EXIT_FAILURE);
}
write(ofd, t1, 10);
nfd = dup(ofd);
write(nfd, t2, 10);
close(ofd);
close(nfd);
return 0;
}
該程序,首先創建一個文件,然後用舊的文件描述符想裏面寫一段數字,然後用dup函數複製該文件描述符,然後用新的文件描述符向裏面寫了一段數字,當程序結束的時候,打開test文件,就會發現,剛纔寫的兩段文字都被放到了test文件中。
dup2的作用常用來作重定向的工作,也就是說,本來應該對newfd的做得事情,被轉移到oldfd上來作,例如,將stdout重定向到一個文件中去,這樣,在屏幕上輸出的將會被寫到文件裏去。例如:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
char *buf = "123456789";
if (0 > (fd = open("test", O_CREAT | O_WRONLY, 0644)))
{
perror("open test failed\n");
exit(EXIT_FAILURE);
}
dup2(fd, STDOUT_FILENO);
printf("%s",buf);
close(fd);
return 0;
}
該程序將標準輸出重定向到test文件中,也就是說,在程序中使用想printf函數這樣對標準輸出文件操作的函數,都將會轉向對test的操作。
同時讀寫多個文件
系統調用select函數可以實現多個文件的同時讀寫工作,這種事尤其是在服務器程序上面的應用十分廣泛,因爲他們要處理很多的文件,如果一個一個處理的話,效率會很低。其原型如下:
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
其中,nfds通常是在受監視的集合中文件描述符最大的的加1的值。readfds是用來讀取數據的集合,writefds是用來寫入數據的集合,exceptfds是文件異常的集合。而最後一個參數timeout表示將會阻塞多久。如果timeout設置爲0,則函數會立即返回,也就是非阻塞模式,當想等待到有情況發生,比如readfds/writefds發生改變,或者出現了錯誤,若想在這種情況下在返回,就直接傳入NULL,如果函數成功,將返回受監視的文件描述符集合中包含的文件描述符的個數,或者返回0表示,在函數返回前沒有描述符改變狀態。如果函數調用出錯,就會返回-1,並設置errno變量。
另外,
FD_ZERO(fd_set *set); 清除集合set
FD_SET(int fd, fd_set *set); 把fd添加到set
FD_CLR(int fd, fd_set *set); 把fd從set中刪除
FD_ISSET(int fd, fd_set *set);判斷fd是否在set中。
用來處理描述符集合。
Linux文件操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.