0
点赞
收藏
分享

微信扫一扫

初识Vue-脚本架(如何创建vue项目并使用)

老王420 2024-05-11 阅读 35

前言

铺垫:文件

1.C语言文件操作 

以写入模式打开文件

#include<stdio.h>

int main()
{
	FILE* fp = fopen("test.txt", "w");
	if (fp == NULL)
	{
		perror("fopen");
		return 1;
	}

	const char* str = "hello";
	fputs(str,fp);

	fclose(fp);

	return 0;
}

将写入的那部分注释掉(fputs),再执行此程序去打开文件test.txt会发现原本写入的字符不见了

以w方式打开的文件,该文件会被自动清空,再写入

很像之前学过的输出重定向>,重定向的本质就是一种写入嘛

既然像重定向,那么有像追加重定向的也很合理吧

FILE* fp = fopen("test.txt", "a");

以"a“方式打开就是追加appending(在结尾出写入不清空)

chdir

chdir("路径");

以读模式打开文件 

#include<stdio.h>
#include<unistd.h>
int main()
{
	FILE* fp = fopen("test.txt", "r");
	if (fp == NULL)
	{
		perror("fopen error!");
		return 1;
	}
	char buf[64];
	const char* msg = "hello bit!\n";
	while (1)
	{
		char* r = fgets(buf, sizeof(buf), fp);
		if (!r)
			break;
		printf("%s\n", buf);
	}
	fclose(fp);
	return 0;
}

此外还有fputc、fwrite、fprintf、scanf、fscanf...

2.stdin、stdout、stderr

之前我们学过一个概念:

Linux一切皆文件,显示器也是(向显示器写入也要先打开文件)

之前我们用的时候却没主动打开默认就能打印、读取

这说明了:进程在运行的时候都会默认打开三个输入输出流:标准输入流、标准输出流以及标准错误流对应C语言中的stdin、stdout、stderr

标准输入对应的是键盘设备,标准输出、标准错误对应的是显示器设备

3.系统文件IO

之前我们有发过这样一张图:

OS不允许进程直接访问硬件,访问硬件必须通过操作系统

访问文件不仅有C语言上的文件接口,OS必须提供对应的访问文件的系统调用接口

所以C标准库中的文件IO接口一定封装了系统调用

以下是一些系统调用接口介绍

1.open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname,int flags,mode_t mode);

open("log.txt",O_WRONLY);

如果没有此文件,报错:无此文件或目录

open("log.txt",O_WRONLY|O_CREAT);

如果没有此文件,新创建一个,但文件的权限会乱码 

flags这些大写的字母能联想到什么?宏

为何用一个或者多个常量进行“或”运算,构成flags?

其实是以位图的方式传参

#define ONE 1
#define TWO (1<<1)
#define THREE (1<<2)
#define FOUR (1<<3)
#define FIVE (1<<4)
 
void Print(int flag)
{
    if(flag & ONE) printf("1\n");
    if(flag & TWO) printf("2\n");
    if(flag & THREE) printf("3\n");
    if(flag & FOUR) printf("4\n");
    if(flag & FIVE) printf("5\n");
}
 
int main()
{
    Print(ONE);
    printf("----------------------\n");
    Print(TWO);
    printf("----------------------\n");
    Print(ONE|TWO);
    printf("----------------------\n");
    Print(THREE|FOUR|FIVE);
    printf("----------------------\n");
    Print(ONE|TWO|THREE|FOUR|FIVE);
}

这些宏的特点是每个二进制为只有一个1,而Print函数中if语句判断的就是对应的比特位哪个位为1

open中的flags其实和这一个道理

​
#define O_RDONLY       0000
#define O_WRONLY       0001
#define O_RDWR         0010
#define O_CREAT        0100

​
int open(arg1, arg2, arg3)
{
	if (arg2&O_RDONLY)
    {
		//O_RDONLY
	}
	if (arg2&O_WRONLY)
    {
		//O_WRONLY
	}
	if (arg2&O_RDWR)
    {
		//O_RDWR
	}
	if (arg2&O_CREAT)
    {
		//O_CREAT
	}
	//...
}

上面我们提到open("log.txt",O_WRONLY|O_CREAT);新创建的文件权限会乱码

打开曾经不存在的文件,mode给初始权限,不给初始权限会乱码

也就是说第三个参数是权限码

open("log.txt",O_WRONLY|O_CREAT,0666);

这个mode受系统umask影响,可以调接口umask(0);自己设

若想创建出来文件的权限值不受umask的影响,则需要在创建文件前使用umask函数将文件默认掩码设置为0。

