(一)库函数、系统调用

库函数:在函数库实现中、在用户空间调用执行

(1)C语言文件操作库函数
  1. FILE* fopen(const char* pathname, const char* mode); // mode "r"
  2. int fread(void* buff, size_t size, size_t count, FILE* fp);
  3. fwrite(void* buff, size_t size, size_t count, FILE* fp);
  4. fseek(FILE* fp, int offset, int whence); //移动读写游标
    // whence : SEEK_SET SEEK_CUR SEEK_END
  5. fclose(FILE* fp);
(2)Linux系统调用

在系统内核中实现,用户空间调用,内核空间执行

1.int open(const char* pathname, int flag);
int open(const char *pathname, int flags, mode_t mode);

参数详解:

  • pathname:路径文件名
  • flags:打开方式 O_RDONLY(只读)O_WRONLY(只写)O_RDWR(读写)O_APPEND(文本尾部追加打开) O_TRUNC(pathname文件存在,清空该文件,没有则创建)
  • O_CREAT: 若此文件不存在则创建它。使用此选项时需要提供第三个参数mode ,表示该文件
    的访问权限。
  • 返回值
    失败-1,并设置全局erron
    成功返回文件描述符

2.int read(int fd, void* buff, size_t size);

参数详解:

  • fd:打开的文件描述符
  • buff:保存数据的buff缓冲区的首地址
  • size:一次读取的最大字节数(sizeof(buff) - 1)
  • 返回值:
    失败 -1
    读取到的字节个数

3.int write(int fd, void* buff, size_t size);

  • fd:文件描述符
  • buff:写入文件的缓冲区的首地址
  • size:buff中的真实数据大小
  • 返回值:
    失败 -1
    成功:写入的字节个数

4.lseek(int fd, int offset, int whence);

  • fd:文件描述符
  • offset:标志偏移量
  • whence: SEEK_SET SEEK_CUR SEEK_END
  • 返回值:
    成功:将结果偏移位置返回为从文件开头开始以字节为单位测量
    失败 -1 errno被设置为指示错误

5.int close(int fd);

关闭文件描述符

(二)用户态和内核态相关知识

侵删,点击参考

(三)系统调用的使用测试

从键盘获取,保存在mydata.txt文件中

#include <stdio.h>	//标准输入输出文件
#include <fcntl.h>	//系统调用头文件
#include <unistd.h>
#include <string.h>
void KeyIntoFile()
{
	int fd = open("mydata.txt", O_WRONLY |O_CREAT | O_TRUNC, 0664);
	if(fd == -1)
		return;
	printf("input data:\n");
	while(1)
	{
		char buff[128] = {0};
		fgets(buff, 127, stdin);
		if(strncmp(buff, "end", 3) == 0)
		{
			break;
		}

		write(fd, buff, strlen(buff));
	}
	close(fd);
	printf("saved\n");
}
int main()
{
	KeyIntoFile();
	return 0;
}

测试结果:
在这里插入图片描述

(四)实现cat的显示文件内容的功能

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
void Mycat(int argc, char* argv[])
{
	if(argc != 2)
	{
		perror("argc error");
		return;
	}
	char filename[128] = {0};
	sprintf(filename, "%s", argv[1]);

	int fd = open(filename, O_RDONLY);
	if(fd == -1)
	{
		perror("file not exist");
		return;
	}
	while(1)
	{
		memset(filename, 0, sizeof(filename));
		int count = read(fd, filename, 127);
		if(count == -1)
		{
			perror("read error");
			break;
		}
		if(count == 0)
		{
			break;
		}
		printf("%s", filename);
	
	}

	close(fd);
}

int main(int argc, char* argv[])
{
	Mycat(argc, argv);
	return 0;
}

测试结果:
在这里插入图片描述

(五)系统调用实现cp命令

cp pathname1 pathname2

将pathname1文件拷贝到pathname2下
我们简单使用了下cp命令,结果如下
在这里插入图片描述

需要额外知识:
文件夹相关操作

开始仿写:

(1)mycp.h文件
#ifndef MYCP_H
#define MYCP_H

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
//cp命令
void Mycp(int argc, char* argv[]);

//-1文件名空, 0不是文件夹,1是文件夹
//检查是否为目录
int CheckIsDir(const char* path);

//-1路径为空 1绝对路径 2相对路径
//判断是否为合法路径、绝对或者相对路径;
int CheckPath(const char* path);

//拷贝数据
void CopyData(const char* path_src, const char* path_des);

//获取源文件名+拼接在目标路径后
char* getNewPath_des(const char* path_src, const char* path_des);

