Linux 基础资料

Unix系统高级编程
业务逻辑:根据业务需求,按照设计好的逻辑规则,处理信息,与系统无关。
系统访问:利用操作系统所提供的各种功能辅助业务逻辑的实现。
标准函数:scanf/printf - 源代码级兼容
系统函数:read/write - 接口级兼容
环境、性能、功能。
一、Unix系统简介
1.Unix系统的背景
1961-1969:史前时代
CTSS(Compatible Time-Sharing System,兼容分时系统),以MIT为首的开发小组,小而简单的实验室原型。
Multics(Multiplexed Information and Computing System,多路信息与计算系统),庞大而负责,不堪重负。
Unics(Uniplexed information and Computing System,单路信息与计算系统),返璞归真,走上正道。
1969-1971:创世纪
Ken Thompson,肯.汤普逊,Unix之父,B语言之父,内核用B语言+汇编语言开发,PDP-7,第一个Unix系统核心和简单应用。后来被移植到PDP-11平台,功能更加完善。
1971-1979:出谷纪
Dennis Ritchie,丹尼斯.里奇,C语言之父,用C语言重写了Unix系统内核,极大地提升了Unix系统的可读性、可维护性和可移植性——Unix V7,第一个真正意义上的Unix系统。
1980-1985:第一次Unix战争
AT&T贝尔实验室:SVR4
加州大学伯克利分校:BSD+TCP/IP
DARPA,ARPANET(INTERNET)
IEEE,国际电气电子工程师协会,POSIX为Unix内核和外壳制定了一系列技术标准和规范,消除系统版本之间分歧,大一统的操作系统。
1988-1990:第二次Unix战争
AT&T+Sun
IBM+DEC+HP
比尔.盖茨->Windows
1990-现在
1991,Linus Torvalds创建了Linux系统的内核
1993,Linux已达到产品级操作系统的水准
1993,AT&T将Unix系统卖给Novell
1994,Novell将Unix系统卖给X/Open组织
1995,X/Open将Unix系统捐给SCO
2000,SCO将Unix系统卖给Celdear——Linux发行商
Linux就是现代版本的Unix。
2.Linux系统的背景
类Unix操作系统,免费开源。
不同发行版本使用相同的内核。
支持多种硬件平台:手机、路由器、视频游戏控制器、个人电脑、大型计算机等等。
隶属于GNU工程。GNU = GNU Not Unix。
受GPL许可证限制:如果发布了可执行的二进制代码,就必须同时发布可读的源代码,并且在发布任何基于GPL许可证的软件时,不能添加任何限制性条款。
3.Linux系统的版本
早期版本:0.01,0.02,…,1.00
旧计划:1.0.1,…,2.6.0 (A.B.C)
A - 主版本号,内核大幅更新
B - 次版本号,内核重大修改,奇数测试版,偶数稳定版
C - 补丁序号,内核轻微修改
新计划:A.B.C-D.E
D - 构建次数,反映极微小的更新
E - 描述信息
rc/r - 候选版本
smp - 支持对称多处理器
EL - Rad Hat的企业版本
mm - 试验新技术

cat /proc/version
4.Linux系统的特点
遵循GNU/GPL许可证
开放性
多用户
多任务
设备无关性
丰富网络功能
可靠的系统安全
良好的可移植性
5.Linux的发行版本
Ubuntu - 大众化,简单易用
Linux Mint - 新潮前位
Fedora - Red Hat的桌面版本
openSUSE - 华丽
Debian - 自由开放
Slackware - 朴素简洁,简陋
Red Hat - 经典,稳定,企业应用,支持全面
二、GNU编译器(gcc)
1.GCC的基本特点
1)支持多种硬件架构
x86-64
Alpha
ARM
PowerPC
SPARC
VAX

