Unix/Linux系统编程学习笔记第七、八章

Unix/Linux系统编程学习笔记第七、八章

知识点归纳以及最有收获的内容

文件操作级别

文件和目录的基本操作

  • 创建文件:使用 touch 命令或编程语言中的文件创建函数。-
  • 创建目录:使用 mkdir 命令或编程语言中的目录创建函数。
  • 复制文件或目录:使用 cp 命令或编程语言中的复制函数。
  • 移动或重命名文件或目录:使用 mv 命令或编程语言中的移动/重命名函数。
  • 删除文件或目录:使用 rm 命令或编程语言中的删除函数。
  • 列出目录内容:使用 ls 命令或编程语言中的目录列表函数。
  • 文件权限和所有权
  • 文件权限:文件可以有读取、写入和执行权限,可以通过 chmod 命令来更改。
  • 文件所有权:文件属于一个用户和一个用户组,可以使用 chown 命令来更改所有权。

文件类型

  • 普通文件:包含文本、二进制或其他数据的常规文件。
  • 目录文件:用于存储其他文件和目录的特殊文件类型。
  • 符号链接:指向其他文件或目录的快捷方式。
  • 设备文件:表示系统硬件设备,如磁盘和串口。
  • 命名管道:用于进程间通信的特殊文件类型。
  • 套接字:用于网络通信的特殊文件类型。

文件扩展名

  • 文件扩展名通常用于指示文件的类型或格式。
  • 例如,.txt 表示文本文件,.jpg 表示JPEG图像,.cpp 表示C++源代码文件等。

文件路径

  • 绝对路径:从根目录开始的完整文件路径,如 /home/user/file.txt。
  • 相对路径:相对于当前工作目录的文件路径,如 ./file.txt 或 ../folder/file.txt。

文件操作操作系统级别

  • 文件操作需要在操作系统级别进行,如UNIX/Linux和Windows。
  • 不同操作系统可能具有不同的文件操作命令和API。

编程中的文件操作

  • 多种编程语言(如Python、C++、Java)提供文件操作的API。
  • 通过编程,可以自动化文件操作,如读取、写入和处理文件内容。

第八章系统调用

什么是系统调用

系统调用(System Call)是操作系统提供给应用程序的编程接口,用于访问底层操作系统功能。

系统调用的作用

  • 允许应用程序执行特权操作,如文件操作、进程管理、网络通信等,而不会破坏操作系统的稳定性和安全性。
  • 提供了用户空间和内核空间之间的界面,允许用户程序请求操作系统内核执行某些任务。

常见的系统调用

  • 文件系统调用:用于文件和目录的创建、读取、写入、删除等操作,例如 open(), read(), write(), close(), unlink() 等。
  • 进程管理调用:用于创建、终止、等待进程,以及进程间通信,例如 fork(), exec(), wait(), pipe() 等。
  • 内存管理调用:用于分配和释放内存,例如 malloc(), free(), mmap() 等。
  • 网络通信调用:用于建立和管理网络连接,例如 socket(), bind(), connect(), send(), recv() 等。
  • 时间管理调用:用于获取当前时间、定时器设置等,例如 time(), sleep(), gettimeofday() 等。

系统调用的执行过程

  • 应用程序通过库函数或系统调用接口发出系统调用请求。
  • 操作系统内核接收请求,并根据请求的类型执行相应的内核函数。
  • 内核执行完成后,将结果返回给应用程序。

系统调用的权限

  • 系统调用通常需要特权级别较高的权限,因此只能由操作系统内核执行。
  • 用户程序通过软中断或陷阱进入内核模式,以便执行系统调用。

系统调用与库函数的关系

  • 库函数通常是对系统调用的封装,提供更高级别的接口,使应用程序更容易使用。
  • 库函数内部通常会调用适当的系统调用来完成请求的任务。

跨平台问题

  • 不同操作系统具有不同的系统调用接口和调用号码,因此需要在不同平台上编写不同的系统调用代码。
  • 一些跨平台库(如POSIX标准)提供了一致的系统调用接口,以便编写可移植的代码。

苏格拉底挑战





遇到问题及解决方式

1. 文件不存在或路径错误:

  • 问题: 尝试打开、读取或写入一个不存在的文件或使用错误的文件路径。
  • 解决方式: 在进行文件操作之前,检查文件是否存在,确保提供的文件路径是正确的。

2. 文件权限问题:

  • 问题: 尝试执行没有权限的文件操作,如读取或写入受保护的文件。
  • 解决方式: 检查文件的权限,确保当前用户具有执行所需操作的权限。可以使用 chmod 命令更改文件权限,或者使用 sudo 来提升权限。

