task_struct、files_struct

每个进程在Linux内核中都有一个task_struct结构体来维护进程相关的信息,称为进程描述符(Process Descriptor),而在操作系统理论中称为进程控制块(PCB,Process Control Block)。task_struct中有一个指针指向files_struct结构体,称为文件描述符表,其中每个表项包含一个指向已打开的文件的指针,如下图所示。

files_struct位于内核,用户程序不能直接访。但是可以使用files_struct的索引(即0、1、2、3这些数字),这些索引就称为文件描述符(File Descriptor),用int型变量保存。当调用open打开一个文件或创建一个新文件时,内核分配一个File Descriptor(files_struct的一个索引)并返回给用户程序,files_struct中某一表项的指针指向新打开的文件。当读写文件时,用户程序把File Descriptor传给readwrite,内核根据File Descriptor找到files_struct中相应的表项,再通过表项中的指针找到相应的文件。

程序启动时会自动打开三个文件:标准输入、标准输出和标准错误输出。在C标准库中分别用FILE *指针stdinstdoutstderr表示。这三个文件的描述符分别是0、1、2,保存在相应的FILE结构体中。头文件unistd.h中有如下的宏定义来表示这三个文件描述符:

#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2

C标准库函数是C标准的一部分,而Unbuffered I/O函数是UNIX标准的一部分,在所有支持C语言的平台上应该都可以用C标准库函数(除了有些平台的C编译器没有完全符合C标准之外),而只有在UNIX平台上才能使用Unbuffered I/O函数,所以C标准I/O库函数在头文件stdio.h中声明,而readwrite等函数在头文件unistd.h中声明。在支持C语言的非UNIX操作系统上,标准I/O库的底层可能由另外一组系统函数支持,例如Windows系统的底层是Win32 API,其中读写文件的系统函数是ReadFileWriteFile

C标准I/O库函数与Unbuffered I/O函数

现在看看C标准I/O库函数是如何用系统调用实现的。

fopen(3)

调用open(2)打开指定的文件,返回一个文件描述符(就是一个int类型的编号),分配一个FILE结构体,其中包含该文件的描述符、I/O缓冲区和当前读写位置等信息,返回这个FILE结构体的地址。

fgetc(3)

通过传入的FILE *参数找到该文件的描述符、I/O缓冲区和当前读写位置,判断能否从I/O缓冲区中读到下一个字符,如果能读到就直接返回该字符,否则调用read(2),把文件描述符传进去,让内核读取该文件的数据到I/O缓冲区,然后返回下一个字符。注意,对于C标准I/O库来说,打开的文件由FILE *指针标识,而对于内核来说,打开的文件由文件描述符标识,文件描述符从open系统调用获得,在使用readwriteclose系统调用时都需要传文件描述符。

fputc(3)

判断该文件的I/O缓冲区是否有空间再存放一个字符,如果有空间则直接保存在I/O缓冲区中并返回,如果I/O缓冲区已满就调用write(2),让内核把I/O缓冲区的内容写回文件。

fclose(3)

如果I/O缓冲区中还有数据没写回文件,就调用write(2)写回文件,然后调用close(2)关闭文件,释放FILE结构体和I/O缓冲区。

以写文件为例,C标准I/O库函数(printf(3)putchar(3)fputs(3))与系统调用write(2)的关系如下图所示。

图 28.1. 库函数与系统调用的层次关系

openreadwriteclose等系统函数称为无缓冲I/O(Unbuffered I/O)函数,因为它们位于C标准库的I/O缓冲区的底层。用户程序在读写文件时既可以调用C标准I/O库函数,也可以直接调用底层的Unbuffered I/O函数,那么用哪一组函数好呢?

  • 用Unbuffered I/O函数每次读写都要进内核,调一个系统调用比调一个用户空间的函数要慢很多,所以在用户空间开辟I/O缓冲区还是必要的,用C标准I/O库函数就比较方便,省去了自己管理I/O缓冲区的麻烦。
  • 用C标准I/O库函数要时刻注意I/O缓冲区和实际文件有可能不一致,在必要时需调用fflush(3)
  • 我们知道UNIX的传统是Everything is a file,I/O函数不仅用于读写常规文件,也用于读写设备,比如终端或网络设备。在读写设备时通常是不希望有缓冲的,例如向代表网络设备的文件写数据就是希望数据通过网络设备发送出去,而不希望只写到缓冲区里就算完事儿了,当网络设备接收到数据时应用程序也希望第一时间被通知到,所以网络编程通常直接调用Unbuffered I/O函数。

原文地址:https://www.cnblogs.com/kelamoyujuzhen/p/9053084.html

时间: 2024-11-06 12:32:59

task_struct、files_struct的相关文章

进程上下文、中断上下文以及中断程序的特点

