Linux系统编程-文件与IO

时间:2024-10-03 07:37:10

open

用于打开或创建文件,并返回一个文件描述符。需要包含头文件:#include <unistd.h>;其参数二的flags需要包含头文件:#include <fcntl.h>

函数原型:

int open(char *pathname, int flags)

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

参数:

  • pathname:欲打开的文件路径名
  • flags:文件打开方式,例如O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(读写)、O_CREAT(创建新文件)、O_APPEND(附加)、O_TRUNC(文件截断)、O_EXCL(文件不存在)、O_NONBLOCK(非阻塞)……
  • mode:参数3使用的前提,参2指定了O_CREAT。取值8进制数,用来描述新创建文件的访问权限。创建文件最终权限 = mode & ~umask。

返回值:

  • 成功:打开文件所得到对应的文件描述符(整数)
  • 失败:-1,并设置errno

close

用于释放程序中打开的文件描述符,头文件和open一样。

函数原型:

int close(int fd);

truncate/ftruncate

用于修改文件的大小,可以将文件截断为指定的长度,或者扩展文件到指定的长度。当截断文件时,超出指定长度的部分将被删除;当扩展文件时,文件内容可以保持不变,新增的部分将会被填充为零字节或未定义的内容。需要包含头文件<unistd.h>

函数原型:

int truncate(const char *path, off_t length);

int ftruncate(int fd, off_t length);

参数:

  • path:要操作的文件的路径名。
  • length:指定的文件大小,截断文件时表示新的文件大小,扩展文件时表示文件新的大小。
  • fd:要操作的文件描述符。

返回值:

  • 成功:0
  • 失败:-1,并设置error

read

用于从文件描述符中读取数据,需要包含头文件:#include <unistd.h>

函数原型:

ssize_t read(int fd, void *buf, size_t count);

参数:

  • fd:文件描述符
  • buf:存数据的缓冲区
  • count:缓冲区大小

返回值:

  • 0:读到文件末尾
  • 成功:> 0,读到的字节数
  • 失败:-1,设置errno
  • -1并且 errno = EAGIN 或 EWOULDBLOCK:说明不是read失败,而是read在以非阻塞方式读一个设备文件(网络文件),并且文件无数据。

write

用于向文件描述符中写入数据,需要包含头文件:#include <unistd.h>

函数原型:

ssize_t write(int fd, const void *buf, size_t count);

参数:

  • fd:文件描述符
  • buf:待写出数据的缓冲区
  • count:数据大小

返回值:

  • 成功:写入的字节数。
  • 失败:-1,设置 errno

例:运用上面四个系统调用实现linux的cp命令

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

using namespace std;

int main(int argc, char *argv[])
{
    char buf[1024];		//缓冲区
    int fd1 = open(argv[1], O_RDONLY);
    if (fd1 == -1)		//错误处理
    {
        perror("open argv1 error");
        exit(1);
    }
    int fd2 = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd2 == -1)		//错误处理
    {
        perror("open argv2 error");
        exit(1);
    }
    int n;
    while ((n = read(fd1, buf, 1024)) > 0)
    {
        if (n == -1)
            break;
        write(fd2, buf, n);
    }
    close(fd1);
    close(fd2);
    return 0;
}

fcntl

fcntl是UNIX系统提供的一个系统调用,用于对文件描述符进行各种操作。它可以用于执行各种文件描述符相关的控制操作,如修改文件状态标志、获取/设置文件描述符属性、锁定文件等。需要包含头文件:#include <fcntl.h>

函数原型:

int fcntl(int fd, int cmd, ... /* arg */);

参数:

  • fd:文件描述符,表示要进行操作的文件描述符。
  • cmd:操作命令,可以指定要执行的操作类型,掌握F_GETFL(获取文件状态)、F_SETFL(设置文件状态)。
  • arg:根据不同的命令类型,arg可能是一个整数值,也可能是一个结构体指针。

