刘某的Linux环境文件相关系统调用

#这是我对这一段时间关于linux系统调用文件部分的一些总结和分享,如有错误欢迎大家指正。

        首先了解一下什么是系统调用,系统调用是操作系统提供的接口,允许应用程序访问硬件资源和操作系统功能。它们是封装成 C 函数的形式,虽然被广泛使用,但并不是 C 标准库的一部分。应用程序通常在用户态运行,而当调用系统时,它们会转变到内核态,进行必要的操作并返回用户态。系统调用常用于文件管理、进程控制和网络通信等任务。

       

        在 UNIX/Linux 系统中,几乎所有的对象(包括设备和服务)都被抽象为文件。这种设计使得系统提供了一套统一的接口来进行文件的读写操作,这部分接口称为系统 I/O(输入/输出)。与此相对,标准 C 语言库提供的文件读写函数被称为标准 I/O。这样,UNIX/Linux 系统允许以一致的方式访问不同类型的对象,大大简化了编程和操作。

        

根据不同的功能和用途,这些文件可以分为以下几类:

  • 普通文件(-):包含二进制文件、文本文件、压缩文件、库文件等。

  • 目录文件(d):包含文件名及其对应的 inode(索引节点),必须具有执行权限才能访问其中的文件。

  • 块设备文件(b):表示块设备,能够以块为单位进行数据存取,例如硬盘驱动器。

  • 字符设备文件(c):表示字符设备,通常用于按字符进行数据操作,如键盘和鼠标。

  • 管道文件(p):用于实现进程间通信,允许一个进程将输出直接传递给另一个进程的输入。

  • Socket 文件(s):用于网络数据连接,允许不同主机上的进程动态有效地进行通信。

  • 链接文件(l):类似于 Windows 中的快捷方式,提供对其他文件的引用,允许文件系统中的链接,帮助用户实现更灵活的文件管理。

       

        在了解这些以后我们正式来看文件相关的系统调用,我将这些内容分为了八个部分,分别是文件创建,文件内容操作,文件描述符的操作,文件属性,文件权限,文件本身操作,链接文件,目录操作。

1. 文件的创建或打开

  • open():打开一个文件或创建一个新文件。
  • creat():创建一个新文件并返回文件描述符。

open() 的原型是:

int open(const char* pathname, int flags);
  • pathname:如名所示,是文件的打开路径,需要特别注意绝对路径和相对路径的区别。

  • flags:指示对文件的打开方式,其有如下几种(根据需要选用):

    1. O_RDONLY:只读。

    2. O_WRONLY:只写。

    3. O_RDWR:读写。

    4. O_CREAT:如果文件不存在则创建,使用时需额外加入 mode 参数。

    5. O_APPEND:追加内容到文件末尾。

    6. O_EXCL:文件已存在则创建失败。

    7. O_TRUNC:如果文件存在则清空文件内容。

针对 O_CREAT 的使用,原型应改为:

int open(const char* pathname, int flags, mode_t mode);
  • mode:权限,通常默认为文件权限 0644,具体如下:

    • S_IRWXU 00700 拥有者 读、写、执行权限。

    • S_IRUSR 00400 读权限。

    • S_IWUSR 00200 写权限。

    • S_IXUSR 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 执行权限。

除了使用 open() 之外,creat() 也可以用来创建文件,其原型为:

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

        这里的 mode 与 open() 的 mode 相同,但 creat() 给不了 777 的权限,只能最多给到 775,但可以后续使用 chmod() 修改权限。这两个函数的返回值都是文件描述符。

2. 文件内容操作

  • read():从文件中读取数据。

  • write():向文件中写入数据。

  • lseek():移动文件指针以实现随机访问。

  • close():关闭已打开的文件。

为了调用这些系统调用,我们必须使用上面提到的返回值——文件描述符。文件描述符有以下特点:

  1. 非负整数,代表打开的文件。

  2. 通过系统调用 open() 或 creat() 返回,可以被内核引用。

  3. 表示一个内核对象,内核无法直接暴露其内存地址,仅通过文件描述符进行访问。

  4. 内核中有一张打开文件的表,文件描述符是访问这张表的下标,因此也称为 "句柄",相当于访问已打开文件的凭证。

  5. 内核中有三个默认打开的文件描述符,分别是 012,代表标准输入、标准输出和标准错误。

文件内容操作的相关原型如下:

ssize_t write(int fd, const void *buf, size_t count);
  • 功能:把内存中的数据写入到文件中。
  • fd:文件描述符(open 的返回值)。
  • buf:待写入数据的内存首地址。
  • count:要写入的字节数。
  • 返回值:成功写入的字节数。
ssize_t read(int fd, void *buf, size_t count);
  • 功能:从文件中读取数据到内存。
  • fd:文件描述符。
  • buf:存储数据的内存首地址。
  • count:要读取的字节数。
  • 返回值:实际读取到的字节数。
int close(int fd);
  • 功能:关闭一个文件。
  • 返回值:成功返回 0,失败返回 -1

