进程就是正在执行的程序或代码,文件也是在进程中被处理的

文件也是在进程中被处理的,vim处理的对象就是一个代码文件,回到顶部,Cache,内存管理,进程管理,进程就是正在执行的程序或代码,只有当程序的代码被加载到内存中

图片 2

Linux之根本中的文件系统

文件陈诉符

相似谈起文件和文件系统的时候,都会无形中的想到它们存在于磁盘上。不过,磁盘只好被动的囤积,却无法积极去管理公事;要管理公事,只可以把它们的多寡复制到内部存款和储蓄器中,交给CPU管理,管理好的多寡先写入内部存款和储蓄器,再传回到磁盘。那么,操作系统怎样在内部存款和储蓄器中管理种种文件呢?即运转时文件系统在基本中的表示。
我们知晓,进度是操作系统一分配配财富的中央单位,文件也是在经过中被拍卖的。例如用vim写代码,那么vim那几个顺序就产生操作系统中的二个进程,vim处理的对象正是贰个代码文件。操作系统用PCB来管理进度,PCB从代码的角度看正是task_struct结构体,这一个结构体中有一个指针指向files_struct结构体,那就是所谓的公文陈说符表,其代码如下:

struct files_struct {
         atomic_t count;        /* 共享该表的进程数 */
         rwlock_t file_lock;     /* 保护以下的所有域,以免在tsk->alloc_lock中的嵌套*/
         int max_fds;           /*当前文件对象的最大数*/
         int max_fdset;        /*当前文件描述符的最大数*/
         int next_fd;          /*已分配的文件描述符加1*/
         struct file ** fd;      /* 指向文件对象指针数组的指针 */
         fd_set *close_on_exec;  /*指向执行exec(  )时需要关闭的文件描述符*/
         fd_set *open_fds;     /*指向打开文件描述符的指针*/
         fd_set close_on_exec_init;/* 执行exec(  )时需要关闭的文件描述符的初值集合*/
         fd_set open_fds_init;  /*文件描述符的初值集合*/
         struct file * fd_array[32];/* 文件对象指针的初始化数组*/
};

这个表是每个进程私有的,每个进程都有一个,它也称为用户打开文件表,这是为了区别系统打开文件表。我们主要关注最后一个成员:struct file * fd_array[32];,这是一个指针数组,数组的每个成员都指向一个file结构体,file结构体中保存着该进程已经打开的文件的信息。由于一个进程可以打开多个文件,所以用一个指针数组来保存它们的信息。对于在数组中有入口地址的每个文件来说,数组的索引就是文件描述符(file descriptor)。通常,数组的第一个元素(索引为0)是进程的标准输入文件,数组的第二个元素(索引为1)是进程的标准输出文件,数组的第三个元素(索引为2)是进程的标准错误文件。这三个文件是操作系统为每个进程默认打开的,如果进程需要打开其他文件,那么进程描述符将从3开始。文件描述符是系统的一个重要资源,虽然说系统内存有多少就可以打开多少的文件描述符,但是在实际实现过程中内核是会做相应的处理的,一般最大打开文件数会是系统内存的10%(以KB来计算)(称之为系统级限制),查看系统级别的最大打开文件数可以使用sysctl -a | grep fs.file-max命令查看。与此同时,内核为了不让某一个进程消耗掉所有的文件资源,其也会对单个进程最大打开文件数做默认值处理(称之为用户级限制),默认值一般是1024,使用ulimit -n命令可以查看。 在Linux中,进程是通过文件描述符(file descriptors,简称fd)而不是文件名来访问文件的,文件描述符实际上是一个整数。

虚拟文件系统 下面来看一下file结构体,代码如下:

struct file
{
 struct list_head        f_list;    /*所有打开的文件形成一个链表*/
 struct dentry           *f_dentry; /*指向相关目录项的指针*/
 struct vfsmount         *f_vfsmnt; /*指向VFS安装点的指针*/
 struct file_operations  *f_op;     /*指向文件操作表的指针*/
 mode_t f_mode;                     /*文件的打开模式*/
 loff_t f_pos;                      /*文件的当前位置*/
 unsigned short f_flags;            /*打开文件时所指定的标志*/
 unsigned short f_count;            /*使用该结构的进程数*/
 unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin; 
/*预读标志、要预读的最多页面数、上次预读后的文件指针、预读的字节数以及预读的页面数*/
 int f_owner;                       /* 通过信号进行异步I/O数据的传送*/
 unsigned int         f_uid, f_gid; /*用户的UID和GID*/
 int                 f_error;       /*网络写操作的错误码*/
 unsigned long f_version;          /*版本号*/
 void *private_data;              /* tty驱动程序所需 */
};