2.close

3.write

ssize_t write(int fd, const void *buf, size_t count);

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	int fd = open("log.txt", O_WRONLY | O_CREAT, 0666);
	if (fd < 0)
    {
		perror("open");
		return 1;
	}
    printf("fd:%d\n",fd);
	const char* str = "hello\n";
	for (int i = 0; i < 10; i++)
    {
		write(fd, str, strlen(str));
	}
	close(fd);
	return 0;
}

4.read 

ssize_t read(int fd, void *buf, size_t count);

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
	int fd = open("log.txt", O_RDONLY);
    if (fd < 0){
		perror("open");
		return 1;
	}

    printf("fd:%d\n",fd);
    char buffer[128];
	ssize_t s = read(fd, buffer, sizeof(buffer)-1);
	if (s>0)
    {
        buffer[s]='\n';
        printf("%s",buffer);
	}
	close(fd);
	return 0;
}

 4.文件描述符fd

上面的系统调用接口很多都使用了文件描述符fd,那么什么是文件描述符呢?

不知你有没有发现,我在上面系统调用接口write、read的代码中偷偷打印了fd

而他们的起始位置都是3

而打开多个文件,fd1、fd2、fd3...起始fd也是从3开始

那么0、1、2哪去了?还记得我们之前讲的stdin、stdout、stderr吗?

没错0、1、2就是被他们占用了

0,1,2,3...n有点像什么?数组下标

FILE* 是什么?

int fd = open();

//系统调用接口用fd接收 

FILE *fp = fopen();

C语言接口用fp接收 

C标准库自己封装的结构体,因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。
所以C库当中的FILE结构体内部,必定封装了fd。

stdin封0、stdout封1、stderr封2

5.文件管理

上面我们说到C语言封装系统调用接口。为什么C语言要封装呢?

不封装就只能使用系统调用接口,换个系统就不能用了。封装了可移植(跨平台)性

 其中

struct file * fd_array[NR_OPEN_DEFAULT]

是个结构体指针数组,而文件fd就是此数组的下标

 不出意外task_struct中有个指针指向它

 

 进程怎么知道自己打开哪些文件?

 

为什么后续访问文件,用系统调用接口,必用fd呢?

进程自己

write(fd,...) ;

可以在数组中找到文件然后就通过

read(fd,...);

可以把缓冲区的数据拷到文件的缓冲区,刷新到磁盘中(写入);读有数据就读,没数据让操作系统从磁盘拷到缓冲区

6.重定向

如果关闭fd为1的标准输出流,那么打印内容会放在哪里呢?

int main()
{
    close(1);//关闭标准输出流
    open("log.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666);
    printf("hello linux\n");
}

没打印出来,把打印内容写入文件中

在最开始close(0);把文件fd为0的删了,再分配给log.txt会分到0

close(2)则分到2 

dup2系统调用 

#include <unistd.h>
int dup2(int oldfd, int newfd);

将oldfd索引内容拷贝给newfd索引内容

 

 7.FILE结构体以及缓冲区问题

在/usr/include/libio.h
struct _IO_FILE {
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
//缓冲区相关
/* The following pointers correspond to the C++ streambuf protocol. */
/* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
char* _IO_read_ptr; /* Current read pointer */
char* _IO_read_end; /* End of get area. */
char* _IO_read_base; /* Start of putback+get area. */
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */
char* _IO_buf_base; /* Start of reserve area. */
char* _IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno; //封装的文件描述符
#if 0
int _blksize;
#else
int _flags2;
#endif
_IO_off_t _old_offset; /* This used to be _offset but it's too small. */
#define __HAVE_COLUMN /* temporary */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
/* char* _save_gptr; char* _save_egptr; */
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

OS的缓冲区是当文件写入磁盘之间会有一块缓冲区,而这块缓冲区是由操作系统决定什么时候刷新的

如何证明缓冲区存在?看以下代码

#include <stdio.h>
#include<unistd.h>
#include <string.h>
int main()
{
    const char *s1="hello write\n";
    const char *s2="hello fprintf\n";
    const char *s3="hello fwrite\n";\
    write(1,s1,strlen(s1));
    fprintf(stdout,"%s", s2);
    fwrite(s3, strlen(s3), 1, stdout);
    fork();
    return 0;
}

直接运行 

 

重定向到文件里

 

如果向显示器进行打印(直接运行),刷新方案就是行刷新

如果向文件写入(重定向),对log.txt刷新策略变成全缓冲

 

举报

相关推荐

0 条评论