要重点介绍一下 lseek(),用于进行随机读写。每个文件都有一个记录读写位置的指针——文件位置指针,所有对文件的读写操作都是从该指针开始进行的。而当我们需要调整读写位置时,lseek() 就可以派上用场。

其原型为:

off_t lseek(int fd, off_t offset, int whence);
  • fd:文件描述符。
  • offset:偏移值。
  • whence:基础位置,可能的值有:
    • SEEK_SET(开始位置)。
    • SEEK_CUR(当前位置)。
    • SEEK_END(结尾位置)。

3. 文件描述符的操作

  • dup():复制文件描述符。
  • dup2():将一个文件描述符复制到另一个文件描述符。
  • fcntl():执行多种文件描述符操作,如修改属性。

文件描述符的复制原型如下:

int dup(int oldfd);
  • 功能:复制一个已打开的文件描述符。
  • 返回值:返回一个当前没有使用过的最小的文件描述符。
int dup2(int oldfd, int newfd);
  • 注意:只改变 newfd 的指向,但不会改变 newfd 的值。
  • 功能:复制成一个指定的文件描述符,使得 newfd 指向 oldfd 指向的文件。
  • 返回值:成功返回 newfd,失败返回 -1

注意:复制成功后,相当于两个不同值的文件描述符对应同一个已打开的文件,它们的打开方式相同。关闭其中一个文件描述符不会影响另一个,但它们共享同一个文件位置指针。

4. 文件属性

文件属性的管理主要通过以下系统调用实现:

  • stat():获取文件状态信息。
  • fstat():根据文件描述符获取状态信息。
  • lstat():获取符号链接的状态信息。
  • chmod():更改文件权限。
  • chown():更改文件的拥有者和群组。
函数原型
int stat(const char *pathname, struct stat *buf);
  • 功能:根据文件的路径获取文件属性。
  • buf:存储文件属性的结构体,是一个输出型参数。
int fstat(int fd, struct stat *buf);
  • 功能:根据文件描述符获取文件属性。
int lstat(const char *pathname, struct stat *buf);
  • 功能:根据文件的路径获取软链接文件属性。
struct stat 结构体

该结构体包含了很多文件的状态信息,定义如下:

struct stat {
    dev_t     st_dev;     // 设备ID
    ino_t     st_ino;     // inode节点号
    mode_t    st_mode;    // 文件类型和权限
    nlink_t   st_nlink;   // 硬链接数
    uid_t     st_uid;     // 用户ID
    gid_t     st_gid;     // 组ID
    dev_t     st_rdev;    // 特殊设备ID号
    off_t     st_size;    // 总字节数
    blksize_t st_blksize;  // IO块字节数
    blkcnt_t  st_blocks;   // 占用512B块数量
    struct timespec st_atime; // 最后访问时间
    struct timespec st_mtime; // 最后内容修改时间
    struct timespec st_ctime; // 最后状态修改时间
};
st_mode 文件类型
  • 文件类型
    • S_IFMT 0170000 // 获取文件类型的掩码
    • S_IFSOCK 0140000 // socket文件
    • S_IFLNK 0120000 // 软链接文件
    • S_IFREG 0100000 // 普通文件
    • S_IFBLK 0060000 // 块设备文件
    • S_IFDIR 0040000 // 目录文件
    • S_IFCHR 0020000 // 字符设备文件
    • S_IFIFO 0010000 // 管道文件

POSIX 定义了一组函数用于判断文件类型:

  • S_ISREG(st_mode) // 是普通文件
  • S_ISDIR(m) // 是目录文件
  • S_ISCHR(m) // 是字符设备文件
  • S_ISBLK(m) // 是块设备文件
  • S_ISFIFO(m) // 是管道文件
  • S_ISLNK(m) // 是软链接文件
  • S_ISSOCK(m) // 是 socket 文件
st_mode 权限信息
  • 权限掩码
    • S_IRWXU 00700 // 用户权限的掩码
    • S_IRUSR 00400 // 读权限
    • S_IWUSR 00200 // 写权限
    • S_IXUSR 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 // 其他用户执行权限

5. 文件权限

        权限的描述:文件权限通常表示为三组,每组包含拥有者、组用户和其他用户的读、写、执行权限。这样的权限管理机制使得用户可以灵活地控制文件的访问权限,确保系统的安全性和资源的合理使用。

access()

int access(const char *pathname, int mode);
  • 功能:测试当前用户对指定文件的权限。

  • pathname:要测试权限的文件路径。

  • mode:想要测试的权限,可以是以下之一:

    • F_OK:测试文件是否存在。
    • R_OK:测试是否具有读权限。
    • W_OK:测试是否具有写权限。
    • X_OK:测试是否具有执行权限。
    • 返回值:如果权限测试通过,则返回 0;如果权限不够或文件不存在,则返回 -1,并设置 errno

 chmod()

int chmod(const char *pathname, mode_t mode);
  • 功能:根据指定路径修改文件权限。

  • mode:由三位八进制数构成的权限码,其中:

    • 0644:表示普通文件,权限为用户读写,组用户和其他用户只读。
    • 0755:表示目录文件或可执行文件,权限为用户读、写、执行,组用户和其他用户读、执行。
  • 返回值:成功时返回 0;失败时返回 -1

    fchmod()