3. 文件句柄泄漏:

  • 问题: 打开文件后,忘记在不再需要时关闭文件,导致文件句柄泄漏。
  • 解决方式: 始终在不再需要文件时使用 close() 或相应的文件关闭函数来关闭文件句柄,以释放资源。

4. 内存泄漏:

  • 问题: 在文件操作过程中,未释放动态分配的内存,导致内存泄漏。
  • 解决方式: 在分配内存后,确保使用 free() 或相应的内存释放函数来释放内存。

5. 文件读写错误:

  • 问题: 文件读取或写入时发生错误,可能导致数据损坏或丢失。
  • 解决方式: 在进行文件读写操作时,检查返回的错误代码,如 errno,以了解发生的错误类型。根据错误类型采取适当的错误处理措施。

6. 并发访问问题:

  • 问题: 多个进程或线程同时访问同一文件,可能导致竞争条件和数据一致性问题。
  • 解决方式: 使用同步机制,如互斥锁,以确保同时只有一个进程或线程可以访问文件。此外,使用文件锁(如fcntl文件锁)来协调对文件的访问。

7. 文件路径跨平台问题:

  • 问题: 文件路径在不同操作系统上具有不同的格式和分隔符。
  • 解决方式: 使用跨平台的路径处理库或编写可移植的代码,以处理不同操作系统上的文件路径。

8. 系统调用参数错误:

  • 问题: 使用不正确的参数或参数类型来执行系统调用。
  • 解决方式: 仔细查看系统调用的文档,并确保传递正确的参数和参数类型。

9. 错误处理不足:

  • 问题: 没有适当的错误处理机制,导致难以诊断和修复问题。
  • 解决方式: 始终实施良好的错误处理,包括记录错误日志、返回适当的错误码和提供有用的错误信息,以便更容易排除故障。

10. 资源泄漏:

  • 问题: 未释放系统资源,如文件句柄或网络连接,导致资源泄漏。
  • 解决方式: 始终在不再需要资源时释放它们,使用资源管理的最佳实践来避免泄漏

以下是一个我个人实践的示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

int main() {
    FILE *file;
    char buffer[1024];

    // 尝试打开文件
    file = fopen("example.txt", "r");
    if (file == NULL) {
        // 处理文件打开错误
        perror("无法打开文件");
        return 1;
    }

    // 读取文件内容并处理错误
    if (fgets(buffer, sizeof(buffer), file) == NULL) {
        if (feof(file)) {
            // 到达文件末尾
            printf("已读取到文件末尾\n");
        } else {
            // 读取错误
            perror("读取文件时发生错误");
        }
    } else {
        // 成功读取文件内容
        printf("文件内容:%s", buffer);
    }

    // 关闭文件并处理错误
    if (fclose(file) != 0) {
        perror("关闭文件时发生错误");
        return 1;
    }

    return 0;
}

实践

实践内容:使用系统调用递归复制文件

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

#define BUFFER_SIZE 4096

int copyFile(const char *srcPath, const char *destPath) {
    int srcFd, destFd;
    ssize_t bytesRead;
    char buffer[BUFFER_SIZE];

    // 打开源文件
    srcFd = open(srcPath, O_RDONLY);
    if (srcFd == -1) {
        perror("无法打开源文件");
        return -1;
    }

    // 创建或打开目标文件,注意使用O_CREAT和O_WRONLY
    destFd = open(destPath, O_CREAT | O_WRONLY | O_TRUNC, 0644);
    if (destFd == -1) {
        perror("无法创建或打开目标文件");
        close(srcFd);
        return -1;
    }

    // 逐块复制文件内容
    while ((bytesRead = read(srcFd, buffer, sizeof(buffer))) > 0) {
        if (write(destFd, buffer, bytesRead) != bytesRead) {
            perror("写入目标文件时出错");
            close(srcFd);
            close(destFd);
            return -1;
        }
    }

    // 检查读取错误
    if (bytesRead == -1) {
        perror("读取源文件时出错");
        close(srcFd);
        close(destFd);
        return -1;
    }

    // 关闭文件描述符
    close(srcFd);
    close(destFd);

    return 0;
}

int main() {
    const char *srcFile = "source.txt";
    const char *destFile = "destination.txt";

    if (copyFile(srcFile, destFile) == 0) {
        printf("文件复制成功:%s 到 %s\n", srcFile, destFile);
    } else {
        fprintf(stderr, "文件复制失败\n");
    }

    return 0;
}
posted @ 2023-09-29 10:24  20211308wjc  阅读(14)  评论(0编辑  收藏  举报