【Linux】文件描述符

news2024/11/29 18:26:13

目录

  • 🌈前言
  • 🌷1、文件的概念
  • 🌹2、文件操作(C语言)
    • 🍡2.1、概念+基本打开关闭操作
    • 🍢2.2、文件的打开方式
    • 🍣2.3、文件的读写操作
    • 🍤2.4、对系统调用的封装
  • 🌸3、系统文件调用接口
    • 🍥3.1、open(打开文件)
    • 🍦3.2、close(关闭文件)
    • 🍧3.3、read(读文件)
    • 🍨3.4、write(写文件)
  • 🌹4、文件描述符
    • 🍩4.1、为什么文件描述符从3开始???
    • 🍪4.2、fd描述符底层原理
    • 🍬4.3、Linux下一切皆文件
    • 🍫4.4、文件描述符的分配规则

🌈前言

本篇文章进行操作系统中文件描述符的学习!!!


🌷1、文件的概念

概念:

  1. 文件 = 文件内容 + 文件属性,文件属性也是数据,即使我们创建一个空文件,也要占据磁盘空间

  2. 文件操作 = 文件内容的操作 + 文件属性的操作 – 在操作文件的过程中,有可能既改变了文件的内容,又改变了文件属性。比如:改变内容到一定次数,文件的时间属性也会被修正

  3. 所谓的“打开”文件,就是将文件的内容和属性加载到内存当中!!! – 冯诺依曼体系结构

  4. 是不是所有的文件,都会处于被打开状态?绝对不是!没有被打开的文件,在磁盘上静静的存储着

打开的文件(“内存文件”)和“磁盘文件”的区别是什么???

  • 内存文件:磁盘文件中的内容和属性大部分被加载到进程当中,就叫做“内存文件”
  • 磁盘文件:存储在磁盘中的文件
  • 区别:一个是虚拟内存文件,一个是磁盘文件
  1. 通常我们使用C语言打开文件、访问文件和关闭文件,都是通过fopen、fclose、fread、fwrite…函数来进行操作的!!!

是谁在进行文件相关操作的呢??? 答案是:进程

  • 一个程序编译链接好后,会生成一个可执行程序,这时程序还没有被执行,在磁盘中还是一个普通的文件!!!

  • 该程序运行时,会被加载到内存当中,然后程序的代码和数据被进程读取,进程会被加入到运行队列

  • 当被调度器调度时,会执行进程中程序的代码,这时就会执行相应的文件操作和其他代码


🌹2、文件操作(C语言)

🍡2.1、概念+基本打开关闭操作

文件在读写之前应该先打开文件,在使用结束之后应该关闭文件 <stdio.h>

//打开文件
FILE * fopen ( const char * filename, const char * mode );
//关闭文件
int fclose ( FILE * stream );
  • 函数 fopen 打开文件名为 “filename” 指向的字符串的文件,将一个流与它关联

  • 当文件被打开时,会默认打开三个流,分别是:stdin & stdout & stderr(后面理解)

  • 仔细观察发现,这三个流的类型都是FILE*, fopen返回值类型,文件指针

  • 如果filename不存在,则会在”当前路径下“创建新的文件!!!

如何理解”当前路径“???

  • 我们都知道程序执行后,会被加载到内存,然后被进程读取
  • 当CPU执行到打开文件时,发现文件不存在
  • 那么CPU就会在”进程所处的工作路径下创建新的文件“!!!

查看进程中的cwd属性,ls /proc/进程pid

[lyh_sky@localhost test]$ cat myfile.c 
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    // 以写的方式打开文件,不存在就新建一个文件
    FILE* fp = fopen("log.txt", "w");
    if (fp == NULL)
    {
        perror("fp");
    }
    
    // 让该进程一直在运行,方便查看cwd
    printf("我是一个进程,我的pid: %d\n", getpid());
    while (1)
    {}
    
    // 关闭文件
    fclose(fp);
    return 0;
}
[lyh_sky@localhost test]$ ls
log.txt  makefile  myfile  myfile.c    // 在当前工作目录创建了新的文件

在这里插入图片描述

用chdir系统函数修改进程当前所处工作路径,并且在新路径创建文件

