文件IO知识回顾

文件IO

1、可执行程序、shell命令

2、系统调用函数

3、库函数

4、查看特殊文件格式规范

系统调用
  • 系统调用的本质:内核提供给外部,访问Linux操作的系列函数
  • 以printf函数,打印hello字符串到屏幕为例
  • 操作函数
打开文件
#include <unistd.h>  
#include<fcntl.h>
int open(const char *pathname, int flags);1:打开文件的路径名
    参2:打开文件的方式
        O_RDONL|O_WRONLY|O_RDWR
       	成功返回一个新的文件描述符(整数)
        失败返回-1
  int open(const char *pathname, int flags, mode_t mode);3:打开文件的方式
        O_RDONLY|O_WRONLY|O_RDWR #include<fcntl.h>
        O_CREAT | O_NONBLOCK | O_APPEND | O_EXCL
     使用前提是,参2 指定了O_CREAT.8进制数 ,用来描述文件的权限 rwx --0664  --rw -rw -r
关闭文件
#include<unistd.h>

int close(int fd);
	参:open返回值。
        成功返回0
        失败返回-1
	
文件创建权限
  • Linux系统中,创建文件的权限,受umask影响。
  • 使用open创建文件的最终权限,受mode(参3)喝umask共同影响
    • 计算的公式: 最终文件权限 = mode& - umask
文件读写
 ssize_t read(int fd, void *buf, size_t count);
参数:
	fd : 打开的文件描述符 --- open() 返回值
    buf: 存储读取的数据的 缓冲区
    count: 前面缓冲区的大小 size_t无符号整数 ssize_t 有符号
 返回值:
        成功:没读到文件末>0否则=0
        失败:error -1
写文件
      #include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);
参数:
	fd : 打开的文件描述符 --- open() 返回值
    buf: 存储写出的数据的 缓冲区地址
    count:数据的大小
错误处理
  • sterror函数

    #include<string.h>
    char* sterror(int errnum);
    	参数:错误号 --errno 当前程序的唯一全局变量
        返回值:错误号对应的错误描述
    
  • perror函数

    #include <stdio.h>
    void perror(const char *s);
    参数:用户自定义的错误信息:perror函数,会自动与error函数的错误描述拼接
    
文件拷贝联系
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>

int main(int argc,char *argv[])
{
    int n = 1;
    //创建用于储存的数据缓冲区
    char buf[4096];
    //打开源文件
    int fd1  = open(argv[1],O_RDONLY);
    if(fd1 == -1)
    {
        perror("open src err:");
        exit(1);//程序非正常结束
    }
    //打开目标文件
    int fd2 = open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0644);
    if(fd2 == -1)
    {
        perror("open dst err:");
        exit(1);//程序非正常结束
    }
    // 、、循环从文件中读取数据,写道目标文件中
    // while((n = read(fd1,buf,1024)) != 0)
    while((n = read(fd1,buf,1024)))
    {
        if(n< 0)
        {
            perror("read error");
            break;

        }
        //读取多少写多少,生成目标文件
        write(fd2,buf,n);
    } 
    //操作结束
    close(fd1);
    close(fd2);
    return 0;
}
预读入缓输出
  • red、write函数是无缓冲区IO函数
strace命令
  • 可以跟踪程序在运行中,使用系统调用。
文件描述符
  • pcb进程控制块:

    • 本质:struct结构体
    • 成员:文件描述符
    • 文件描述符:0/1/2–1023
      • 默认上限最大为:1024
    • 使用规则:使用表中没有被占用的最小的文件描述符
  • 3个特殊文件

    • stdin:标准输入:0 -STDIN_FILENO
    • stdout: 标准输出: 1 -STDOUT_FILENO
    • stderr:标准错误: 2- STDERR_FILENO##### 阻塞与非阻塞
  • 阻塞、非阻塞是设备文件、网络文件具备的属性。(不是read、write的属性)

  • 查收阻塞的场景:

    • 读取设备文件
    • 读取网络文件 读取常规文件无阻塞
  • /dev/tty — 终端文件

    • 设置/dev/tty文件为非阻塞状态 --重写打开指定O_NONBLOCK属性。

    • int fd = open("/dev/tty",O_RDWR|O_NONBLOCK);
      
