欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

APUE——文件与文件IO

程序员文章站 2022-06-28 18:42:32
简析文件系统与文件io...

1 系统IO与文件IO的比较

文件IO的响应速度更快,但是标准IO的吞吐量更大!
系统IO比文件IO多了一步写入stdio buffer的操作,可以参考下图
APUE——文件与文件IO
APUE——文件与文件IO
注意标准IO和文件IO不要混用,因为两者的文件指针指向的buf不一样,前者是std buf,后者是内核buf(file*里有),可以用如下两个函数进行转换:

int fileno(FILE *stream); 将指针转成文件描述符
FILE *fdopen(int fd, const char *mode); 将文件描述符转成指针

2 文件操作

2.1 mode_t位图分析(16位)

关于mode_t类型,st_mode是一个16位的位图,用于表示文件类型,文件访问权限,以及特殊权限位

键入ls -l 查看文件状态,其中第一列为st_mode,其为16位的位图

APUE——文件与文件IO
APUE——文件与文件IO
APUE——文件与文件IO
实际16位如下所示,前四位表示文件类型,后九位表示文件权限APUE——文件与文件IO

2.2 stat获取文件属性

       int stat(const char *pathname, struct stat *statbuf);//返回statbuf
       int fstat(int fd, struct stat *statbuf); 
       int lstat(const char *pathname, struct stat *statbuf);
       
       lstat与stat区别在于,lstat函数遇到link文件,不会
       解析其源文件,只会解析其link文件本身,而stat会解析其源文件

struct stat结构体如下所示:
APUE——文件与文件IO
stat命令:
APUE——文件与文件IO
这里区分lstat与stat,如果入参为link类型,则stat描述的是符号链接链接的源文件,而lstat则描述符号链接文件

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

int main(int argc, char** argv)
{
    int rs1,rs2;
    struct stat *statbuf1= (struct stat *)malloc(sizeof(struct stat));
    struct stat *statbuf2= (struct stat *)malloc(sizeof(struct stat));
    rs1 = stat(argv[1],statbuf1);
    rs2 = lstat(argv[1],statbuf2);

    if(rs1<0)
    {
        perror("stat");
        exit(1);
    }
    if(rs2<0)
    {
        perror("lstat");
        exit(1);
    }
    printf("no1 of %d\n",statbuf1->st_blocks);
    printf("no2 of %d\n",statbuf2->st_blocks);
    free(statbuf1);
    free(statbuf2);
    exit(0);
}

APUE——文件与文件IO

2.3 umask

umask主要为了防止权限过松的文件
umask默认为0022,创建文件open,mkdir时,使用默认0666 & ~umask
也可以指定文件权限的位图,例如上例子open第三个参数,0600

mode_t umask(mode_t mask);

umask()将调用进程的文件权限创建掩码(umask)设置为mask & 0777(即:,只使用掩码的文件权限位),并返回之前的掩码值。

2.4 chmod

chmod可以更改权限,例如chmod 666 ./hellodup6, 更改成666权限
APUE——文件与文件IOAPUE——文件与文件IO

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

2.5 目录创建和销毁

增加文件目录和删除文件目录
mkdir

NAME
       mkdir, mkdirat - create a directory

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

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

rmdir

NAME
       rmdir - delete a directory

SYNOPSIS
       #include <unistd.h>

       int rmdir(const char *pathname); //删除空目录

DESCRIPTION
       rmdir() deletes a directory, which must be empty.

2.6 更改当前工作路径,获取

chdir() = cd

NAME
       chdir, fchdir - change working directory

SYNOPSIS
       #include <unistd.h>

       int chdir(const char *path);
       int fchdir(int fd);
DESCRIPTION
       chdir() changes the current working directory of the calling process to the directory specified in path.

       fchdir() is identical to chdir(); the only difference is that the directory is given as an open file descriptor.

获取当前路径,getcwd()= pwd

NAME
       getcwd, getwd, get_current_dir_name - get current working directory

SYNOPSIS
       #include <unistd.h>

       char *getcwd(char *buf, size_t size);
       输入参数buf为指定字符串,以及size长度,如果当前目录超过size,则报错。如果buf写NULL,则会自动malloc,需要free
       注意,成功时,buf为当前目录

使用getcwd与readdir,opendir,获取当前目录下所有文件

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <unistd.h>

#define BUFSIZE 50