[lyh_sky@localhost test]$ cat myfile.c 
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main()
{
	chdir("/home/lyh_sky");
    // 以写的方式打开文件,不存在就新建一个文件
    FILE* fp = fopen("log.txt", "w");
    if (fp == NULL)
    {
        perror("fp");
    }
    
    // 让该进程一直在运行,方便查看cwd
    printf("我是一个进程,我的pid: %d\n", getpid());
    while (1)
    {}
    
    // 关闭文件
    fclose(fp);
    return 0;
}
// 运行后,然后ctrl + c退出进程,就能看到文本文件存在了
[lyh_sky@localhost test]$ ls /home/lyh_sky -al | grep log.txt
-rw-rw-r--.  1 lyh_sky lyh_sky     0 1120 23:58 log.txt

在这里插入图片描述

🍢2.2、文件的打开方式

  • 参数 “mode” 指向一个字符串,以下列序列之一开始 (序列之后可以有附加的字符):
打开方式作用指定文件不存在
“r”打开文本文件,用于读。流被定位于文件的开始出错
“r+”打开文本文件,用于读写。流被定位于文件的开始出错
“w”将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始建立新的文件
“w+”打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始建立新的文件
“a”打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾建立新的文件
“a+”打开文件,用于追加 (在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾建立新的文件

实例:以写的方式打开文件,并且关闭文件

#include <stdio.h>

int main()
{
    // 以写的方式打开文件,不存在就新建一个文件
    FILE* fp = fopen("log.txt", "w");
    // 判断是否打开成功
    if (fp == NULL)
    {
        perror("fp");
    }
    // 关闭文件
    fclose(fp);
    return 0;
}

注意:当以w的方式打开文件时,如果文件有数据,会被截断清空,并且流被定位到文件的开始!

在这里插入图片描述


🍣2.3、文件的读写操作

文件的顺序读写

功能函数名适用于
字符输入函数fgetc所有输入流
字符输出函数fputc所有输出流
文本行输入函数fgets所有输入流
文本行输出函数fputs所有输出流
格式化输入函数fscanf所有输入流
格式化输出函数fprintf所有输出流
二进制输入fread文件
二进制输出fwriter文件

例子一:对文件进行写入数据 – 使用”fprintf“

int fprintf(FILE *stream, const char *format, …);

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

int main()
{
    // 以写的方式打开文件,不存在就新建一个文件
    FILE* fp = fopen("log.txt", "w");
    if (fp == NULL)
    {
        perror("fp");
    }
    int cnt = 1;
    while (cnt <= 5)
    {
        fprintf(fp, "%d: hello world!!!\n", cnt);
        ++cnt;
    }

    // 关闭文件
    fclose(fp);
    return 0;
}

例子二:对文件进行读取数据 – 使用”fgets“

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

int main()
{
    // 以读的方式打开文件,不存在会报错
    FILE* fp = fopen("log.txt", "r");
    if (fp == NULL)
    {
        perror("fp");
    }
    // 读取文件数据
    char buffer[64];
    while(fgets(buffer, sizeof(buffer), fp) != NULL)
    {
        printf("%s", buffer);
    }
    // 关闭文件
    fclose(fp);
    return 0;
}

在这里插入图片描述

例子三:对文件进行追加数据 – 使用“fprintf”进行写入

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

int main()
{
    // 以追加的方式打开文件,不存在会创建新的文件
    FILE* fp = fopen("log.txt", "a");
		if (fp == NULL)
    {
        perror("fp");
    }
    int cnt = 1;
    // 向文件写入五次hello world
    while (cnt <= 5)
    {
        fprintf(fp, "%d: hello world!!!\n", cnt);
        ++cnt;
    }
    // 关闭文件
    fclose(fp);
    return 0;
}

在这里插入图片描述


🍤2.4、对系统调用的封装

理论:

  • 当我们打开文件后,向文件写入数据,是向磁盘写入数据的,而不是向内存文件写入

  • 磁盘是一个硬件,只有OS有资格向硬件写入数据

  • 我们不能绕开OS对硬件进行相关的操作,因为所有上层访问文件的操作,都必须贯穿OS

  • 用户(上层)是通过操作系统提供的相关系统调用来访问底层硬件的

  • C/C++的部分库函数都提供了系统调用的封装!!!

  • C语言中printf就是封装了OS提供的相关系统调用来对硬件(显示器)进行写入数据

  • 所有的语言都会系统接口做了封装

为什么要封装呢???

  • 原生系统接口,使用成本比较高,我们要了解不同OS中不同的系统调用的参数问题!!!

  • 语言不具备跨平台性!!!

  • 比如:封装了LInux系统调用的fork(),如果在Windows上跑,是跑不了的!!!

封装是如何解决跨平台性的呢???

  • 使用多态的思想,上层调用相同功能的接口,但是底层却完全不同

  • C语言是通过穷举所有的底层接口,通过条件编译控制不同的OS版本接口

  • C++是通过xoskit解决跨平台开发问题的

  • 其他不同的语言或脚本,都有自己不同的跨平台解决方案!!!


🌸3、系统文件调用接口

🍥3.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)