fanctl函数
  • 作用:不重新打开文件修改文件权限。
  • 函数原型
#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );
# 获取文件权限
int flg = fcntl(fd,F_GETFL)
 #修改文件权限
flg  |= O_NONBLOCK //添加非阻塞权限
fcntl(fd,F_SETFL,flg)
lseek函数
SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       off_t lseek(int fd, off_t offset, int whence);
fd 文件描述符 
offset 偏移量
whence:起始偏移位. SEEK_SET \SEEK_CUR \SEEK_END
返回:
	成功:较起始位置便宜
     失败:-1
  • 相关知识:
    1. 文件的读写使用的同一偏移位置
    2. 可以使用lseek获取文件大小
    3. 可以使用lseek拓展文件的大小。真正的拓展需要引起IO操作
      • 直接拓展文件,也可以使用truncate()函数来完成
      • #include<stdio.h>
        #include<stdlib.h>
        #include<string.h>
        #include<unistd.h>
        #include<fcntl.h>
        #include<errno.h>

void sys_err(const char*str)
{
perror(str);
exit(1);
}

int main(int argc,char* argv[])
{
int fd = open(“lseek.txt”,O_RDWR|O_CREAT,0664);
if(fd == -1)
{
sys_err(“open err”);
}
//使用lseek拓展文件大小
int len = lseek(fd,10,SEEK_END);
printf(“len = %d\n”,len);

//必须引起IO操作,真正拓展文件
write(fd,"a",strlen("a"));

close(fd);

int ret = truncate("lseek.txt",250);
printf("ret = %d\n",ret);
return 0;

}

  • inode
    • 本质:struct结构体。存储文件的属性信息。
    • 大多数的inode在磁盘中,少量近期常用的保存在内存中
  • dentry:目录项
    • 本质:struct结构体{文件名、inode号…}。文件内容保存在盘块中。
  • 文件系统:
    • 一组规则,规定对文件的存储以及读取的方式、方法.在磁盘格式化的时候指定。fat32、ntfs、ext2/3/4。