file结构体并不与实际的文件一一对应,例如,当一个进程多次打开同一文件的时候,都会分配一个不同的file结构体以及相应的文件描述符,尽管这些file结构体最终都指向同一个实际物理文件。可以看出,内存中的文件和磁盘中的文件实现方式还是不太一样的,内存中的文件是动态的,因为要不停的读写,所以只是一份拷贝,所有的操作针对的只是这份拷贝,操作完成后,把结果写回到磁盘文件中;写回这个动作执行之前,所有的改动只存在与内存中,并不会实时反应到磁盘上,可以用vim和cat同时操作一个文件来验证一下。懂了这个道理之后,再看为啥每次打开文件要分配不同的file结构体:因为每次打开意味着要对同一个文件进行不同的操作,并希望不同操作的结果都能写回到磁盘文件中;如果使用同一个file,那么就有可能后面的操作覆盖掉前面的操作。 覆盖的可能性来自于file结构体的成员loff_t f_pos;它表示文件当前的读写位置。每个文件都有一个32位的数字来表示下一个读写的字节位置,这个数字叫做文件位置。每次打开一个文件,除非明确要求,否则文件位置都被置为0,即文件的开始处,此后的读或写操作都将从文件的开始处执行,可以通过执行系统调用LSEEK(随机存储)对这个文件位置进行修改。所以,如果两次操作是针对同一文件的不同位置,使用同一个file结构体将使它们拥有同样的读写位置,这显然无法达到目的。这还是同一进程的不同操作,更何况不同进程的不同操作呢?于是,每次打开文件几乎都要分配一个新的file结构体。可以说,file结构体主要保存的就是这个读写位置。 上面使用了“几乎”,那就表示有例外情况:生成一个新进程时,子进程要共享父进程的所有信息,包括file结构体,其成员unsigned short f_count;就表示了当前有多少进程在使用这个结构体,只有当它的值为0时,才从内存中销毁这它。 file结构体中的第一个成员是 struct list_head f_list;它使file结构形成一个双链表,称为系统打开文件表,其最大长度是NR_FILE,在fs.h中定义为8192。 那么文件描述符表、系统打开文件表和实际文件之间的关系可以如下图: 然后我们看看file结构体如何指向最终的实际文件,这要涉及到第二个成员:struct dentry *f_dentry; 其代码如下:

struct dentry {
atomic_t d_count;        /* 目录项引用计数器 */
unsigned int d_flags;    /* 目录项标志 */
struct inode  * d_inode;   /* 与文件名关联的索引节点 */
struct dentry * d_parent;       /* 父目录的目录项 */
struct list_head d_hash;        /* 目录项形成的哈希表 */
struct list_head d_lru;         /*未使用的 LRU 链表 */
struct list_head d_child;       /*父目录的子目录项所形成的链表 */
struct list_head d_subdirs;     /* 该目录项的子目录所形成的链表*/
struct list_head d_alias;       /* 索引节点别名的链表*/
int d_mounted;                  /* 目录项的安装点 */
struct qstr d_name;             /* 目录项名(可快速查找) */
unsigned long d_time;           /* 由 d_revalidate函数使用 */
struct dentry_operations  *d_op; /* 目录项的函数集*/
struct super_block * d_sb;      /* 目录项树的根 (即文件的超级块)*/
unsigned long d_vfs_flags;  
void * d_fsdata;                /* 具体文件系统的数据 */
unsigned char d_iname[DNAME_INLINE_LEN]; /* 短文件名 */
};

这个结构体保存了路径信息,包括文件名。我在介绍ln命令时说过,操作系统根据inode号寻找文件,而人类根据文件名,故需要从这个结构体中找到二者之间的对应关系。我们可以利用这两个成员:struct inode * d_inode;和struct qstr d_name;。前者指向了与真正的文件相关的inode结构体,它保存着从磁盘分区的inode读上来信息。至此,进程中文件描述符指向了最终的磁盘文件,我猜file结构体最终要依靠inode结构体写回磁盘。 完。

