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;
}
标签:文件,八章,return,读取,系统,Unix,调用,Linux,操作系统
From: https://www.cnblogs.com/wujiuchun/p/17736833.html