int main(int argc,char** argv)
{
    DIR* dir;
    struct dirent* sdir;
    char* pwd = NULL;  //这里不能用char pwd[50]; pwd = getcwd(buf,BUFSIZE), 数组名是常量指针
    char buf[BUFSIZE];
    pwd = getcwd(buf,BUFSIZE); //如果是NULL,则会malloc,记得free
    puts(buf);
    dir = opendir(pwd);
    if(dir == NULL)
    {
        perror("dir");
        exit(1);
    }
    
    while(1)
    {
        sdir = readdir(dir);
        if((sdir == NULL)&&(errno!= 0))
        {
            perror("readdir");
            exit(1);
        }
        else if(sdir == NULL)
            break;
        puts(sdir->d_name);
    }

    closedir(dir);
    exit(0);
}

APUE——文件与文件IO

2.7 分析目录与读取目录

可以用glob函数,获取某个目录下(通配符)的所有文件。
glob函数,使用通配符pattern,来指定目录,其与int argc char** argv类似./glob ./*.c
glob申请了gl_pathv的内存,需要搭配globfree函数来销毁
APUE——文件与文件IO

           typedef struct {
               size_t   gl_pathc;    /* Count of paths matched so far  */
               char   **gl_pathv;    /* List of matched pathnames.  */
               size_t   gl_offs;     /* Slots to reserve in gl_pathv.  */
           } glob_t

这里注意,glob_t.gl_pathc≈argc ,glob_t.gl_pathv≈argv

#include <stdio.h>
#include <stdlib.h>
#include <glob.h>

#define PAT "./ *.c"
#define DIR "./ *" //为非隐藏,./.*为隐藏,这里不能用./来当做pattern参数

int main(int argc,char** argv)
{
    glob_t globres,globdir;
    int revalue = glob(PAT,0,NULL,&globres);
    int rdvalue = glob(DIR,0,NULL,&globdir);
    
    if(revalue)
    {
        printf("Errror code = %d\n",revalue);
        exit(1);
    }
    if(rdvalue)
    {
        printf("Errror code = %d\n",rdvalue);
        exit(1);
    }
    for(int i=0;i<globres.gl_pathc;i++)
    {
        printf("%s\n",globres.gl_pathv[i]);
    }
    puts("--------------------------------------");
    for(int i=1;argv[i]!=NULL;i++)
    {
        printf("%s\n",argv[i]);
    }
    puts("--------------------------------------");

    for(int i=0;i<globdir.gl_pathc;i++)
    {
        printf("%s\n",globdir.gl_pathv[i]);
    }
    globfree(revalue);
    globfree(rdvalue);
    exit(0);
}

APUE——文件与文件IO
上述两种获得当前路径文件的方法,glob与readdir等函数,其实际可能是利用了dentry结构中的双向链表遍历,同一层dentry的子目录来获取
APUE——文件与文件IO

2.8 目录流操作函数

文件操作与目录操作类似,均

       DIR *opendir(const char *name); //打开目录流
       DIR *fdopendir(int fd);  
       struct dirent *readdir(DIR *dirp);
	   int closedir(DIR *dirp);	
	   void rewinddir(DIR *dirp);  //复位目录流,回到头部
	   void seekdir(DIR *dirp, long loc);  //设置目录流位置
	   long telldir(DIR *dirp);  //告诉目录流当前位置
	   

这里分析下readdir函数:

struct dirent *readdir(DIR *dirp);
返回值:正常为struct dirent *类型的指针,读到底为NULL(当errno不等于0时,在判断出错)

struct dirent *类型如下所示:
struct dirent {
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below 这里d_off的与telldir是一样的,目录文件指针的位置*/
    unsigned short d_reclen;    /* Length of this record */
    unsigned char  d_type;      /* Type of file; not supported
                                   by all filesystem types */
    char           d_name[256]; /* Null-terminated filename 文件名*/
};

d_type有如下8种类型,其中7种为基础文件类型
DT_BLK      This is a block device.
DT_CHR      This is a character device.
DT_DIR      This is a directory.
DT_FIFO     This is a named pipe (FIFO).
DT_LNK      This is a symbolic link.
DT_REG      This is a regular file.
DT_SOCK     This is a UNIX domain socket.
DT_UNKNOWN  The file type could not be determined.

使用目录流操作函数来获取某目录下的所有文件,

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
int main(int argc,char** argv)
{
    DIR* dir;
    struct dirent* sdir;
    dir = opendir(argv[1]);
    if(dir == NULL)
    {
        perror("dir");
        exit(1);
    }
    
    while(1)
    {
        sdir = readdir(dir);  //持续读,有目录流指针定位
        if((sdir == NULL)&&(errno!= 0))
        {
            perror("readdir");
            exit(1);
        }
        else if(sdir == NULL)
            break;
        puts(sdir->d_name);
    }

    closedir(dir);
    exit(0);
}