文件汇报符
一般说到文件和文件系统的时候,都会无意识的想到它们存在于磁盘上。但是,磁盘只好被动的储存,…

阅读目录

回来最上部
系统调用
操作系统的最首要功能是为处理硬件财富和为应用程序开垦人士提供不错的条件,可是计算机体系的各样硬件财富是零星的,因而为了有限辅助每二个进度都能平平安安的试行。管理器设有二种形式:“客户情势”与“内核方式”。一些便于发生安全难点的操作都被限定在独有基本形式下技能够举行,举例I/O操作,修改基址贮存器内容等。而一连客户格局和基础情势的接口称之为系统调用。
应用程序代码运维在顾客方式下,当应用程序须求完成基本方式下的下令时,先向操作系统一发布送调用央求。操作系统收到伏乞后,施行系统调用接口,使处理器步入基础情势。当计算机管理完系统调用操作后,操作系统会让Computer重临顾客形式,继续实行客商代码。
进程的设想地址空间可分为两有的,内核空间和客商空间。内核空间中寄放的是内核代码和多少,而经过的客户空间中存放的是顾客程序的代码和数据。不管是根本空间如故客户空间,它们都处在设想空间中,都以对物理地址的照射。
应用程序中贯彻对文件的操作进程正是超人的连串调用进程。

task_struct数据结构1235-1644

Linux进程的知道

经过的叙说

通俗的讲,进度就是正在施行的前后相继或代码。大家知晓,程序本人正是一群代码,起首的时候存款和储蓄在磁盘上,那时它是静态的、无性命的;独有当程序的代码被加载到内部存款和储蓄器中,代码才有了人命,工夫被CPU动态的推行。

难题是,未来的操作系统能够互相的奉行四个程序,也正是内部存款和储蓄器中同一时候贮存着四个程序的代码,为了方便处理,必供给创立的团队它们。格局便是由操作系统给每段代码增添一些元数据,那一个元数据便是PCB,纵然命调节块。

轻巧明白的是,每一种程序的代码实际上能够分为两局地:指令的数码。指令正是程序代码规定的各样操作;数据就是这几个操作的目标。一个前后相继能够再三被加载到内部存款和储蓄器成为多个经过,譬就好像不时间张开多个vim分别编写制定区别的文件。那么难题是:这时候有要求在内部存款和储蓄器中同临时间寄存多个该程序的下令copy吗?答案是不用的。指令部分被安装为只读且允许系统中正在周转的八个或多少个进程之间能够分享这一代码;而数据部分则被依次进度私有,不得以分享,举个例子各个vim都只可以编辑自个儿的文本。

那么PCB里面皆有些什么呢?

经过id。系统中各样进程有独一的id,在C语言中用 pid_t
类型表示,其实就是叁个非负整数。
进度的意况,有运营、挂起、甘休、尸鬼等意况。
进度切换时供给保留和回复的局部CPU存放器。 描述设想地址空间的新闻。
描述调整终端的新闻。 当前工作目录(Current Working Directory)。 umask
掩码。 文件叙述符表,包蕴众多对准 file 结构体的指针。 和非频限信号有关的新闻。
客商id和组id。 调整终端、Session和进度组。
进度能够使用的财富上限(Resource Limit)。

看得出,操作系统为了调控进程,PCB的组织依旧挺复杂的。 进度的创办

时有时据悉“成立叁个进度”,那毕竟是怎么回事呢?首先能想到的是,进程不是美猴王,不或许自个儿蹦出来,断定是人家“生”的。Linux中,进度是由父进度创造的,正确的说,是父进度中的代码的授命部分积极利用了创办进程的函数fork(),然后二个子历程就被“生”了出去。fork函数怎么着职业的吗?由于各样进程都有贰个PCB,所以它首先要跟操作系统申请贰个PCB(PCB是个其余),然后分配新历程内部存款和储蓄器,接着copy父进度的代码。实际上,fork正是很懒的复制了一晃父进度,相当于说,fork函数调用进程中,内部存款和储蓄器中会会产出五个差不离一样的过程,当然除了进度号(它是举世无双的)。进程的复制作而成功后,四个经过都有贰个fork函数等待再次来到(只顾,是重临,因为fork函数本人也是一条一条的代码,前边的一某个形成复制功效,子进度出现后,就到了回去的那部分代码了),而它们的回到结果是不一致的(操作系统来支配重临结果):父进度中的fork重临子进度的pid;而子进度中的fork再次来到0;假诺fork失利,重临的是-1。