参数解析:

  • 返回值:打开成功,返回新打开的文件描述符,打开失败,返回-1
  • pathname:要打开或创建的目标文件

前面三个常量/宏,必须指定一个且只能指定一个

  • flags标记位:打开文件时,可以传入多个参数选项,用下面的一个或者多个宏进行“或”运算:
  1. O_RDONLY:只读打开
  2. O_WRONLY:只写打开
  3. O_RDWR :读,写打开
  4. O_APPEND:追加写数据
  5. O_CREAT: 若文件不存在,则创建它,需要使用mode选项,来指明新文件的访问权限
  6. O_TRUNC:文件已存在,且是一个普通文件,打开文件是可写(即文件是用 O_RDWR 或O_WRONLY 模式 打开 ),就把文件的长度设置为零 , 丢弃其中的现有内容

flags底层是通过位图来标识不同的状态的!!! – 下面代码助于理解

  • 系统传递标记位,是通过位图的结构来传递的

  • 每一个宏标记,一般只要有一个比特位是1,并且与其他宏对应的值,不能重叠,这样就能通过按位与传递多个不同参数!!!

#include <stdio.h>

// flags标记位,通过不同的标示码来执行不同的代码
#define PRINT_A 0x1  // 0001
#define PRINT_B 0X2  // 0010
#define PRINT_C 0x4  // 0100
#define PRINT_D 0x8  // 1000
#define PRINT_DEF 0x0

void Show(int flags)
{
    if (flags & PRINT_A) printf("PRINT_A: hello A!!!\n");
    if (flags & PRINT_B) printf("PRINT_B: hello B!!!\n");
    if (flags & PRINT_C) printf("PRINT_C: hello C!!!\n");
    if (flags & PRINT_D) printf("PRINT_D: hello D!!!\n");
    if (flags == PRINT_DEF) printf("PRINT_DEF: hello default!!!\n");
}

int main()
{
    // 通过按位与传递多个标示码
    Show(PRINT_DEF); 
    Show(PRINT_A); 
    Show(PRINT_B);
    Show(PRINT_A | PRINT_C); 
    Show(PRINT_A | PRINT_B | PRINT_C | PRINT_D);
    return 0;
}
  • mode_t:若文件不存在,创建新的文件时,需要指定文件拥有者、所属组和other的权限

模拟以只写的方式打开文件 – 标记位:O_WRONLY、O_CREAT、O_TRUNC

  • 只读方式打开
  • 只写涉及:文件不存在则创建新文件
  • 文件已经存在,重新打开会将文件长度设置0
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fp = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC);
    if (fp < 0)
    {
        perror("fp");
    }
    close(fp);
    return 0;
}

注意看log.txt的读写可执行权限!!!

在这里插入图片描述

使用mode_t参数调整新建文件的权限问题!!!

[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fp = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fp < 0)
    {
        perror("fp");
    }
    close(fp);
    return 0;
}

注意,调整后的权限会受bash进程默认的文件权限掩码的影响!!!

在这里插入图片描述

可以使用umask函数在程序内改变该进程的权限掩码

#include <sys/types.h>
#include <sys/stat.h>

mode_t umask(mode_t mask);
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    // 将该进程的文件全线掩码修改成0
    umask(0);
    int fp = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fp < 0)
    {
        perror("fp");
    }
    close(fp);
    return 0;
}

在这里插入图片描述


🍦3.2、close(关闭文件)

close - 关闭一个文件描述符,使它不在指向任何文件,并且可以在新的文操作中被再次使用

