Linux系统编程 | 【02】文件I/O操作常用函数介绍

一、文件IO

1.1 open、close
#include<fcntl.h>

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

/* 
open
---------
* @param flags: O_RDONLY, O_WRONLY,O_RDWR【O_APPEND、O_CREAT、O_EXCL、O_TRUNC、O_NONBLOCK】
* @param mode: 指定文件的权限【0xxx】,xxx按照二进制【421 - rwx】
* return: 失败返回-1
*/

【注】:

创建文件时,权限受~umask限制,mode&~mask

常见错误

  • 打开文件不存在;
  • 权限错误;
  • 用只写方式打开目录。
1.2 read、write
ssize_t read(int fd, void *buf, size_t count);

/*
read:采用预读入缓输出机制,先将读取到的数据缓存在内核,达到一定的数据量在往磁盘写入
-------
* @param fd: 文件描述符;
* @param buf:数据缓冲区;
* @param count:缓冲区大小。
* return:成功返回读取到的字节数,失败返回-1,文件末尾返回0 
*/
ssize_t write(int fd, const void *buf, size_t count);

/*  
write
-------
* @param fd: 文件描述符;
* @param buf:数据缓冲区;
* @param count:实际内容的大小。
* return:成功返回1,失败返回-1
*/
1.3 lseek
off_t lseek(int fd, off_t offset, int whence);
> - 获取文件大小;
> - 移动文件指针;
> - 文件拓展;
/* 文件拓展
* @param fd: 文件;
* @param offset: 文件字节数;
* @param whence: 
* 	- SEEK_SET: 设置偏移字节;
* 	- SEEK_CUR: 文件偏移量被设置为当前位置加上偏移量字节;
* 	- SEEK_END: 文件偏移量被设置为文件的大小加上偏移量字节;
* 	- SEEK_DATA: 将文件的偏移量调整到文件中大于或等于包含偏移量的下一个位置数据;
* 	- SEEK_HOLE: 
* 使用文件拓展,则需要在最后做一次写操作。
*/
1.4 stat
#include < sys/types.h >
#include < sys/stat.h >
#include < unistd.h >

int stat(const char * path , struct stat * buf );
int fstat(int fd , struct stat * buf );
int lstat(const char * path , struct stat * buf );