fork只是成立出了四个大概一致的进度,它们运转的是同一的代码,那和一早先说的首肯同样,因为我们成立一个新进度多数是用来实践新程序代码的。那时大家须要exec类函数,当进度调用一种exec函数时,该进程的程序代码完全被新程序替换,从新程序的启航例程初始进行。调用exec并不创立新历程,所以调用exec前后该进度的id并未改观。如若调用成功则加载新的顺序从起步代码初始推行,不再回来,要是调用出错则赶回-1,所以exec函数独有出错的重回值而从未成功的再次来到值。exec
系统调用实践新程序时会把命令行参数和条件变量表传递给 main
函数。情状变量表是进程所处系统意况的三个描述,一段代码要符合规律试行应当要运用种种系统能源,碰着变量表正是对它的一个架空。然则,exec类函数是要求显式调用的,子进度不会再接再砺加载新的程序代码!所以,一般是在父进程的代码中,依照fork的再次来到值写八个支行,子进程的分段中显式调用exec。
进度的暂息

二个历程在停止时会关闭全体文件描述符,释放在客商空间分配的内部存储器,但它的PCB还保留着,操作系统在在那之中保存了有的音信:要是是正规终止则保留着退出状态,假设是至极终止则保留着产生该进程终止的非信号是哪个。那一个过程的父进度能够调用
wait 或 waitpid
获取这几个新闻,然后通透到底清除掉这些历程。我们领略四个历程的淡出状态能够在Shell中用新鲜变量
$? 查看,因为Shell是它的父进程,当它终止时Shell调用 wait 或 waitpid
获得它的淡出状态同一时间透顶清除掉那几个过程。

设若一个经太早就终止,但是它的父进程尚未调用 wait 或 waitpid
对它举办清理,那时的进度情形叫做尸鬼(Zombie)进度。任何进度在刚停下时都以僵尸进度,寻常意况下,尸鬼进度都立时被父进度清理了。

若果四个父进度终止,而它的子进度还留存(那一个子进程也许仍在运作,大概曾经是活死人进度了),则那个子进程的父进度改为
init 过程。 init 是系统中的二个奇怪进程,平日程序文件是 /sbin/init
,进度id是1,在系统运营时担任运维各类系统服务,之后就担负清理子进度,只要有子进度终止,
init 就能够调用 wait 函数清理它。

活死人进度是无法用 kill 命令清除掉的,因为 kill
命令只是用来终止进程的,而活死人进程早已终止了。所以一颗可行的方法是,kill其父进度。

wait 和 waitpid 函数的原型是:

#include 
#include 
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

若调用成功则返回清理掉的子进程id,若调用出错则返回-1。父进程调用 wait 或 waitpid 时可能会: + 阻塞(如果它的所有子进程都还在运行)。 + 带子进程的终止信息立即返回(如果一个子进程已终止,正等待父进程读取其终止信息)。 + 出错立即返回(如果它没有任何子进程)。

进度的叙说
通俗的讲,进度就是正值实施的前后相继或代码。大家驾驭,程序本人便是一批代码,初步的时候存款和储蓄在磁盘上,那…

系统调用

操作系统的十分重要意义是为治本硬件财富和为应用程序开采职员提供突出的条件,可是计算机连串的种种硬件能源是个其余,因而为了确定保障每一个历程都能平安的实行。管理器设有两种形式:“顾客格局”与“内核格局”。一些便于产生安全主题材料的操作都被界定在唯有基本方式下才方可实行,比方I/O操作,修改基址贮存器内容等。而三番五次客商方式和根本情势的接口称之为系统调用。

应用程序代码运转在客户方式下,当应用程序要求贯彻基础形式下的指令时,先向操作系统一发布送调用必要。操作系统收到央浼后,实施系统调用接口,使管理器进入基础方式。当Computer管理完系统调用操作后,操作系统会让Computer重回客户方式,继续试行客户代码。

经过的设想地址空间可分为两有的,内核空间和客户空间。内核空间中寄放的是内核代码和多少,而经过的客户空间中贮存的是顾客程序的代码和数量。不管是根本空间照旧顾客空间,它们都处于虚构空间中,都以对物理地址的炫耀。