#endif
(2)mycp.c文件
#include "mycp.h"

//cp命令
void Mycp(int argc, char* argv[])
{
	if(argc != 3)
	{
		printf("argc is err\n");
		return;
	}
	//检查是绝对路径还是相对路径
	int ret_pathsrc = CheckPath(argv[1]);
	int ret_pathdes = CheckPath(argv[2]);
	//判断是文件夹还是普通文件
	int type_src = CheckIsDir(argv[1]);
	int type_des = CheckIsDir(argv[2]);
	
	if(type_src == 0 || type_src == -1)
	{
		printf("%s不是合法的文件或文件夹\n", argv[1]);
		return;
	}
	/*给定目标文件名
		src:绝对路径或相对路径/src普通文件名
		des:绝对路径或相对路径/des普通文件名(新文件名) 
	*/
	if(type_src == 2 && (type_des == 2 || type_des == -1))	//
	{
		CopyData(argv[1], argv[2]);
		return;
	}

	/*未给定目标文件名(需要拼接目标文件名)
	src:绝对路径或相对路径/src普通文件名
	des:绝对路径或相对路径/文件夹
	*/
	if(type_src == 2 && type_des == 1)
	{
		//获取源文件名+拼接在目标路径后
		char* newpath_des = getNewPath_des(argv[1], argv[2]);
		if(newpath_des == NULL)
			return;
		CopyData(argv[1], newpath_des);
		free(newpath_des);
		return;
	}
}

//-1文件名空/文件名不存在, 0不是文件夹、普通文件,1是文件夹, 2是普通文件
//检查是否为目录
int CheckIsDir(const char* path)
{
	if(path == NULL)
		return -1;
	struct stat file_info;
	int ret = stat(path, &file_info);
	if(ret != 0)
		return -1;
	if(S_ISDIR(file_info.st_mode))
	{
		return 1;
	}
	else if(S_ISREG(file_info.st_mode))
	{
		return 2;
	}
	else
	{
		return 0;
	}
	
}
//-1路径为空 1绝对路径 2相对路径
//判断是否为合法路径、绝对或者相对路径;
int CheckPath(const char* path)
{
	if(path == NULL)
		return -1;
	//绝对路径
	if(strncmp(path, "/", 1) == 0)
	{
		return 1;
	}
	//相对路径
	else
	{
		return 2;
	}
}

//拷贝数据
void CopyData(const char* path_src, const char* path_des)
{
	if(path_src == NULL || path_des == NULL)
		return;

	int fd_src = open(path_src, O_RDONLY);
	int fd_des = open(path_des, O_WRONLY | O_CREAT | O_TRUNC, 0664);
	if(fd_src == -1 || fd_des == -1)
	{
		printf("open src_file/ create des_file err\n");
		return;
	}
	while(1)
	{
		char buff[1024] = {0};
		int ret = read(fd_src, buff, 1023);
		if(ret <= 0)
		{
			break;
		}
		write(fd_des, buff, ret);
	}
	close(fd_src);
	close(fd_des);
}

//获取源文件名+拼接在目标路径后
char* getNewPath_des(const char* path_src, const char* path_des)
{
	if(path_src == NULL || path_des == NULL)
		return NULL;

	char src_name[128] = {0};
	char des_name[128] = {0};
	strcpy(src_name, path_src);
	strcpy(des_name, path_des);

	//存放分割的每一个元素
	char* src_buff[16] = {0};
	char* s = strtok(src_name, "/");
	int i = 0;
	while(s != NULL)
	{
		src_buff[i++] = s;
		s = strtok(NULL, "/");
	}

	//拼接目标文件名
	//目的路径 + '/' + 文件名
	char* heap_space = (char*)malloc(sizeof(char) * 128);
	if(heap_space == NULL) return NULL;
	memset(heap_space, 0, sizeof(char) * 128);

	strcpy(heap_space, path_des);
	strcat(heap_space, "/");
	strcat(heap_space, src_buff[i - 1]);

	return heap_space;
}
(3)main.c文件
#include "mycp.h"

int main(int argc, char* argv[])
{
	Mycp(argc, argv);
	return 0;
}
(4)makefile文件
objects = main.o mycp.o
mycp:$(objects)
	cc -o mycp $(objects) -g

main.o:mycp.h
	cc -c main.c -g
mycp.o:mycp.h
	cc -c mycp.c -g
.PHONY:clean
clean:
	rm mycp $(objects)
(5)测试结果

测试用例:./mycp
在这里插入图片描述

Logo

更多推荐