#include <unistd.h>
int close(int fd);
  • 返回值:返回0表示关闭成功,返回-1表示关闭时出错

  • fd:文件描述符,open函数的返回值


🍧3.3、read(读文件)

read - 在文件描述符上执行读操作

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
  • read() 从文件描述符fd中读取count字节的数据并放入从buf开始的缓冲区中

  • 如果count为零,read()返回0,不执行其他任何操作

  • 如果 count 大于SSIZE_MAX,那么结果将不可预料

  • 返回值:成功时返回读取到的字节数,发生错误时返回-1,并置errno为相应值

[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main()
{
    // 将该进程的文件全线掩码修改成0
    umask(0);
    // 以读的方式打开文件
    int fd = open("log.txt", O_RDONLY, 0666);
    if (fd < 0)
    {
        perror("fp");
    }
    char buffer[128];
    ssize_t ret = read(fd, buffer, sizeof(buffer));
    if (ret == -1)
    {
        perror("ret");
    }
    printf("%s", buffer);
    printf("ret: %d\n", ret);
    close(fd);
    return 0;
}

在这里插入图片描述


🍨3.4、write(写文件)

write -在一个文件描述符上执行写操作

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
  • write向文件描述符fd所引用的文件中写入,从buf开始的缓冲区中count字节的数据

  • 返回值:成功时返回所写入的字节数(若为零则表示没有写入数据),错误时返回-1

[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    // 将该进程的文件全线掩码修改成0
    umask(0);
    // 以写的方式打开
    int fd = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
    if (fd < 0)
    {
        perror("fp");
    }
    char buffer[] = "hello world!!!\n";
    int cnt = 1;
    while (cnt <= 5)
    {
        int ret = write(fd, buffer, sizeof(buffer));
        if (ret == -1)
        {
            perror("ret");
        }
        ++cnt;
    }
    close(fd);
    return 0;
}

在这里插入图片描述


🌹4、文件描述符

🍩4.1、为什么文件描述符从3开始???

如何理解文件描述符,前面遇到的各种文件系统接口都要使用它

[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>

int main()
{
    
    // 将该进程的文件全线掩码修改成0
    umask(0);
    int fd1 = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
    int fd2 = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
    int fd3 = open("log.txt", O_WRONLY | O_TRUNC | O_CREAT, 0666);
    if (fd1 < 0) printf("fd1");
    if (fd2 < 0) printf("fd2");
    if (fd3 < 0) printf("fd3");

    printf("fd1: %d\n", fd1);
    printf("fd2: %d\n", fd2);
    printf("fd3: %d\n", fd3);
    return 0;
}

在这里插入图片描述

为什么文件描述符是从3开始呢?0、1、2去哪里了呢???

  • 因为程序运行时,会默认打开三个流:stdin & stdout & stderr
  1. 0号描述符:标准输入流,键盘
  2. 1号描述符:标准输出流,显示器
  3. 2号描述符:标准错误流,显示器
  • 0、1、2就是标准输入输出错误流!!!
#include <stdio.h>
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;
  • FILE*是一个文件指针

  • FILE是C库提供的结构体,它封装了很多成员,其中也必定包含了“fd描述符”

  • C库函数调用文件相关系统接口,必须通过fd描述符来实现

验证标准I/O的存在

// 验证stdin的存在 -- 将键盘输入的数据保存到buf数组中,并且回显到显示器
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>

int main()
{
    char buf[1024];
    ssize_t ret = read(0, buf, sizeof(buf));
    if (ret < 0)
    {
        printf("ret");
    }
    else
    {
        buf[ret - 1] = '\0';
        printf("%s\n", buf);
    }
    return 0;
}
[lyh_sky@localhost test]$ ./myfile 
abcdef
abcdef
[lyh_sky@localhost test]$ 
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>

int main()
{
    // 验证标准输出流,将buf数据回显到显示器上
    char buf[] = "hello world!!!\n";
    ssize_t ret = write(1, buf, sizeof(buf));
    if (ret < 0)
    {
        printf("ret");
    }
    return 0}
[lyh_sky@localhost test]$ ./myfile 
hello world!!!

标准输出和错误打印的一样,这里不演示了!!!

// 打印标准输入输出错误流的文件描述符
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>

int main()
{
	// 验证012和stdin,stdout,stderr的对应关系
    printf("stdin: %d\n", stdin->_fleno);
    printf("stdout: %d\n", stdout->_fileno);
    printf("stderr: %d\n", stderr->_fileno);
    return 0}
[lyh_sky@localhost test]$ ./myfile 
stdin: 0
stdout: 1
stderr: 2

🍪4.2、fd描述符底层原理

0, 1, 2, 3, 4, 5…,你见过什么样的数据,是这样子的???

  • 我们常见的数组下标,就是从0开始的!!!

  • 打开文件时,是通过调用相关文件系统函数,fd描述符是OS提供的返回值!!!

一个进程可以打开多个文件吗???

  • 可以的,所以在内核中,进程 :打开的文件比例是1 :n

  • 所以在系统运行中,有可能会存在大量的文件被打开!

  • 那么操作系统如何管理这些被打开的文件呢?先描述,再组织!!!

一个文件被打开,在内核中,要创建被打开文件的内核数据结构 – 先描述

struct file
{
	// 文件大部分内容和属性
	// ....
	struct file* next;
	struct file* prev;
};

进程如何与被打开的文件建立映射关系呢???

  • 文件被打开后,它的数据和属性会被加载到struct file中

  • 进程中包含一个struct files_struct*的结构体指针,该指针指向多文件的结构体(struct files_struct )

  • files_struct里面包含了一个数组,该数组是一个结构体指针数组(struct file* fd[])

  • 该数组里面存储着struct file的地址,该数组的下标就是文件描述符!!!

文件被打开后,struct file会被分配到指针数组中为空的位置,然后OS会通过open返回该位置的下标给用户

  • 拿到文件描述符后,用户可以通过文件描述符来进行文件的读写操作!!!

在这里插入图片描述


🍬4.3、Linux下一切皆文件

文件描述符0,1,2对应的是键盘,显示器,显示器,它们都是硬件,被打开时,都是用struct file来标识的!

如何使用C语言,实现面向对象(类)呢?

// 我们可以使用函数指针来实现!!! -- 实现一个文件类
#include <stdio.h>

struct file
{
	// 文件的数据和属性
	.....
	// 文件的基本读写操作 -- 函数指针实现
	void (*readp)(struct file* filep, int fd...);
	void (*writep)(struct file* filep, int fd...);
};

// C++的类中有一个隐藏的this指针,相当于这里的struct file*
void read(struct file* filep, int fd...)
{}

void write(struct file* filep, int fd...)
{}

int main()
{
	struct file f = {/*文件的数据和属性...*/, read, write};
	f.readp(&f, ...);
	f.writep(&f, ...);
	return 0;
}
  • 硬件包含:磁盘、键盘、显示器、网卡等等其他硬件

  • 驱动可以提供不同设备的读写操作

  • 当要对硬件进行读写时,OS会创建一个struct file来标识对应的硬件

  • 对硬件进行读写操作,只需要调用struct file里面封装的读写方法即可

  • struct file是存储在文件描述符指针数组里面的

图解:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


🍫4.4、文件描述符的分配规则

分配规则:

  • 从头变量fd_array结构体指针数组,找到一个最小的,没有被使用的下标

  • 分配给新打开的文件(struct file)

验证分配规则

[lyh_sky@localhost test]$ ls
log.txt  makefile  myfile  my_files.c
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>

int main()
{
    // 关闭0号文件描述符
    close(0);
    // 以读的方式打开文件,设置权限为0666
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("fd");
        return 1;
    }
    printf("fd: %d\n", fd);
    // 关闭文件
    close(fd);
    return 0;
}

[lyh_sky@localhost test]$ ./myfile 
fd: 0
[lyh_sky@localhost test]$ ls
log.txt  makefile  myfile  my_files.c
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>

int main()
{
    // 关闭2号文件描述符
    close(0);
    // 以读的方式打开文件,设置权限为0666
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("fd");
        return 1;
    }
    printf("fd: %d\n", fd);
    // 关闭文件
    close(fd);
    return 0;
}