应用程序中完成对文本的操作进程正是头角峥嵘的系统调用进程。

 

再次回到最上端

3、file结构一方面可从f_dentry链接到目录项模块以及inode模块,获取具备和文书有关的新闻,另一方面链接file_operations子模块,当中蕴涵全部能够使用的种类调用函数,进而最后成就对文本的操作。那样,从进度到进度的文本陈述符表,再涉及到已开垦文件列表上相应的文件结构,进而调用其可进行的体系调用函数,实现对文本的各类操作。
经过 vs 文件列表 vs Inode
1、多个进程能够何况针对三个开垦文件对象(文件列表表项),例如父进度和子进度间分享文件对象;
2、贰个经过能够频仍开采三个文书,生成差异的公文描述符,每一个文件呈报符指向不一致的文书列表表项。可是出于是同五个文件,inode唯一,所以这一个文件列表表项都对准同三个inode。通过如此的章程达成文件分享(分享同贰个磁盘文件);

struct task_struct {
1236    volatile long state;    运行状态/* -1 unrunnable, 0 runnable, >0 stopped */
1237    void *stack;  进程的内核堆栈
1238    atomic_t usage;   
1239    unsigned int flags; /* per process flags, defined below */
1240    unsigned int ptrace;
#ifdef CONFIG_SMP  多处理器时会用到
1243    struct llist_node wake_entry;
1244    int on_cpu;
1245    struct task_struct *last_wakee;
1246    unsigned long wakee_flips;
1247    unsigned long wakee_flip_decay_ts;
1248
1249    int wake_cpu;
1250#endif
//下面一段和优先级,调度相关
1251        int on_rq;
1252
1253    int prio, static_prio, normal_prio;
1254    unsigned int rt_priority;
1255    const struct sched_class *sched_class;
1256    struct sched_entity se;
1257    struct sched_rt_entity rt;
1258#ifdef CONFIG_CGROUP_SCHED
1259    struct task_group *sched_task_group;
1260#endif
1261    struct sched_dl_entity dl;


1295        struct list_head tasks;  进程链表 
1296#ifdef CONFIG_SMP
1297    struct plist_node pushable_tasks;
1298    struct rb_node pushable_dl_tasks;
1299#endif
1300
1301    struct mm_struct *mm, *active_mm;  内存管理进程的地址空间相关 
1302#ifdef CONFIG_COMPAT_BRK
1303    unsigned brk_randomized:1;
1304#endif
1305    /* per-thread vma caching */
1306    u32 vmacache_seqnum;
1307    struct vm_area_struct *vmacache[VMACACHE_SIZE];
1308#if defined(SPLIT_RSS_COUNTING)
1309    struct task_rss_stat    rss_stat;
1310#endif


/* Revert to default priority/policy when forking */
1325    unsigned sched_reset_on_fork:1;
1326    unsigned sched_contributes_to_load:1;
1327
1328    unsigned long atomic_flags; /* Flags needing atomic access. */
1329
1330    pid_t pid;  进程的pid
1331    pid_t tgid;
1332
1333#ifdef CONFIG_CC_STACKPROTECTOR
1334    /* Canary value for the -fstack-protector gcc feature */
1335    unsigned long stack_canary;
1336#endif


//下面一段为进程的父子关系
    struct task_struct __rcu *real_parent; /* real parent process */
1343    struct task_struct __rcu *parent; /* recipient of SIGCHLD, wait4() reports */
1344    /*
1345     * children/sibling forms the list of my natural children
1346     */
1347    struct list_head children;  /* list of my children */
1348    struct list_head sibling;   /* linkage in my parent's children list */
1349    struct task_struct *group_leader;   /* threadgroup leader */
1350
1351    /*
1352     * ptraced is the list of tasks this task is using ptrace on.
1353     * This includes both natural children and PTRACE_ATTACH targets.
1354     * p->ptrace_entry is p's link on the p->parent->ptraced list.
1355     */
1356    struct list_head ptraced;    调试用的
1357    struct list_head ptrace_entry;
1358
1359    /* PID/PID hash table linkage. */
1360    struct pid_link pids[PIDTYPE_MAX];  pid的哈希表   可以方便查找

1361    struct list_head thread_group;
1362    struct list_head thread_node;
1363


一下一段为时间相关的数据结构
    cputime_t utime, stime, utimescaled, stimescaled;
1369    cputime_t gtime;
1370#ifndef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
1371    struct cputime prev_cputime;
1372#endif
1373#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
1374    seqlock_t vtime_seqlock;
1375    unsigned long long vtime_snap;
1376    enum {
1377        VTIME_SLEEPING = 0,
1378        VTIME_USER,
1379        VTIME_SYS,
1380    } vtime_snap_whence;
1381#endif
1382    unsigned long nvcsw, nivcsw; /* context switch counts */
1383    u64 start_time;     /* monotonic time in nsec */
1384    u64 real_start_time;    /* boot based time in nsec */
1385/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
1386    unsigned long min_flt, maj_flt;
1387
1388    struct task_cputime cputime_expires;
1389    struct list_head cpu_timers[3];
1390

/* process credentials */
1392    const struct cred __rcu *real_cred; /* objective and real subjective task
1393                     * credentials (COW) */
1394    const struct cred __rcu *cred;  /* effective (overridable) subjective task
1395                     * credentials (COW) */
1396    char comm[TASK_COMM_LEN]; /* executable name excluding path
1397                     - access with [gs]et_task_comm (which lock
1398                       it with task_lock())
1399                     - initialized normally by setup_new_exec */
1400/* file system info */
1401    int link_count, total_link_count;
1402#ifdef CONFIG_SYSVIPC
1403/* ipc stuff */
1404    struct sysv_sem sysvsem;
1405    struct sysv_shm sysvshm;
1406#endif
1407#ifdef CONFIG_DETECT_HUNG_TASK
1408/* hung task detection */
1409    unsigned long last_switch_count;
1410#endif
1411/* CPU-specific state of this task */
1412    struct thread_struct thread;    和当前任务cpu相关的一些状态,与之前my_kernelvs中自己定义的PCB相似,在进程切换时起着关键作用

1413/* filesystem information */
1414    struct fs_struct *fs;   文件系统
1415/* open file information */
1416    struct files_struct *files;  打开的文件描述符列表
1417/* namespaces */
1418    struct nsproxy *nsproxy;
1419/* signal handlers */
1420    struct signal_struct *signal;  信号处理相关
1421    struct sighand_struct *sighand;
1422
1423    sigset_t blocked, real_blocked;
1424    sigset_t saved_sigmask; /* restored if set_restore_sigmask() was used */
1425    struct sigpending pending;

转自:

回去最上部
文本读写基本流程
读文件
1、进度调用库函数向基础发起读文件央求;
2、内核通过检查进程的公文汇报符定位到虚构文件系统的已开采文件列表表项;
3、调用该公文可用的连串调用函数read()
3、read()函数通过文件表项链接到目录项模块,依据传入的文件路线,在目录项模块中搜索,找到该文件的inode;
4、在inode中,通过文件内容偏移量总计出要读取的页;
5、通过inode找到文件对应的address_space;
6、在address_space中拜谒该公文的页缓存树,查找对应的页缓存结点:
(1)假如页缓存命中,那么直接回到文件内容;
(2)假诺页缓存缺点和失误,那么发生多少个页缺点和失误非常,创建三个页缓存页,同期经过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;
7、文件内容读取成功。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
    int pid;
    /* fork another process */
    pid = fork();
    if (pid < 0) 
    { 
        /* error occurred */
        fprintf(stderr,"Fork Failed!");
        exit(-1);
    } 
    else if (pid == 0) 
    {
        /* child process */
        printf("This is Child Process!\n");
    } 
    else 
    {  
        /* parent process  */
        printf("This is Parent Process!\n");
        /* parent will wait for the child to complete*/
        wait(NULL);
        printf("Child Complete!\n");
    }
}

Address Space

上边大家总括已经钻探过的address_space全体成效。address_space是Linux内核中的一个注重抽象,它被视作文件系统和页缓存的中档适配器,用来提示一个文本在页缓存中曾经缓存了的物理页。由此,它是页缓存和外部设备普通话件系统的大桥。即便将文件系统能够精通成数据源,那么address_space能够说提到了内部存款和储蓄器系统和文件系统。

由图中能够见见,地址空间address_space链接到页缓存基数树和inode,由此address_space通过指针能够平价的收获文件inode和page的新闻。那么页缓存是怎样通过address_space完毕缓冲区功用的?大家再来看完整的公文读写流程。

 

回来顶上部分

图片 1

图片 2