返回值的含义取决于执行的具体操作。一般情况下,fcntl调用的返回值可以有以下几种情况:

  • 成功执行:如果 fcntl调用成功执行,返回值通常是与操作相关的具体数值。例如,对于获取文件状态标志 (F_GETFL) 操作,返回的是文件状态标志的值。
  • 失败:如果 fcntl调用执行失败,返回值通常是 -1,并且会设置全局变量 errno来指示具体的错误原因。可以使用perror或其他错误处理函数来获取错误信息。

例:

int flags = fcntl(STDIN_FILENO, F_GETFL); 		//获取stdin属性信息  
flags |= O_NONBLOCK;  							//设置非阻塞
int ret = fcntl(STDIN_FILENO, F_SETFL, flags);  //更新stdin属性信息

lseek

用于在文件描述符上设置文件偏移量(file offset)。文件偏移量是用来指示当前读/写操作在文件中的位置的一个指针。需要包含头文件#include<unistd.h>

函数原型:

off_t lseek(int fd, off_t offset, int whence);

参数:

  • fd:文件描述符
  • offset:偏移量,就是将读写指针从whence指定位置向后偏移offset个单位
  • whence:起始偏移位置,包括SEEK_SET / SEEK_CUR / SEEK_END

返回值:

  • 成功:较起始位置偏移量
  • 失败:-1,并设置errno

应用场景:

  1. 文件的“读”、“写”使用同一偏移位置。
  2. 使用lseek获取文件大小(返回值接收),int length = lseek(filepath, 0, SEEK_END);
  3. 使用lseek拓展文件大小:要想使文件大小真正拓展,必须引起IO操作

例:写一个句子到空白文件,然后调整光标位置,读取刚才写那个文件。

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <cstring>

using namespace std;

int main()
{
    char msg[] = "It is a test for lseek.\n";
    int fd = open("test.txt", O_RDWR | O_CREAT, 0644);
    if (fd < 0)
    {
        perror("open file error");
        exit(1);
    }
    write(fd, msg, strlen(msg));

    // 修改文件读写指针位置,位于文件开头。
    lseek(fd, 0, SEEK_SET);

    char ch;
    int n;
    while ((n = read(fd, &ch, 1)) > 0)
    {
        if (n < 0)
        {
            perror("read error");
            exit(1);
        }
        write(STDOUT_FILENO, &ch, n);
    }

    close(fd);
    return 0;
}

stat/lstat

用于获取文件的状态信息,比如文件大小、创建时间、修改时间等。如果文件是一个符号链接,stat函数会进行符号穿透,即返回符号链接文件所指向的文件的信息;而lstat函数将返回符号链接文件本身的信息,而不会解引用符号链接。需要包含头文件#include <sys/stat.h>

函数原型:

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

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

参数:

  • path:文件路径
  • buf:(传出参数)存放文件属性,inode结构体指针。

返回值:

  • 成功: 0
  • 失败: -1,并设置errno
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
    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
};

获取文件大小: buf.st_size

获取文件类型: buf.st_mode

获取文件权限: buf.st_mode

例:

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cstdlib>

using namespace std;

int main()
{
    struct stat buf;
    int ret = stat("./test.txt", &buf);
    if (ret == -1)
    {
        perror("stat error");
        exit(1);
    }
    cout << "file size: " << buf.st_size << endl;
    return 0;
}

检查文件类型的宏函数:

  • S_ISDIR(mode):检查文件是否为目录文件。
  • S_ISREG(mode):检查文件是否为普通文件。
  • S_ISCHR(mode):检查文件是否为字符特殊文件(字符设备)。
  • S_ISBLK(mode):检查文件是否为块特殊文件(块设备)。
  • S_ISLNK(mode):检查文件是否为符号链接。
  • S_ISFIFO(mode):检查文件是否为FIFO(命名管道)。
  • S_ISSOCK(mode):检查文件是否为套接字。