2)支持多种操作系统
Unix
Linux
BSD
Android
Mac OS X
iOS
Windows
3)支持多种编程语言
C
C++
Objective-C
Java
Fortran
Pascal
Ada
4)GCC的版本
gcc -v
2.构建过程
源代码(.c)-预编译->头文件和宏扩展-编译->汇编码(.s)-汇编->目标码(.o)-链接->可执行代码(a.out)
代码:hello.c
vi hello.c - 编写源代码
gcc -E hello.c -o hello.i - 预编译(编译预处理)
gcc -S hello.i - 获得汇编代码(hello.s)
gcc -c hello.s - 获得目标代码(hello.o)
gcc hello.o -o hello - 获得可执行代码(hello)
./hello - 运行可执行代码
3.文件名后缀
.h - C语言源代码头文件
.c - 预处理前的C语言源代码文件 > 可读文本
.s - 汇编语言文件 /
.o - 目标文件
.a - 静态库文件 > 不可读的二进制
.so - 共享(动态)库文件 /
.out - 可执行文件 /
4.编译选项
gcc [选项] [参数] 文件1 文件2 …
-o: 指定输出文件
如:gcc hello.c -o hello
-E: 预编译,缺省输出到屏幕,用-o指定输出文件
如:gcc -E hello.c -o hello.i
-S: 编译,将高级语言文件编译成汇编语言文件
如:gcc -S hello.c
-c: 汇编,将汇编语言文件汇编成机器语言文件
如:gcc -c hello.s
-Wall:产生全部警告
如:gcc -Wall wall.c
代码:wall.c
-Werror:将警告作为错误处理
如:gcc -Werror werror.c
代码:werror.c
-x: 指定源代码的语言
xxx.c - C语言
xxx.cpp - C++语言
xxx.for - Fortran语言
xxx.java - Java语言

gcc -x c++ cpp.c -lstdc++ -o cpp
代码:cpp.c
-O0/O1/O2/O3: 指定优化等级,O0不优化,缺省O1优化
5.头文件
1)头文件里写什么?
头文件卫士
#ifndef XXX
#define XXX

#endif
a.h
/
b.h c.h
\ /
d.c
包含其它头文件
宏定义
#define PI 3.14159
自定义类型
struct Circle {
double x, y, r;
};
类型别名
typedef struct Circle C;
外部变量声明
extern double e;
函数声明
double circleArea(C c);
a.h
/
b.c c.c
| |
b.o c.o
\ /
d - 重定义
一个头文件可能会被多个源文件包含,写在头文件里的函数定义也会因此被预处理器扩展到多个包含该头文件的源文件中,并在编译阶段被编译到等多个不同的目标文件中,这将导致链接错误:multiple definition,多重定义。
2)去哪里找头文件?
gcc -I<头文件的附加搜索路径>
#include <my.h>
先找-I指定的目录,再找系统目录。
#include “my.h”
先找-I指定的目录,再找当前目录,最后找系统目录。
头文件的系统目录:
/usr/include - 标准C库
/usr/local/include - 第三方库
/usr/lib/gcc/i686-linux-gnu/5.4.0/include - 编译器库
代码:calc.h、calc.c、math.c
6.预处理指令
#include - 将指定的文件内容插至此指令处
#define - 定义宏
#undef - 删除宏
#if - 如果
#ifdef - 如果宏已定义
#ifndef - 如果宏未定义
#else - 否则,与#if/#ifdef/#ifndef配合使用
#elif - 否则如果,与#if/#ifdef/#ifndef配合使用
#endif - 结束判定,与#if/#ifdef/#ifndef配合使用
#error - 产生错误,结束预处理
#warning - 产生警告,继续预处理
代码:error.c
#line - 指定行号
代码:line.c
#pragma - 设定编译器的状态或者指示编译器的操作
#pragma GCC dependency 被依赖文件
a<-b
#pragma GCC poison 语法禁忌
#pragma pack(按几字节对齐:1/2/4/8)
#pragma pack() - 按缺省字节数对齐
代码:dep.c、pragma.c
7.预定义宏
无需自行定义,预处理器会根据事先设定好的规则将这些宏扩展成其对应的值。
BASE_FILE: 正在被处理的源文件名
FILE: 所在文件名
LINE: 所在行的行号
FUNCTION: 所在函数的函数名
func: 同__FUNCTION