[lyh_sky@localhost test]$ ./myfile 
fd: 2
[lyh_sky@localhost test]$ ls
log.txt  makefile  myfile  my_files.c
[lyh_sky@localhost test]$ cat my_files.c 
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/stat.h>

int main()
{
    // 关闭1号文件描述符
    close(1);
    // 以读的方式打开文件,设置权限为0666
    int fd = open("log.txt", O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0)
    {
        perror("fd");
        return 1;
    }
    printf("fd: %d\n", fd);
    // 关闭文件
    close(fd);

	// 后面讲为什么要刷新缓冲区才会打印
	fflsh(stdout);
    return 0;
}

[lyh_sky@localhost test]$ ./myfile 
[lyh_sky@localhost test]$ cat log.txt
fd: 1

总结:经过以上测试发现,就是按规则进行分配的!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/26412.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python 另一种将内容写入记事本的方式

目录 问题描述&#xff1a; 方案一 &#xff08;常见的写法&#xff09;&#xff1a; 方案二&#xff1a; 问题描述&#xff1a; 如下图所示&#xff0c;欲将下图内容写入一个.txt 方案一 &#xff08;常见的写法&#xff09;&#xff1a; 使用f.write()函数&#xff0c; 如…

如何使用CompletableFuture

目录 一、CompletableFuture是什么 二、CompletableFuture用法 2.1、创建CompletableFuture 2.1.1、直接创建 2.1.2、创建一个使用指定数据作为结果的已结束的CompletableFuture 2.1.3、通过执行异步任务获取CompletableFuture 2.2、获取任务结果 2.3、消费结果 2.3.1、…