进程上下文VS中断上下文 内核空间和用户空间是现代操作系统的两种工作模式,内核模块运行在内核空间,而用户态应用程序运行在用户空间.它们代表不同的级别,而对系统资源具有不同的访问权限.内核模块运行在最高级别(内核态),这个级下所有的操作都受系统信任,而应用程序运行在较低级别(用户态).在这个级别,处理器控制着对硬件的直接访问以及对内存的非授权访问.内核态和用户态有自己的内存映射,即自己的地址空间. www.2cto.com 处理器总处于以下状态中的一种: 1.内核态,运行于进程上下文,内核代表进程

进程上下文、中断上下文及原子上下文

谈论进程上下文 .中断上下文 . 原子上下文之前,有必要讨论下两个概念: a -- 上下文 上下文是从英文context翻译过来,指的是一种环境.相对于进程而言,就是进程执行时的环境: 具体来说就是各个变量和数据,包括所有的寄存器变量.进程打开的文件.内存信息等. b -- 原子  原子(atom)本意是"不能被进一步分割的最小粒子",而原子操作(atomic operation)意为"不可被中断的一个或一系列操作" : 一.为什么会有上下文这种概念 内核空间和用户

CGroup 介绍、应用实例及原理描述(已发表于IBM开发者论坛)

原文请查看:http://www.ibm.com/developerworks/cn/linux/1506_cgroup/index.html CGroup 介绍 CGroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制.记录.隔离进程组 (process groups) 所使用的物力资源 (如 cpu memory i/o 等等) 的机制.2007 年进入 Linux 2.6.24 内核,CGroups 不是全新创造的,它将进程管理从 cpuset 中剥离

linux进程的创建、执行和消亡

在linux系统中,第一个进程是系统固有的,与生俱来的或者说是由内核的设计者安排好了的,内核在引导并完成了基本的初始化以后,就有了系统第一进程(实际上是内核线程).除此之外,所有其他的进程和内核线程都有这个原始进程或其子孙进程所创建,都是这个原始进程的后代. linux将进程的创建和执行分成两步. 第一步是从已存在的"父进程"中像细胞分裂一样地复制出一个"子进程".复制出来的子进程有自己的task_struct结构和系统空间堆栈,单与父进程共享其他所有的资源.例如要

《UNIX环境高级编程》笔记——3.文件IO

一.引言 说明几个I/O函数:open.read.write.lseek和close,这些函数都是不带缓冲(不带缓冲,只调用内核的一个系统调用),这些函数不输入ISO C,是POSIX的一部分: 多进程共享资源(包括文件)时,会有很多额外的烦恼,需要对共享资源.原子操作等概念深入理解,需要理解涉及的内核有关数据结构,这些数据结构对理解文件.共享有重要作用: 最后介绍dup.fcntl.sync.fsync和ioctl函数. 二.文件描述符 open或creat文件时,内核--文件描述符fd-->

Linux进程的创建函数fork()及其fork内核实现解析

进程的创建之fork() Linux系统下,进程可以调用fork函数来创建新的进程.调用进程为父进程,被创建的进程为子进程. fork函数的接口定义如下: #include <unistd.h> pid_t fork(void); 与普通函数不同,fork函数会返回两次.一般说来,创建两个完全相同的进程并没有太多的价值.大部分情况下,父子进程会执行不同的代码分支.fork函数的返回值就成了区分父子进程的关键.fork函数向子进程返回0,并将子进程的进程ID返给父进程.当然了,如果fork失败,

文件系统VFS数据结构(超级块 inode dentry file)(收集整理)

Linux虚拟文件系统四大对象: 1)超级块(super block) 2)索引节点(inode) 3)目录项(dentry) 4)文件对象(file) 一个进程在对一个文件进行操作时各种对象的引用过程如下: 通过task_struct得到files_struct,然后通过文件描述符(int fd)获得相应的文件对象(file **fd),接下来获得目录项对象(dentry),最后得到索引节点对象(inode),该对象就包括具体操作该文件的相关操作,这些操作是从超级对象块中继承而来的.它与具体的

Linux Kernel File IO Syscall Kernel-Source-Code Analysis(undone)

目录 0. 引言 1. open() syscall 2. close() syscall 0. 引言 在linux的哲学中,所有的磁盘文件.目录.外设设备.驱动设备全部被抽象为了"文件"这个概念,所以本文提到的"File IO"适用于linux下所有的IO操作,需要明白的的,本文分析的是linux下的IO系统调用对应的内核源代码,linux下每一个系统调用都有对应的内核源代码,而我们在ring3常用的glib c的编程所有的c库API,它们只是对系统调用的一个封装

Linux Kernel中获取当前目录方法(undone)

目录 0. 引言 1. 基于进程内存镜像信息struct mm_struct获取struct path调用d_path()获取当前进程的"绝对路径" 2. 基于文件描述符(fd).task_struct调用d_path()获取当前进程的"当前目录" 3. 基于dentry.vfsmount调用d_path()获取当前进程的"当前目录" 4. 基于get_fs_pwd获取当前目录 0. 引言 本文涉及的是ring0下的获取当前进程工作目录的方法,L