stat函数
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#if 0
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 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
     *                   precision for the following timestamp fields.
     *                                     For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* time of last access */
    struct timespec st_mtim;  /* time of last modification */
    struct timespec st_ctim;  /* time of last status change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

#endif

//查看文件信息命令
//deng@itcast:~/share/4th$ stat txt

//获取文件相关信息
int main(void)
{
    int ret = -1;

    struct stat buf;

    memset(&buf, 0, sizeof(buf));
    //获取文件相关信息
    ret = stat("txt", &buf);
    if (-1 == ret)
    {
        perror("stat"); 
        return 1;
    }

    printf("st_dev: %lu\n", buf.st_dev);
    printf("st_ino: %lu\n", buf.st_ino);
    printf("st_nlink: %lu\n", buf.st_nlink);
    printf("st_uid: %d\n", buf.st_uid);
    printf("st_gid: %d\n", buf.st_gid);
    printf("st_rdev:%lu\n", buf.st_rdev);
    printf("st_size: %ld\n", buf.st_size);
    printf("st_blksize: %ld\n", buf.st_blksize);
    printf("st_blocks: %ld\n", buf.st_blocks);


    return 0;
}

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<sys/stat.h>
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

int main(int argc,char * argv[])
{
    struct stat sbuf;
    int ret = lstat(argv[1],&sbuf);
    printf("file size is %ld\n",sbuf.st_size);
    if(ret == -1)
    {
        sys_err("stat err");
    }
    if(S_ISREG(sbuf.st_mode)){
        printf("It's a regular file");
    }
    else if (S_ISDIR(sbuf.st_mode))
    {
        printf("it's a dir\n");
    }
    else if( S_ISFIFO(sbuf.st_mode))
    {
        printf("It's is a fifo");
    }
    else if(S_ISLNK(sbuf.st_mode))
    {
        printf("It's a software link");
    }
    
    return 0;
}

如何查看软连接本身的文件内容。readlink命令或者readlink()函数

link和unlink函数
int link(const char*oldpath,const char* newpath);
int unlink(const char*pathname)
  • unlink特性:
    • 调用unlink,只是让文件具备了被释放的条件,不一定立即释放。
    • 硬链接基数为0(没有dentry)的文件不好马上被释放。要等到所有打开该文件的进程运行结束无进程使用后释放该文件。
  • 隐式回收
    • 程序运行结束,打开的文件、申请的内存空间,会被系统释放。—隐式回收系统资源。
目录操作
操作函数
  1. 打开目录 opendir
  2. 关闭目录 closedir
  3. 读取目录项 readdir 没有提供写目录的函数。
opendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开一个目录
参数:
    name:目录名
返回值:
    成功:返回指向该目录结构体指针
    失败:NULL
closedir
#include <sys/types.h>
#include <dirent.h>int closedir(DIR *dirp);
功能:关闭目录
参数:
    dirp:opendir返回的指针
返回值:
    成功:0
    失败:-1
readdir
#include <dirent.h>!
struct dirent *readdir(DIR *dirp);
功能:读取目录
参数:
    dirp:opendir的返回值
返回值:
    成功:目录结构体指针
    失败:NULL
    
struct dirent
{
    ino_t d_ino;                  // 此目录进入点的inode
    off_t d_off;                    // 目录文件开头至此目录进入点的位移
    signed short int d_reclen;      // d_name 的长度, 不包含NULL 字符
    unsigned char d_type;           // d_type 所指的文件类型 
    char d_name[256];               // 文件名
};
d_name: 文件名。 要求长度不能超过255个有效字符。

d_type文件类型说明:

取值含义
DT_BLK块设备
DT_CHR字符设备
DT_DIR目录
DT_LNK软链接
DT_FIFO管道
DT_REG普通文件
DT_SOCK套接字
DT_UNKNOWN未知
实现简单的ls
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}
int main(int argc,char*argv[])
{
    DIR *dp;
    struct dirent *sdp ;
    int i = 0;
    dp = opendir(argv[1]);
    if(dp==NULL)
    {
        sys_err("opendir err");
    }
    while((sdp = readdir(dp))!=NULL)
    {
        i++;
        if(i%6==0)printf("\n");
        if(sdp->d_name[0]!='.')printf("%s\t",sdp->d_name);
    }
    printf("\n");
    closedir(dp);
    return 0;
}
dup和dup2
dup
  • 将文件描述符,复制产生新文件描述符并且返回。新旧文件符指向同一个文件
//函数原型:
int dup(int oldfd);
参:旧文件描述符
返回值:
    成功:新文件描述符
    失败:-1 errno
//应用:
save_fd = dup(STDOUT_FILENO)
write(save_fd,msg,strlen(msg));//数据会被写道屏幕
dup2函数
#include <unistd.h>int dup2(int oldfd, int newfd);
功能:
    通过 oldfd 复制出一个新的文件描述符 newfd,如果成功,newfd 和函数返回值是同一个返回值,最终 oldfd 和新的文件描述符 newfd 都指向同一个文件。
参数:
    oldfd : 需要复制的文件描述符
    newfd : 新的文件描述符,这个描述符可以人为指定一个合法数字(0 - 1023),如果指定的数字已经被占用(和某个文件有关联),此函数会自动关闭 close() 断开这个数字和某个文件的关联,再来使用这个合法数字。
返回值:
    成功:返回 newfd
    失败:返回 -1
使用案例
  • 使用dup2实现cat hello.c > out
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<dirent.h>
void sys_err(const char*str)
{
    perror(str);
    exit(1);
}

int  main(int argc,char* argv[])
{
    char buf[1024];
    int n;
    if(argc==4)
    {
        if(strcmp(argv[2],">")==0)
        {
            int fd2 = open(argv[3],O_WRONLY|O_CREAT|O_TRUNC,0664);
            if(fd2 == -1)
            {
                sys_err("open out err");
            }
            // dup2(STDERR_FILENO,fd2);
            dup2(fd2,STDERR_FILENO);
        }
    }
    int fd = open(argv[1],O_RDONLY);
    if(fd == -1)
    {
        sys_err("open argv[1] err");
    }
    while(( n = read(fd,buf,sizeof(buf))))
    {
        write(STDOUT_FILENO,buf,n);
    }
}
  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值