云原生技术中台 CNStack2.0 正式发布

作者&#xff1a;奥陌 11 月 5 日&#xff0c;在 2022 杭州 云栖大会上&#xff0c;云原生技术中台 CNStack2.0 正式发布。 阿里巴巴资深技术专家 谢吉宝介绍 CNStack2.0 企业在数字化转型的过程中&#xff0c;一部分问题得到了解决&#xff0c;但随着 IT 水平的不断提升&am…

【Milvus的人脸检索】

0. 介绍 在上一篇文章中&#xff0c;介绍了milvus提供的以图搜图的样例&#xff0c;这篇文章就在以图搜图样例的基础上进行修改&#xff0c;实现人脸检索。 常见的人脸任务&#xff0c;分为人脸检测、人脸识别、人脸对比和人脸检索&#xff0c;其中人脸检索的含义是&#xff…

点成分享 | 蛋白质浓度测定之考马斯亮蓝(Bradford)法

蛋白质是组成生物细胞、组织的重要成分&#xff0c;生物的所有生命活动都离不开蛋白质的参与。蛋白质是生命的物质基础&#xff0c;是构成细胞的基本有机物&#xff0c;是生命活动的主要承担者。生物材料中蛋白质含量的测定是生物学研究中最重要也是最基本的实验操作之一&#…

【微机接口】串行通信基础

计算机通信:CPU与外部的信息交换 并行通信&#xff1a;数据所有位同时被传输 串行通信&#xff1a;数据被逐位顺序传送 串行通信类型&#xff1a; 串行异步通信&#xff1a;一个字符用起始位和停止位来完成收发同步。 串行同步通信&#xff1a;采用同步字符来完成收发双方同…

营丘福稻品牌山东大米 国稻种芯·中国水稻节:淄博高青招牌

营丘福稻品牌山东大米 国稻种芯中国水稻节&#xff1a;淄博高青招牌 淄博市广播电视台新生活 大众网海报新闻记者 董玉歌 淄博报道) 新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健…

【电源专题】案例:电源芯片规格书大标题写5A那是能输出5A吗?

这个案例是找到了问题点后再去审查规格书发现规格书里竟然有写明!只是最初始不是我导入的芯片就是了(其实就算是我导入的,以前也没有测试方法和手段能发现异常),而且这个芯片已经用了好久好久了,现在都停产了,买不到了。 从下图所示的规格书大标题中可以看到同步升压芯片…

苹果Mac电脑L2TP连接公司内部网络失败解决方案

苹果Mac电脑L2TP连接公司内部网络 苹果Mac系统在创建VPN连接时&#xff0c;一直提示&#xff1a;L2TP-VPN服务器没有响应。请尝试重新连接。如果仍然有问题&#xff0c;请验证你的设置并与管理员联系。 我们在添加VPN的时候需要填写机器认证中的共享秘钥&#xff0c;我这里填…

详解容灾恢复过程中跨数据中心级的关键故障切换