/**
【stat和lstat的区别】:当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;
						而stat返回的是该链接指向的文件的信息;
*/
struct stat {
               dev_t     st_dev;         /* ID of device containing file */
               ino_t     st_ino;         /* Inode number */
               mode_t    st_mode;        /* File type and mode */
               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;     /* Block size 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
           };

/*
st_mode字段定义了以下标志:  
 ---------------------------------
S_IFMT 0170000:文件类型位字段的位掩码  
S_IFSOCK 0140000:套接字   
S_IFLNK 0120000:符号链接  
S_IFREG 0100000:常规文件  
S_IFBLK 0060000:块设备  
S_IFDIR 0040000:目录  
S_IFCHR 0020000:字符设备  
S_IFIFO 0010000:先进先出  
S_ISUID 0004000 set-user-ID bit  
S_ISGID 0002000 set-group-ID位(见下文)  
S_ISVTX 0001000:粘性位(见下面)  

文件所有者权限的掩码  
---------------------------------
S_IRUSR 00400 owner:具有读权限  
S_IWUSR 00200 owner:具有写权限  
S_IXUSR 00100 owner:具有执行权限  

组权限的掩码  
---------------------------------
S_IRGRP 00040:组具有读权限  
S_IWGRP 00020:组具有写权限  
S_IXGRP 00010:组具有执行权限  
S_IRWXO 0000:7对其他权限的掩码(不在组中)  
S_IROTH 00004: others具有读权限  
S_IWOTH 00002: others有写权限  
S_IXOTH 00001:其他人有执行权限  
*/
1.5 stat案例实现ls功能
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<time.h>
#include<pwd.h>
#include<grp.h>
#include<iostream>

int main(int argc, char* argv[]){
    if(argc < 2){ // 判断是否有传入参数
        std::cout << "./a.out filename" << std::endl;
        exit(1);
    }

    struct stat st; // 构建stat结构体
    int ret = stat(argv[1], &st); 

    if(ret == -1){ // 文件打开是否失败
        std::cout << "文件打开失败" << std::endl;
        exit(1);
    }

    char perms[11] = {0}; // 构建字符数组
    // 判断文件类型
    switch(st.st_mode & S_IFMT){ // 将文件的模式与上掩码即可得到文件类型
        case S_IFLNK: 
            perms[0] = '1'; // 符号链接
            break;
        
        case S_IFDIR: // 文件夹
            perms[0] = 'd';
            break;
        case S_IFREG: // 文件
            perms[0] = '-';
            break;
        case S_IFBLK: // 块设备
            perms[0] = 'b';
            break;
        case S_IFCHR: // 字符设备
            perms[0] = 'c';
            break;
        case S_IFSOCK: // 套字接
            perms[0] = 's';
            break; 
        case S_IFIFO: // 先进先出
            perms[0] = 'p';
            break;
        default: // 未知
            perms[0] = '?';
    }
    // 判断文件的访问权限
    // 文件所有者
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
    perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
    perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';

    // 文件所属组
    perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';
    // 其他用户    
    perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
    perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
    perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';

    // 硬链接计数
    int linkNum = st.st_nlink;
    // 文件所有者
    char* fileUser = getpwuid(st.st_uid)->pw_name;
    //文件所属组
    char *fileGrp = getgrgid(st.st_gid)->gr_name;
    //文件大小
    int fileSize = (int)st.st_size;
    // 修改时间
    char* time = ctime(&st.st_mtime);
    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time)-1);
    char buf[1024];
    sprintf(buf, "%s\t%d\t%s\t%s\t%d\t%s\t%s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);

    std::cout << buf << std::endl;

    return 0;
}

1.6 access函数
  • 作用:测试指定文件是否是访问文件路径名;
int access(const char *pathname, int mode);

/*
* @param mode:  R_OK、W_OK、X_OK、F_OK【读、写和执行权限,文件是否存在】;
* return: 成功返回0,失败返回-1;
*/
1.7 chmod和fchmod
  • chmod:更改路径名在path中指定的文件的权限,如果是符号链接,则取消引用;
  • fchmod:更改打开的文件描述符fd所引用的文件的权限;
/*
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);

S_ISUID (04000):set user-ID (set process effective user ID on execve(2))  ;
S_ISGID (02000):set-group-ID (set process effective group ID on execve(2)); 强制锁定,如fcntl(2)所述; 拿一个新的文件的组从父目录,如chown(2)和mkdir(2)所述  ;
S_ISVTX(01000):粘性位(限制删除标志,如unlink(2)所述)  ;
S_IRUSR(00400)::由所有者读取  ;
S_IWUSR(00200):按所有者写  ;
S_IXUSR(00100):按所有者执行/搜索(“搜索”适用于目录,意味着可以访问目录中的条目)  ;
S_IRGRP(00040):按组读取  ;
S_IWGRP(00020):按组写;
S_IXGRP(00010):按组执行/搜索;  
S_IROTH(00004):被别人读取;  
S_IWOTH(00002):由别人写;
S_IXOTH(00001):由其他人执行/搜索;

* return: 成功返回0,失败返回-1;
*/
1.8 truncate函数
  • 将文件大小修改为指定大小,若原文件比设定的大,则超过的部分会被删除,若小则拓展,扩展部分读取null字节(‘\0’)且文件偏移量没有改变;
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

/*
* @param path: 文件路径
* @param length: 指定文件大小
* return: 成功返回0,失败返回-1
*/
1.9 link、symlink、readlink、unlink
  • link:创建一个硬链接;
  • symlink:创建一个软链接;
  • readlink:读软连接对应的文件名;
  • unlink:删除一个文件的目录项并减少它的链接数,且必须有该权限;
int link(const char *oldpath, const char *newpath);
int symlink(const char *oldpath, const char *newpath);
ssize_t readlink(const char *path, char *buf, size_t bufsiz);
int unlink(const char *pathname);