DATE: 处理日期
TIME: 处理时间
INCLUDE_LEVEL: 包含层数,从0开始
a - 0
#include “b” - 1
#include “c” - 2
cplusplus: C++有定义,C无定义
代码:print.h、predef.h、predef.c
8.环境变量
在进程向下文中保存的一些数据:键(功能,是什么)=值(内容)。
env
C_INCLUDE_PATH
C语言头文件的附加搜索路径,相当于-I选项。
CPATH
同C_INCLUDE_PATH
CPLUS_INCLUDE_PATH
C++语言头文件的附加搜索路径,相当于-I选项。
LIBRARY_PATH
链接库路径
LD_LIBRARY_PATH
加载库路径
代码:calc.h、calc.c、math.c
#include “/…/…/xxx.h” - 移植性差
#include “xxx.h”
gcc -I/…/… … - 推荐
C_INCLUDE_PATH/CPATH=/…/…😕… - 易冲突
三、库
a.c -> a.out
foo()
bar()
hum()
main()
单一模型:将程序中所有功能全部实现于一个单一的源文件内部。编译时间长,不易于维护和升级,不易于协作开发。
分离模型:将程序中的不同功能模块划分到不同的源文件中。缩短编译时间,易于维护和升级,易于协作开发。
a.c -> a.o
foo() | -> …
bar() |
b.c -> b.o /
hum()
a.o
b.o | -> 库 + 其它模块 -> …
c.o |
… /
1.静态库
静态库的本质就是将多个目标文件打包成一个文件。
链接静态库就是将库中被调用的代码复制到调用模块中。
使用静态库的程序通常会占用较大的空间,库中代码一旦修改,所有使用该库的程序必须重新链接。
使用静态库的程序在运行无需依赖库,其执行效率高。
静态库的形式:libxxx.a
构建静态库:
.c -> .o -> .a
ar -r libxxx.a x.o y.o z.o
^ _
/
|
___|
使用静态库:
gcc … -lxxx -L<库路径>
export LIBRARY_PATH=<库路径>
gcc … -lxxx

代码:static/
2.动态(共享)库
动态库和静态库最大的不同就是,链接动态库并不需要将库中被调用的代码复制到调用模块中,相反被嵌入到调用模块中的仅仅是被调用代码在动态库中的相对地址。
如果动态库中的代码同时为多个进程所用,动态库的实例在整个内存空间中仅需一份,因此动态库也叫共享库或共享对象(Shared Object, so)。
使用动态库的模块所占空间较小,即使修改了库中的代码,只要接口保持不变,无需重新链接。
使用动态库的代码在运行时需要依赖库,执行效率略低。
动态库的形式:libxxx.so
构建动态库:
gcc -c -fpic xxx.c -> xxx.o
|
生成位置无关码
库内部的函数调用也用相对地址表示
gcc -shared -o libxxx.so x.o y.o z.o
^ _/
|
_|
使用动态库:
gcc … -lxxx -L<库路径>
export LIBRARY_PATH=<库路径>
gcc … -lxxx
运行时所调用的动态库必须位于LD_LIBRARY_PATH环境变量所表示的路径中。
代码:shared/
gcc缺省链接共享库,可通过-static选项强制链接静态库。
代码:hello.c
3.动态加载动态库
#include <dlfcn.h> \ 系统提供的针对动态
-ldl / 库的动态加载函数集
void* dlopen(const char* filename, int flag);
成功返回动态库的句柄,失败返回NULL。

FILE* fp = fopen(…);
fread(fp…);
fwrite(fp…);