int fchmod(int fd, mode_t mode);
  • 功能:根据文件描述符修改文件权限。此函数常用于当文件已经通过 open() 打开时修改其权限。

  • fd:打开文件的文件描述符。

  • mode:与 chmod() 相同,由三位八进制数构成的权限码。

  • 返回值:成功时返回 0;失败时返回 -1

6. 文件本身操作

  • unlink():删除一个文件或链接。
  • rename():重命名一个文件。
函数原型和功能
int remove(const char *pathname);
  • 功能:这是标准库中的函数,用于删除文件或空目录。它实际上调用了底层的 unlink() 函数。
int unlink(const char *pathname);

功能:用于删除指定路径的文件。该函数减少文件的链接计数,如果链接计数为零且无任何进程打开该文件,文件的存储空间将被释放。

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

功能:重命名、移动文件

7. 链接文件

在 Linux 文件系统中,文件存储结构主要由两个分区组成:

  • inode 信息块:默认大小为 128 字节,记录文件的权限、大小、所有者、修改时间等信息。

  • 数据块(block):默认大小为 4KB,存储文件的数据和文件名信息。

每个文件必须拥有一个唯一的 inode,以及一个或多个数据块。读写文件时,通过目录中的文件名和 inode 号查找该文件的 inode,并使用 inode 来读取数据块。

什么是软链接和硬链接?
  • 硬链接:硬链接文件并没有自己的 inode 和数据块,它指向的是原文件的 inode。多条路径可以通过相同的 inode 访问同一文件的数据块。例如,创建一个硬链接可以让不同目录下的文件共享相同的数据内容。

  • 软链接(符号链接):软链接具有自己的 inode 和数据块,软链接的数据块存储的是指向源文件的路径信息。软链接的作用与快捷方式类似,可以指向其他文件或目录,无论是同一文件系统还是不同文件系统。

硬链接与软链接的区别:
  1. 删除源文件:删除源文件时,硬链接依然有效,因为它们共享相同的 inode。而软链接失效,无法访问。

  2. 计数与删除:对于一个文件而言,只有当其硬链接数减少为零时,文件才被真正删除。

  3. 内容修改:对通过硬链接访问的文件内容进行修改时,源文件的内容也会相应被修改,因为它们实际上是同一个文件。

  4. 链接限制

    • 硬链接不能链接到目录(除非使用超级用户)。
    • 软链接可以链接到目录。
  5. 跨文件系统

    • 硬链接不能跨越文件系统。
    • 软链接可以指向不同文件系统的文件。
相关的系统调用
int link(const char *oldpath, const char *newpath);
  • 功能:创建硬链接文件。
int symlink(const char *target, const char *linkpath);
  • 功能:创建软链接文件,linkpath 为软链接的路径,而 target 是指向的源文件。
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
  • 功能:读取软链接文件的指向路径。读取硬链接会返回随机数,但不会报错。
  • 返回值:返回链接路径的长度。

8. 目录操作

1. 创建目录
int mkdir(const char *pathname, mode_t mode);
  • 功能:创建一个新目录。
  • pathname:要创建的目录的路径。
  • mode:权限设置,必须有执行权限才能访问该目录。权限通常使用八进制表示法进行设置,例如 0755
2. 删除目录
int rmdir(const char *pathname);
  • 功能:删除指定的空目录。
3. 获取当前工作目录
char *getcwd(char *buf, size_t size);
  • 功能:获取当前工作目录,相当于 shell 命令 pwd
  • buf:存储结果的内存地址。
  • sizebuf 的大小。
  • 返回值:返回 buf 的地址,方便进行链式调用。如果发生错误,返回 NULL
4. 修改工作目录
int chdir(const char *path);
  • 功能:根据路径字符串修改当前工作路径,相当于 shell 命令 cd
int fchdir(int fd);
  • 功能:根据文件描述符修改当前工作路径。
5. 打开目录文件
DIR *opendir(const char *name);
  • 功能:打开指定的目录文件,返回一个指向目录流结构体的指针。
DIR *fdopendir(int fd);
  • 功能:根据文件描述符打开目录文件,返回一个指向目录流结构体的指针。
6. 读取目录信息
struct dirent *readdir(DIR *dirp);
  • 功能:从目录流中读取一条记录信息,记录目录中一个文件的信息。
struct dirent {
    ino_t   d_ino;       // inode号
    off_t   d_off;       // 下一条信息的偏移量
    unsigned short d_reclen; // 当前信息的长度
    unsigned char  d_type; // 文件的类型
    char           d_name[256]; // 文件名
};

总结:

        在 Linux 环境中,文件管理是系统编程的核心部分,涉及到文件的创建、访问、修改以及目录操作等多个方面。通过理解和运用这些系统调用,开发者可以高效地进行文件和目录的管理,使得在 Linux 环境中进行系统编程更加灵活和高效。了解这些系统调用的功能和用法为开发者提供了操作文件系统的强大工具,同时也使得管理文件和目录的权限、属性的过程变得系统化和规范化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值