Linux文件操作

 

特點與概念
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中。
            用來處理描述符集合。   
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章