filename - 动态库路径,若只给文件名,则根据LD_LIBRARY_PATH环境变量搜索动态库
flag - 加载方式,可取以下值:
RTLD_LAZY - 延迟加载,使用动态中的符号时才加载
RTLD_NOW - 立即加载
该函数所返回的动态库句柄唯一地标识了系统内核所维护的动态库对象,将作为后续函数调用的参数。
void* dlsym(void* handle, const char* symbol);
成功返回函数地址,失败返回NULL。
handle - 动态库句柄
symbol - 符号(函数或全局变量)名
该函数所返回的函数指针是void类型,需要强制类型转换为实际的函数指针类型才能调用。
int dlclose(void
handle);
成功返回0,失败返回非零。
handle - 动态库句柄
char* dlerror(void);
之前若有错误发生则返回错误信息字符串,否则返回NULL。
代码:load.c
四、辅助工具
1.查看符号表:nm
列出目标文件(.o)、可执行文件、静态库文件(.a)或动态库文件(.so)中的符号
代码:nm.c
2.显示二进制模块的反汇编信息:objdump -S
3.删除目标文件(.o)、可执行文件、静态库文件(.a)或动态库文件(.so)中的符号表和调试信息:strip
4.查看可执行程序文件或动态库文件所依赖的动态库文件:ldd
五、错误号和错误信息
1.通过函数的返回值表达错误
返回整数的函数:通过返回合法值域以外的值表示错误
int age(char const* name) {

return 1000;
}
返回指针的函数:通过返回NULL指针表示错误
不需要通过返回值输出信息的函数:返回0表示成功,返回-1表示失败。
int delete(char const* filename) {

return 0;

return -1;
}
2.通过错误号和错误信息表示产生错误的具体原因
#include <errno.h>
全局变量:errno,整数,标识最近一次系统调用的错误
#include <string.h>
char* strerror(int errnum); // 根据错误号返回错误信息
#include <stdio.h>
void perror(const char* s); // 打印最近错误的错误信息
printf函数的%m标记被替换为最近错误的错误信息
代码:errno.c
虽然所有的错误号都不是0,但是因为在函数执行成功的情况下错误号全局变量errno不会被清0,因此不能用errno是否为0作为函数成功失败的判断条件,是否出错还是应该根据函数的返回值来决定。
返回值 = 函数调用(…);
if (返回值表示函数调用失败) {
根据errno判断发生了什么错误
针对不同的错误提供不同的处理
}
代码:iferr.c
六、环境变量
每个进程都有一张独立的环境变量表,其中的每个条目都是一个形如“键=值”形式的环境变量。
env
全局变量:environ,需要自己在代码做外部声明。
environ->| * |->AAA=aaa\0
| * |->BBB=bbb\0
| * |->CCC=ccc\0
|NULL|
所谓环境变量表就是一个以NULL指针结束的字符指针数组,其中的每个元素都是一个字符指针,指向一个以空字符结尾的字符串,该字符串就是形如”键=值”形式的环境变量。
argv-> * * * NULL
| | |
a.out -c b.c
根据环境变量名获取其值
char* getenv(char const* name);
成功返回变量名匹配的变量值,失败返回NULL。
name - 环境变量名,即等号左边的部分
添加或修改环境变量
int putenv(char* string);
成功返回0,失败返回-1。
string - 形如“键=值”形式的环境变量字符串
若其键已存在,则修改其中,若其键不存在,则添加新变量
添加或修改环境变量
int setenv(const char* name, const char* value,
int overwrite);
成功返回0,失败返回-1。
name - 环境变量名,即等号左边的部分
value - 环境变量值,即等号右边的部分
overwrite - 当name参数所表示的环境变量名已存在,此参数取0则保持该变量的原值不变,若此参数取非0,则将该变量的值修改为value。
删除环境变量
int unsetenv(const char* name);
成功返回0,失败返回-1。
name - 环境变量名,即等号左边的部分
清空环境变量
int clearenv(void);
成功返回0,失败返回-1。
代码:env.c
七、内存
1.虚拟内存、物理内存、半导体内存和换页文件
虚拟内存:地址空间,虚拟的存储区域,应用程序所访问的都是虚拟内存。
物理内存:存储空间,实际的存储区域,只有系统内核可以访问物理内存。
虚拟内存和物理内存之间存在对应关系,当应用程序访问虚拟内存时,系统内核会依据这种对应关系找到与之相应的物理内存。上述对应关系存储在内核中的内存映射表中。
物理内存包括半导体内存和换页文件两部分。
当半导体内存不够用时,可以把一些长期闲置的代码和数据从半导体内存中缓存到换页文件中,这叫页面换出,一旦需要使用被换出的代码和数据,再把它们从换页文件恢复到半导体内存中,这叫页面换入。因此,系统中的虚拟内存比半导体内存大得多。
2. 进程映射(Process Maps)
每个进程都拥有独立的4G字节的虚拟内存,分别被映射到不同的物理内存区域。
内存映射和换入换出都是以页为单位,1页=4096字节。
4G虚拟内存中高地址的1G被映射到内核的代码和数据区,这1个G在各个进程间共享。用户的应用程序只能直接访问低地址的3个G虚拟内存,因此该区域被称为用户空间,而高地址的1个G虚拟内存则被称为内核空间。用户空间中的代码只能直接访问用户空间的数据,如果要想访问内核空间中的代码和数据必须借助专门的系统调用完成。
用户空间的3G虚拟内存可以进一步被划分为如下区域:
------------------
系统内核(1G)
高地址------------------
命令行参数
和环境变量
------------------
栈区:非静态局部变量
- - - - - - - -
v
3G
^
- - - - - - - -
堆区:动态内存分配(malloc函数族)
-----------------
BSS区:无初值的全局和静态局部变量
-----------------
数据区:非const型有初值的全局和静态局部变量
-----------------
只读常量:字面值常量,const型有初值的全局
和静态局部变量
代码区(正文段):可执行指令
低地址-----------------
代码:maps.c