【摘要】容灾设计过程当中需要考虑的故障切换的场景有很多,数据中心内部的高可用切换不在本次讨论范围之内,我们讨论的是容灾恢复过程中的关键跨数据中心级的故障切换场景,从网络层到存储层都会涉及到。(文中涉及相关技术产品参数请以官网最新发布为准) 1. 容灾设计需要进…

c++异常处理

目录 1.c异常的由来 2.怎么使用异常来解决问题 3.异常安全 4.异常规范 5.异常处理的优缺点 1.c异常的由来 在c语言中&#xff0c;如果程序出现了错误&#xff0c;采用的是返回错误码的方式。最常见的&#xff1a; int main() {return 0; } 这里的return 0的0就是表示返…

Kubernetes学习(一)入门及集群搭建

一、简介 1.简介 Kubernetes 最初源于谷歌内部的Borg&#xff0c;Kubernetes 是一个全新的基于容器技术的分布式架构解决方案。 包含几个基本功能&#xff1a; 1.将应用水平扩容到多个集群 2.为扩容的实例提供负载均衡策略 3.提供基本的健康检查和自愈能力 4.实现任务的…

电机无位置控制方法研究

无位置控制方法研究 1.无位置控制技术研究现状 2.反电动势过零检测法 3.反电动势三次谐波积分检测法 4.续流二极管法 5.磁链法 6.扩展卡尔曼滤波算法&#xff08;EKF&#xff09; 7.基于扩展卡尔曼滤波算法&#xff08;EKF&#xff09;的转速及位置估算 8.电感测量法 9.涡流效…

LeetCode-878. 第 N 个神奇数字【数学,二分查找,找规律】

LeetCode-878. 第 N 个神奇数字【数学&#xff0c;二分查找&#xff0c;找规律】 题目描述&#xff1a;解题思路一&#xff1a;二分答案容斥原理。给定一个上下界&#xff0c;然后依次增大下界或者减小上界&#xff0c;直到只剩一个答案。容斥原理是&#xff0c;加上两个集合&a…

六、TCP实现聊天

客户端&#xff1a; 连接服务器 Socket(address,port)发送消息 import java.io.IOException; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets;/*** 客…

LabVIEW使用VI Snippets存储和共享重用代码段

LabVIEW使用VI Snippets存储和共享重用代码段 VI Snippets是存储、共享和重用LabVIEW代码小部分的新方式。VI代码段将截图的可移植性与VI文件的功能结合在标准PNG图像中嵌入LabVIEW代码。当将VI代码段PNG图像拖到框图上时&#xff0c;它会将代码片段作为图形代码拖放&#xff…

【C++】重载运算符+-=>/*[]==++-- MyString 智能指针(* ->)

目录 15.重载运算符 15.1 eg.Person 15.2 eg.MyString 15.3 智能指针 15.重载运算符 定义&#xff1a;给原有的运算符赋予新的意义。 为什么重载<<或>>一定要用友元&#xff1f; 如果是重载双目操作符&#xff08;即为类的成员函数&#xff09;&#xff0c;就…

10年老码农亲授:什么是分布式系统

首先&#xff0c;分布式系统是在硬件成本日益提高&#xff0c;或者单机提升的成本过于昂贵而程序的问题得不到解决时&#xff0c;为了解决更加高效、内容量更大的数据而采取的一种解决手段。 而分布式系统又分为两个部分&#xff0c;计算和存储&#xff0c;而准确来说这两部分…

知行之桥EDI系统2022版Tomcat部署

1.首先需要下载Tomcat&#xff0c;可在Tomcat官网获取&#xff0c;本部署步骤以apache-tomcat-9.0.67.tar.gz为例&#xff0c;通过XFTP将该包放在服务器上的指定位置&#xff0c;如/opt/test 进入/opt/test文件夹后&#xff0c;在命令行执行以下命令对该包进行解压缩 tar -zxv…

大数据毕设 - 公交数据分析与可视化系统(大数据 python flask)

文章目录0 前言1 课题背景2 具体实现3 Flask框架4 ECharts可视化工具5 最后0 前言 &#x1f525; Hi&#xff0c;大家好&#xff0c;这里是丹成学长的毕设系列文章&#xff01; &#x1f525; 对毕设有任何疑问都可以问学长哦! 这两年开始&#xff0c;各个学校对毕设的要求越…