系统调用概述及文件操作
在计算机系统中,系统调用是用户程序向操作系统请求服务的重要机制。本文将概述系统调用及其相关概念,包括内核态与用户态、库函数与系统调用的关系,以及文件操作的相关函数。
内核态与用户态
在大多数现代操作系统中,处理器的操作等级被划分为不同的权限级别。以 Intel CPU 为例,其权限分为四个级别,即 Ring 0、Ring 1、Ring 2 和 Ring 3。其中,Ring 0 为最高权限,Ring 3 为最低权限。Linux 系统采用的则是 Ring 0 和 Ring 3,其中:
- 内核态 (Ring 0): 在这一模式下,程序可以直接访问硬件资源和系统内核。这种模式下的线程被称为内核线程。
- 用户态 (Ring 3): 在该模式下,程序被限制无法直接访问硬件资源,所有请求必须通过系统调用转交给内核处理。这种模式下的线程称为用户线程。
内核态与用户态之间的切换是通过上下文切换来实现的,频繁的切换会导致程序执行效率降低。
库函数与系统调用的关系
在 C 语言编程中,有些库函数会调用系统调用,有些则不会。系统调用是操作系统提供的接口,用于执行特权操作,而库函数则是对系统调用的一个封装,提供了更高级或更便捷的功能。例如,printf
函数是一个库函数,它最终会调用系统调用来输出数据。
文件操作
文件操作是使用系统调用的一个重要方面。以下介绍常用的文件操作系统调用。
int open(char *path, int flag);
int open(char *path, int flag, int mode);
参数:
- path: 要打开的文件路径。
- flag: 文件打开的状态:
O_RDONLY
: 只读模式。O_WRONLY
: 只写模式。O_RDWR
: 读写模式。O_CREAT
: 如果文件不存在,则创建新文件。O_APPEND
: 以追加模式打开文件。O_NONBLOCK
: 非阻塞模式。
多个状态之间可以使用 |
连接。
- mode: 文件权限(仅当使用三参数形式时有效):
0
: 不可读,不可写,不可执行。1
: 可执行。2
: 可写。4
: 可执行且可写。
示例代码:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
int fd = open("example.txt", O_CREAT | O_WRONLY | O_APPEND, 0644);
if (fd < 0) {
perror("Failed to open file");
exit(EXIT_FAILURE);
}
// 文件成功打开,接下来可以进行写入操作
close(fd);
return 0;
}
int close(int fd)
用于关闭打开的文件。
示例代码:
if (close(fd) < 0) {
perror("Failed to close file");
}
int write(int fd, const void *buf, size_t count)
用于向打开的文件中写入数据。
示例代码:
const char *text = "Hello, World!\n";
if (write(fd, text, strlen(text)) < 0) {
perror("Failed to write to file");
}
int read(int fd, void *buf, size_t count)
用于从打开的文件中读取数据。
示例代码:
char buffer[100];
ssize_t bytesRead = read(fd, buffer, sizeof(buffer) - 1);
if (bytesRead < 0) {
perror("Failed to read from file");
} else {
buffer[bytesRead] = '\0'; // 确保字符串以 null 结尾
printf("Read from file: %s", buffer);
}
int fcntl(int fd, int cmd, int arg)
用于操作文件描述符的状态。
参数:
- fd: 文件描述符。
- cmd: 操作方式:
F_DUPFD
: 拷贝已有文件描述符。F_GETFL
: 获取文件状态。F_SETFL
: 设置文件状态。
- arg: 操作方式的值。
示例代码:
int flags = fcntl(fd, F_GETFL);
if (flags < 0) {
perror("Failed to get file status flags");
} else {
// 设置文件为非阻塞模式
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
int stat(const char *path, struct stat *buf);
参数:
- path: 文件路径。
- buf: 存储文件状态的结构体变量指针。
示例代码:
struct stat fileInfo;
if (stat("example.txt", &fileInfo) < 0) {
perror("Failed to get file status");
} else {
printf("File size: %lld bytes\n", (long long)fileInfo.st_size);
}
文件描述符
文件描述符是系统打开文件时为文件分配的标识符。通常情况下,系统默认打开三个文件:标准输入、标准输出和错误输出,分别对应:
- 标准输入:
0
- 标准输出:
1
- 错误输出:
2
实用经验(注意)
- 如果文件存在,建议使用两参数形式的
open
函数。 - 如果文件不存在,建议使用三参数形式的
open
函数。
文件夹操作
文件夹操作的基本系统调用包括:
DIR *opendir(const char *name)
: 打开目录。int closedir(DIR *dirp)
: 关闭目录。struct dirent *readdir(DIR *dirp)
: 读取目录中的文件。
示例代码:
#include <dirent.h>
DIR *dir = opendir(".");
if (dir == NULL) {
perror("Failed to open directory");
exit(EXIT_FAILURE);
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
printf("Found file: %s\n", entry->d_name);
}
closedir(dir);
代码示例
实现某一目录下的所有文件及文件夹的遍历
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
// 遍历家目录下的文件夹
void bianLi(char *name)
{
// 打开家目录
DIR *home = opendir(name);
if (home == NULL)
{
printf("目录打开失败");
return 0;
}
while (1)
{
struct dirent *res = readdir(home);
// 遍历完毕,跳出循环
if (res == NULL)
{
break;
}
// 如果为目录
if (res->d_type == DT_DIR)
{
if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)
{
continue;
}
char names[300] = {0};
strcat(names, name);
strcat(names, "/");
strcat(names, res->d_name);
// 回调函数
printf("%s\n", names);
bianLi(names);
}
// 如果为文件
else if (res->d_type == DT_REG)
{
printf("%s\n", res->d_name);
}
}
closedir(home);
}
//这是主函数
int main(int argc, char const *argv[])
{
//家目录是"/home/wyf"
char *name = "/home/wyf";
bianLi(name);
return 0;
}
实现某一文件类容的复制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
// stat
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
// 空间
#include <stdlib.h>
// 文件类容的复制
int main(int argc, char const *argv[])
{
//这个是我已经存在的文件
int temp = open("/home/wyf/2024/3jieDuan/02day/test.txt",O_RDONLY);
if (temp == -1)
{
printf("打开失败!\n");
return 0;
}
//下面这里选择的模式是:O_WRONLY | O_APPEND | O_CREAT 写入,尾部追加,没有就创建该文件
//上面文章提到过(经验)
//如果文件存在,建议使用两参数形式的 open 函数。
//如果文件不存在,建议使用三参数形式的 open 函数。
int temp2 = open("/home/wyf/2024/3jieDuan/02day/testCpy.txt",O_WRONLY | O_APPEND | O_CREAT,0664);
if (temp2 == -1)
{
printf("创建失败\n");
return 0;
}
// 定义存储信息的结构体,并将信息存储在tes里面
struct stat tes;
stat("/home/wyf/2024/3jieDuan/02day/test.txt",&tes);
int len = tes.st_size;
// printf("长度为:%d\n",len);
char *res = (char *)calloc(len+1,1);
read(temp,res,len);
write(temp2,res,len);
return 0;
}
实现从键盘录入文件地址,判断该文件是文件夹还是文件,并显示器可操作权限
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
// 从键盘录入文件地址,判断该文件是文件夹还是文件,并显示器可操作权限
int main(int argc, char const *argv[])
{
char name[100] = {0};
read(0, name, 100);
int len = strlen(name);
//最后一位换行置为0
name[len-1] = 0;
//系统stat结构体
struct stat res;
int temp = stat(name, &res);
if (temp == -1)
{
printf("录入的%s不存在\n", name);
return;
}
else
{
//这里只展示了一种类型的判断
if ((res.st_mode & __S_IFREG) == __S_IFREG)
{
printf("%s是文件\t", name);
}
else
{
printf("%s是目录\t", name);
}
//
if ((res.st_mode & S_IRUSR) == S_IRUSR)
{
printf("可以读取\t");
}
else
{
printf("不可读取\t");
}
//
if ((res.st_mode & S_IWGRP) == S_IWGRP)
{
printf("可以写\t");
}
else
{
printf("不可以写\t");
}
//
if ((res.st_mode & S_IXUSR) == S_IXUSR)
{
printf("可以执行\n");
}
else
{
printf("不可执行\n");
}
}
return 0;
}