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中。
            用来处理描述符集合。   
 

你可能感兴趣的:(Linux文件操作)