link/unlink

link函数用于创建一个新的硬链接,将oldpath指定的文件链接到newpath指定的路径上。

unlink函数用于删除指定路径的文件。unlink清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放,要等到所有打开文件的进程关闭该文件,系统才会挑时间将该文件释放掉。

需要头文件#include<unistd.h>

函数原型:

int link(const char *oldpath, const char *newpath);

int unlink(const char *pathname);

返回值:

  • 0:成功
  • -1:失败

opendir/closedir

opendir函数用于打开一个目录并返回一个指向该目录的指针,以便后续对目录进行读取操作。

closedir函数用于关闭通过 opendir函数打开的目录流,并释放与该目录流相关的资源。

需要包含头文件#include<dirent.h>

函数原型:

DIR *opendir(constchar *name);

int closedir(DIR *dir);

返回值:

  • 0:成功关闭目录
  • -1:关闭目录失败

readdir

readdir函数用于读取目录流中的下一个目录条目,并返回一个指向dirent结构体的指针,该结构体包含有关读取的目录条目的信息。需要包含头文件#include<dirent.h>

函数原型:

struct dirent *readdir(DIR *dir);

参数dir是通过opendir函数打开的目录流指针。

函数返回一个指向dirent结构体的指针,该结构体包含有关下一个目录条目的信息。如果到达目录流的末尾或者发生错误,readdir将返回NULL

dirent结构体定义如下:

struct dirent {
    ino_t d_ino;         // inode number
    off_t d_off;         // offset to the next dirent
    unsigned short d_reclen; // length of this record
    unsigned char d_type;    // type of file
    char d_name[256];    // filename
};

例:递归遍历目录打印文件名和大小

#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <cstdlib>
#include <dirent.h>
#include <cstring>

using namespace std;

bool isdir(const char *name)
{
    struct stat buf;
    if (stat(name, &buf) == 0)
    {
        cout << name << ": " << buf.st_size << endl;
        if (S_ISDIR(buf.st_mode))
            return true;
        return false;
    }
    else
    {
        perror("get stat error");
        exit(1);
    }
}

void fetchdir(const char *name)
{
    if (!isdir(name))
        return;
    DIR *dir = opendir(name);
    if (dir == NULL)
    {
        perror("open dir error");
        exit(1);
    }
    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL)
    {
        char *filename = entry->d_name;
        if (strcmp(filename, ".") != 0 && strcmp(filename, "..") != 0)
        {
            char fullname[256];
            sprintf(fullname, "%s/%s", name, filename); // 拼接出完整路径
            fetchdir(fullname);
        }
    }
    closedir(dir);
}

int main(int argc, char *argv[])
{
    if (argc == 1)
        fetchdir(".");
    else
    {
        for (int i = 1; i <= argc; i++)
            fetchdir(argv[i]);
    }
    return 0;
}

dup/dup2

dup函数用于复制文件描述符,返回最小的尚未被使用的文件描述符。

dup2函数也用于复制文件描述符,但是可以指定新的文件描述符,将oldfd拷贝给newfd。使用dup2可以实现重定向的功能。

函数原型:int dup(int oldfd);

参数:oldfd:已有文件描述符

返回值:成功时返回新的文件描述符,新文件描述符和oldfd指向相同内容;失败时返回 -1。

函数原型:int dup2(int oldfd, int newfd);

参数:

  • oldfd:要复制的文件描述符。
  • newfd:指定的新文件描述符。

返回值:成功时返回新的文件描述符(即newfd),失败时返回 -1。

例:

int fd1 = open(file1, O_RDWR);
int fd2 = open(file2, O_RDWR);
int fdret = dup2(fd1, fd2);		//将fd1复制给fd2,最终fd2也指向file1

// 重定向
dup2(fd1, STDOUT_FILENO);		//将fd1复制给标准输出,最终标准输出也指向file1