APUE——文件与文件IO

3 open函数

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

flags可以如下设置
APUE——文件与文件IO
mode可以如下设置
APUE——文件与文件IO
APUE——文件与文件IO

open("hellodup5",O_RDWR|O_CREAT|O_TRUNC,0600);
0600 = user read 400 + write 200 ,其他为0

其中前三个表示文件拥有者的权限,中间三个表示文件所属组拥有的权限,最后三个表示其他用户拥有的权限。

4 lseek函数(与空洞文件)

#include <unistd.h>
off_t lseek( int filedes, off_t offset, int whence );

返回值:若成功则返回新的文件偏移量,若出错则返回-1。

  1. 按系统默认情况,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。
  2. lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作。然后,该偏移量用于下一个读或写操作。
  3. 文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,文件中空洞均为0。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char* argv[])
{

    int fd;

    fd = open(argv[1],O_WRONLY|O_CREAT|O_TRUNC,0600);
    if(fd<0)
    {
        perror("open()");
        exit(1);
    }
    lseek(fd,5ll*1024ll*1024ll*1024ll-1ll,SEEK_SET);

    write(fd,"",1);  //系统调用,导致lseek偏移的并非为空洞文件,直接cp,会导致Blocks为0,为空洞文件
    close(fd);

    exit(0);
}

APUE——文件与文件IO

通过 cp操作后, 使用stat或者ls -l查看文件状态,发现其Blocks为0,因为cp发现其为空洞文件,则没有执行write操作

APUE——文件与文件IO

lseek = fseek + ftell , 先执行fseek,设置文件指针,后通过ftell获取文件指针

4 程序重定向(dup=duplication)

dup与dup2,fcntl,类似于echo “???”> PATH

int dup(int oldfd); //将oldfd的指针复制到当前可用的最小的
int fcntl(int fd, F_DUPFD, arg );  //fcntl(fd,F_DUPFD,0) = dup(fd),注意,要大于等于arg的最小值
			Duplicate the  file  descriptor  fd  using  the  lowest-numbered
              available file descriptor greater than or equal to arg.  This is
              different from dup2(2), which uses exactly the  file  descriptor
              specified
int dup2(int oldfd, int newfd); //如果newfd已经打开,则close(newfd),再dup(oldfd),不再是最小的,而是newfd
 如果oldfd = newfd的话,则dup2什么都不会做,返回newfd

dup(dup使用时,可能是非原子,使用dup2代替可以)
例子1

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    int fd;
    FILE* fp;
    //close(1);

    fd = open("hellodup5",O_RDWR|O_CREAT|O_TRUNC,0600);
    // fp = fopen("hellodup1","w+");
    // fd = fileno(fp);
    if(fd<0)
    {    perror("fopen");
        exit(1);
    }
    close(1);
    dup(fd);  //这两步可以用dup(fd,1)代替
	if(fd != 1)	
		close(fd);
    printf("helloworld\n");
    
    exit(0);
}

例子2

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
    int fd;
    FILE* fp;
    //close(1);

    fd = open("hellodup5",O_RDWR|O_CREAT|O_TRUNC,0600);
    // fp = fopen("hellodup1","w+");
    // fd = fileno(fp);
    if(fd<0)
    {    perror("fopen");
        exit(1);
    }
	dup2(fd,1);  //如果fd = 1,则dup2什么都不干
	if(fd != 1)	
		close(fd); //则要判断fd是否为1,是1,则不close
    printf("helloworld\n");
    
    exit(0);
}

dup2,指向性替换fd


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    FILE* fp;
    int fd;
    fp = fopen("helloworld","w+");
    if(fp==NULL)
    {
        perror("fopen");
        exit(1);
    }
    fd = fileno(fp);
    printf("fd = %d\n",fd);
    //close(2);
    int fdn = dup2(fd,2);  //dup2是close(2),与dup(fd),的原子操作
    printf("???????\n");
    //fcntl(fd,F_DUPFD,1);
    for(int i=0;i<10; i++)
    {
        fprintf(stderr,"hello+");
    }
    fflush(stderr);
    printf("end/n");
    fclose(fp);

    exit(0);
}

本文地址:https://blog.csdn.net/weixin_44537992/article/details/107258860