通过size命令查看一个可执行程序的代码区、数据区和BSS区的大小。
每个进程的用户空间都拥有独立的从虚拟内存到物理内存的映射,谓之进程间的内存壁垒。
代码:vm.c
3.内存的分配与释放
malloc/calloc/realloc/free
|
v
brk/sbrk
|
v
mmap/munmap
|
v
kmalloc/kfree
以增加方式分配或释放虚拟内存
分配:映射+占有
| ____________
在地址空间(虚拟内存)和
存储空间(物理内存)之间 指定内存空
建立映射关系 间的归属性
释放:放弃占有+解除映射
| |
解除对内存空 消除地址空间(虚拟内存)和存储
间的归属约束 空间(物理内存)之间的映射关系
#include <unistd.h>
void* sbrk(intptr_t increment);
堆顶->- - - - - - - -
堆区
-----------------
sbrk(10)
堆顶->- - - - - - - -
10字节
- - - - - - - -<-返回值
堆区
-----------------
sbrk(-10)
- - - - - - - -<-返回值
10字节
堆顶->- - - - - - - -
堆区
-----------------
成功返回调用该函数之前的堆顶指针,失败返回-1。
increment

0 - 堆顶指针上移,增大堆空间,分配虚拟内存
<0 - 堆顶指针下移,缩小堆空间,释放虚拟内存
=0 - 不分配也不释放虚拟内存,仅仅返回当前堆顶指针
系统内核维护一个指针,指向堆内存的顶端,即有效堆内存中最后一个字节的下一个位置。sbrk函数根据增量参数increment调整该指针的位置,同时返回该指针原来的位置,期间若发生内存耗尽或空闲,则自动追加或取消相应内存页的映射。
123____~~~~~~~~…
^ ^ ^ ^
堆顶
代码:sbrk.c
以绝对地址的方式分配或释放虚拟内存
int brk(void* end_data_segment);
成功返回0,失败返回-1。
end_data_segment
当前堆顶,分配虚拟内存
<当前堆顶,释放虚拟内存
=当前堆顶,空操作
堆顶->- - - - - - - -<-void* p = sbrk(0);
堆区
-----------------
brk(p+10)
- - - - - - - -<-p+10
10字节
堆顶->- - - - - - - -
堆区
-----------------
brk§
堆顶->- - - - - - - -<-p
堆区
-----------------
系统内核维护一个指针,指向当前堆顶,brk函数根据指针参数end_data_segment设置堆顶的新位置,期间若发生内存耗尽或空闲,则自动追加或取消相应内存页的映射。
代码:brk.c
建立虚拟内存到物理内存或文件的映射
#include <sys/mman.h>
void* mmap(void* start, size_t length, int prot,
int flags, int fd, off_t offset);
成功返回映射区虚拟内存的起始地址,失败返回MAP_FAILED(void类型的-1)。
start - 映射区虚拟内存的起始地址,NULL表示自动选择
length - 映射区的字节数,自动按页取整
prot - 访问权限,可取以下值:
PROT_READ - 可读
PROT_WRITE - 可写
PROT_EXEC - 可执行
PROT_NONE - 不可访问
flags - 映射标志,可取以下值:
MAP_ANONYMOUS - 匿名映射,将虚拟内存映射到物理内存,函数的最后两个参数fd和offset被忽略
MAP_PRIVATE - 私有映射,将虚拟内存映射到文件的内存缓冲区中而非磁盘文件
MAP_SHARED - 共享映射,将虚拟内存映射到磁盘文件中
MAP_DENYWRITE - 拒写映射,文件中被映射区域不能存在其它写入操作
MAP_FIXED - 固定映射,若在start上无法创建映射,则失败(无此标志系统会自动调整)
MAP_LOCKED - 锁定映射,禁止被换出到换页文件
fd - 文件描述符
offset - 文件偏移量,自动按页对齐
解除虚拟内存到物理内存或文件的映射
int munmap(void
start, size_t length);
成功返回0,失败返回-1。
start - 映射区的起始地址
length - 映射区的字节数
代码:mmap.c
八、系统调用
应用程序--------------+
vi/emacs/gftp/firefox |
| |
标准库、第三方库 |
C/C++/Qt/X11 |
| |
系统调用<------------+
brk/sbrk/mmap/munmap
1.Linux系统内核提供了一套用于实现各种系统功能的子程序,谓之系统调用。程序编写者可以象调用普通C语言函数一样调用这些系统调用函数,以访问系统内核提供的各种服务。
2.系统调用函数在形式上与普通C语言函数并无差别。二者的不同之处在于,前者工作在内核态,而后者工作在用户态。
3.在Intel的CPU上运行代码分为四个安全级别:Ring0、Ring1、Ring2和Ring3。Linux系统只使用了Ring0和Ring3。用户代码工作在Ring3级,而内核代码工作在Ring0级。一般而言用户代码无法访问Ring0级的资源,除非借助系统调用,使用户代码得以进入Ring0级,使用系统内核提供的功能。
4.系统内核内部维护一张全局表sys_call_table,表中的每个条目记录着每个系统调用在内核代码中的实现入口地址。
5.当用户代码调用某个系统调用函数时,该函数会先将参数压入堆栈,将系统调用标识存入eax寄存器,然后通过int 80h指令触发80h中断。
6.这时程序便从用户态(Ring3)进入内核态(Ring0)。
7.工作系统内核中的中断处理函数被调用,80h中断的处理函数名为system_call,该函数先从堆栈中取出参数,再从eax寄存器中取出系统调用标识,然后再从sys_call_table表中找到与该系统调用标识相对应的实现代码入口地址,挈其参数调用该实现,并将处理结果逐层返回到用户代码中。
九、文件
1.文件系统的物理结构
1)硬盘的物理结构:驱动臂、盘片、主轴、磁头、控制器
2)磁表面存储器的读写原理
硬盘片的表面覆盖着薄薄的磁性涂层,涂层中含有无数微小的磁性颗粒,谓之磁畴。相邻的若干磁畴组成一个磁性存储元,以其剩磁的极性表示二进制数字0和1。为磁头的写线圈中施加脉冲电流,可把一位二进制数组转换为磁性存储元的剩磁极性。利用磁电变换,通过磁头的读线圈,可将磁性存储元的剩磁极性转换为相应的电信号,表示二进制数。
3)磁道和扇区
磁盘旋转,磁头固定,每个磁头都会在盘片表面画出一个圆形轨迹。改变磁头位置,可以形成若干大小不等的同心圆,这些同心圆就叫做磁道(Track)。每张盘片的每个表面上都有成千上万个磁道。一个磁道,以512字节为单位,分成若干个区域,其中的每个区域就叫做一个扇区(Sector)。扇区是文件存储的基本单位。
4)柱面、柱面组、分区和磁盘驱动器
硬盘中,不同盘片相同半径的磁道所组成的圆柱称为柱面(Cylinder)。整个硬盘的柱面数与每张盘片的磁道数相等。
硬盘上的每个字节需要通过以下参数定位:
磁头号:确定哪个盘面
柱面号:确定哪个磁道 > 磁盘I/O
扇区号:确定哪个区域 /
偏移量:确定扇区内的位置
若干个连续的柱面构成一个柱面组
若干个连续的柱面组构成一个分区
每个分区都建有独立的文件系统
若干分区构成一个磁盘驱动器
2.文件系统的逻辑结构
磁盘驱动器:| 分区 | 分区 | 分区 |
分区:| 引导块 | 超级块 | 柱面组 | 柱面组 | 柱面组 |
柱面组:
| 引导块 | 柱面组 | i节点映 | 块位图 | i节点表 | 数据块集 |
| 副 本 | 信 息 | 射 表 | | | |
i节点号:431479
i节点
文件元数据
100 | 200 | 300
根据目录文件中记录的i节点编号检索i节点映射表,获得i节点下标,用该下标查i节点表,获得i节点,i节点中包含了数据块索引表,利用数据块索引从数据块集中读取数据块,即获得文件数据。
直接块:存储文件实际数据内容
间接块:存储下级文件数据块索引表
100


xxx

200

xxx

300

400 | 500 | 600
3.文件分类
普通文件(-):可执行程序、文本、图片、音频、视频、网页
目录文件(d):该目录中每个硬链接名和i节点号的对应表
符号链接文件(l):存放目标文件的路径
管道文件§:有名管道,进程间通信
套接字文件(s):进程间通信
块设备文件(b):按块寻址,顺序或随机读写
字符设备文件©:按字节寻址,只能以字节为单位顺序读写
4.文件的打开与关闭
打开:在系统内核中建立一套数据结构,用于访问文件
进程表项

文件描述符表
|文件描述符标志 | 文件表项指针 | 0
|文件描述符标志 | 文件表项指针 | 1
|文件描述符标志 | 文件表项指针 | 2
… | ^
±----------------------------+ |
| 文件描述符
v
文件表项
文件状态标志
文件读写位置
v节点指针
… |
±----+
|
v
v节点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值