二、目录操作函数

2.1 chdir、fchdir
  • chdir:修改当前进程的路径;
  • fchdir:作用相同,参数可为打开的文件描述符。
int chdir(const char *path);
int fchdir(int fd);
2.2 getcwd、getwd、get_current_dir_name
  • getcwd:获取当前进程工作目录;
  • getwd:不建议使用;
  • get_current_dir_name
char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
char *get_current_dir_name(void);

/*
* @param buf:用来保存返回的路径;
* @param size:buf的大小;
* return: 以buf返回一个字符串
*/
2.3 mkdir
  • 创建目录;
int mkdir(const char *pathname, mode_t mode);

/*
* @param mode: 指定文件权限,mode & ~umask & 0777;
* return:成功返回0,失败返回-1;
*/
S_IRWXU	00700:代表该文件所有者拥有读,写和执行操作的权限
S_IRUSR(S_IREAD)00400:代表该文件所有者拥有可读的权限
S_IWUSR(S_IWRITE)00200:代表该文件所有者拥有可写的权限
S_IXUSR(S_IEXEC)00100:代表该文件所有者拥有执行的权限

S_IRWXG	00070:代表该文件用户组拥有读,写和执行操作的权限
S_IRGRP	00040:代表该文件用户组拥有可读的权限
S_IWGRP	00020:代表该文件用户组拥有可写的权限
S_IXGRP	00010:代表该文件用户组拥有执行的权限

S_IRWXO	00007:代表其他用户拥有读,写和执行操作的权限
S_IROTH	00004:代表其他用户拥有可读的权限
S_IWOTH	00002:代表其他用户拥有可写的权限
S_IXOTH	00001:代表其他用户拥有执行的权限
2.4 rmdir
  • 删除一个【空】目录;
int rmdir(const char *pathname);
/*
* return:成功返回0,失败返回-1;
*/
2.5 opendir、fdopendir
#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);
/*
* @func: 打开对应的目录流目录名;
* @param name: 文件路径;
*/
DIR *fdopendir(int fd);
/*
* @func: 和上述函数类似,但返回一个打开文件引用的目录的目录流描述符fd;
* return: 错误返回NULL;
*/
2.5 readdir
  • 读目录;
struct dirent *readdir(DIR *dirp);

/*
struct dirent {
         ino_t          d_ino;      //inode number
         off_t          d_off;      // not an offset; see NOTES
         unsigned short d_reclen;   //length of this record
         unsigned char  d_type;     //type of file; not supported
                                    // by all file system types
         char           d_name[256]; //filename
     };
		DT_BLK 是一个块设备。  
		DT_CHR 字符设备。  
		DT_DIR 是一个目录。  
		DT_FIFO 这是一个命名管道(FIFO)。  
		DT_LNK 符号链接。  
		DT_REG 这是一个普通文件。  
		DT_SOCK UNIX域套接字。  
		DT_UNKNOWN 文件类型未知。  
*/
2.5.1 读取文件夹案例
#include<iostream>
#include<cstring>
#include<sstream>
#include<sys/types.h>
#include<dirent.h>
#include<unistd.h>
#include<stdlib.h>

