当前位置: 移动技术网 > 网络运营>服务器>Linux > APUE——文件与文件IO

APUE——文件与文件IO

2020年07月16日  | 移动技术网网络运营  | 我要评论

1 系统IO与文件IO的比较

文件IO的响应速度更快,但是标准IO的吞吐量更大!
系统IO比文件IO多了一步写入stdio buffer的操作,可以参考下图
在这里插入图片描述
在这里插入图片描述
注意标准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位的位图

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
实际16位如下所示,前四位表示文件类型,后九位表示文件权限在这里插入图片描述

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结构体如下所示:
在这里插入图片描述
stat命令:
在这里插入图片描述
这里区分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);
}

在这里插入图片描述

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权限
在这里插入图片描述在这里插入图片描述

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);
}

在这里插入图片描述

2.7 分析目录与读取目录

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

           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);
}

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

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);
}

在这里插入图片描述

3 open函数

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

flags可以如下设置
在这里插入图片描述
mode可以如下设置
在这里插入图片描述
在这里插入图片描述

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);
}

在这里插入图片描述

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

在这里插入图片描述

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网