void getFileNum(const char* root, int &total){
    DIR* dir = opendir(root); // 打开文件
    if(!dir){
        std::cout << "error open file..." << std::endl; 
        exit(1);
    }

    // 遍历该目录
    struct dirent *ptr = NULL;
    std::ostringstream path;
    /*
     *readdir()返回一个struct dirent *指针
     struct dirent {
             ino_t          d_ino; // inode编号      
             off_t          d_off;  
             unsigned short d_reclen; // 记录的长度   
             unsigned char  d_type;   // 文件类型
             char           d_name[256];  // 文件名
         };
     */
    while((ptr=readdir(dir)) != NULL){
        // 过滤文件.和..
        if(strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
            continue;
        // 若该文件为目录, 则递归
        if(ptr->d_type == DT_DIR){
            path << root << "/" << ptr->d_name; // 拼接文件路径
            getFileNum(path.str().c_str(),total);// 递归目录
        }else{
            total++;
        
        }
    }

    closedir(dir);
}


int main(int argc, char* argv[]){
    if(argc < 2){
        std::cout << "error: Missing a parameter" << std::endl;
        exit(1);
    }

    int total = 0;
    getFileNum(argv[1], total);
    std::cout << "文件总数为:" << total << std::endl; 
    return 0;
}

2.6 dup、dup2函数
  • dup:使用编号最低的未使用的描述符作为新的描述符;
  • dup2: 若new是被打开的,则会在拷贝前关掉,把old拷贝到new;
int dup(int oldfd);
int dup2(int oldfd, int newfd);
2.7 fcntl
  • 复制一个现有的描述符;
  • 获得/设置记录锁;
  • 获取/设置文件描述符;
  • 获取/设置文件状态标记;
  • 获取/设置异步I/O所有权;
int fcntl(int fd, int cmd, ... /* arg */ );

/*
* @fun1: 复制一个现有的描述符 - F_DUPFD;
* @fun2: 获取/设置文件描述符- F_GETFD\F_SETFD;
* @fun3: 获取/设置文件状态标记
* 	- F_GETFL:
* 		O_RDONLY:只读打开
* 		O_WRONLY:只写打开
* 		O_RDWD:读写打开
* 		O_EXEC:执行打开
* 		O_SEARCH:搜索打开目录
* 		O_APPEND:追加写
* 		O_NONBLOCK:非阻塞模式
* 	- F_SETFL:更改标识 - O_AOOEND\O_NONBLOCK
* @fun4:获得/设置异步I/O - F——GETOWN\O_NONBLOCK
* @fun5:获得/设置记录锁 - F_GETLK\F_SETLK\F_SETKW
*/
2.7.1 fcntl案例
#include<cstring>
#include<iostream>
#include<stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char* argv[]){

    // 先用只写打开文件
    int fd = open("test.txt", O_WRONLY);

    if(fd == -1){
        std::cout << "open error..." << std::endl;
        exit(1);
    }

    // 写入内容,此时的文件指针在开头,且覆盖原来开头的内容
    if(write(fd, "test one", 8) == -1){
        std::cout << "write error..." << std::endl;
        exit(1);
    }

    // 获取文件状态标志,并修改该文件状态标志
    int flag = fcntl(fd, F_GETFL, 0);

    if(flag == -1){
        std::cout << "error..." << std::endl;
        exit(1);
    }
    // 修改文件权限为追加模式,文件指针指向末尾
    flag |= O_APPEND;

    if(fcntl(fd, F_SETFL, flag) == -1){
        std::cout << "set error..." << std::endl;
        exit(1);
    }
    // 
    if(write(fd, "test two", 8) == -1){
        std::cout << "write error..." << std::endl;
        exit(1);
    }
    close(fd);
    return 0;
}

三、其他函数

3.1 realpath
#include <limits.h>
#include <stdlib.h>

char *realpath(const char *path, char *resolved_path);
/*
@func: 
	1)打印指定文件的绝对路径;
	2)显示软链接指向的目标文件的绝对路径;
	3)打印某个文件相对于另外一个目录的路径;
	4)打印某个文件相对于基目录的路径,如果文件在基目录下,则会省去基目录;
return: 成功返回获取的路径,失败返回NULL;
*/

四、文件描述符

4.1 PCB进程控制块
  • 本质:结构体;
  • 成员:文件描述符表【指针】;
  • 文件描述符:0-1023;
    • 由于一个进程打开1024个地址;
    • 遵循表中最小,可用的文件描述符;
    • 0 - STDIN_FILENO、1 - STDOUT_FILENO、2 - STDERR_FILENO
4.2 最大打开文件数
  • 一个进程默认打开文件的个数1024;
  • 可用ulimit -a查看打开文件的对应值;
  • 可使用ulimit -n 4096 修改;
  • cat /proc/sys/fs/file-max可查看该电脑最大可打开的文件个数。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jxiepc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值