应该会移除所有以 A 为开头的文件而不是只删除文件名是A*的文件。这个特性也是最小吃惊原则(principle of least surprise)
最小吃惊原则一半常用于用户界面和软件设计。它的原型是:该功能或者特征应该符合用户的预期,不应该使用户感到惊讶和震惊。
一些有经验的程序员通常希望系统具有较强的功能性和灵活性。设计 Linux 的一个基本目标是每个应用程序只做一件事情并把他做好。所以编译器只负责编译的工作,编译器不会产生列表,因为有其他应用比编译器做的更好。
很多人都不喜欢冗余,为什么在 cp 就能描述清楚你想干什么时候还使用 copy?这完全是在浪费宝贵的hacking time。为了从文件中提取所有包含字符串ard的行,Linux 程序员应该输入
grep ard f
Linux 接口
Linux 系统是一种金字塔模型的系统,如下所示
应用程序发起系统调用把参数放在寄存器中(有时候放在栈中),并发出trap系统陷入指令切换用户态至内核态。因为不能直接在 C 中编写 trap 指令,因此 C 提供了一个库,库中的函数对应着系统调用。有些函数是使用汇编编写的,但是能够从 C 中调用。每个函数首先把参数放在合适的位置然后执行系统调用指令。因此如果你想要执行 read 系统调用的话,C 程序会调用 read 函数库来执行。这里顺便提一下,是由 POSIX 指定的库接口而不是系统调用接口。也就是说,POSIX 会告诉一个标准系统应该提供哪些库过程,它们的参数是什么,它们必须做什么以及它们必须返回什么结果。
除了操作系统和系统调用库外,Linux 操作系统还要提供一些标准程序,比如文本编辑器、编译器、文件操作工具等。直接和用户打交道的是上面这些应用程序。因此我们可以说 Linux 具有三种不同的接口:系统调用接口、库函数接口和应用程序接口
Linux 中的GUI(Graphical User Interface)和 UNIX 中的非常相似,这种 GUI 创建一个桌面环境,包括窗口、目标和文件夹、工具栏和文件拖拽功能。一个完整的 GUI 还包括窗口管理器以及各种应用程序。
Linux 上的 GUI 由 X 窗口支持,主要组成部分是 X 服务器、控制键盘、鼠标、显示器等。当在 Linux 上使用图形界面时,用户可以通过鼠标点击运行程序或者打开文件,通过拖拽将文件进行复制等。
Linux 组成部分
事实上,Linux 操作系统可以由下面这几部分构成
引导程序(Bootloader):引导程序是管理计算机启动过程的软件,对于大多数用户而言,只是弹出一个屏幕,但其实内部操作系统做了很多事情内核(Kernel):内核是操作系统的核心,负责管理 CPU、内存和外围设备等。初始化系统(Init System):这是一个引导用户空间并负责控制守护程序的子系统。一旦从引导加载程序移交了初始引导,它就是用于管理引导过程的初始化系统。后台进程(Daemon):后台进程顾名思义就是在后台运行的程序,比如打印、声音、调度等,它们可以在引导过程中启动,也可以在登录桌面后启动图形服务器(Graphical server):这是在监视器上显示图形的子系统。通常将其称为 X 服务器或 X。桌面环境(Desktop environment):这是用户与之实际交互的部分,有很多桌面环境可供选择,每个桌面环境都包含内置应用程序,比如文件管理器、Web 浏览器、游戏等应用程序(Applications):桌面环境不提供完整的应用程序,就像 Windows 和 macOS 一样,Linux 提供了成千上万个可以轻松找到并安装的高质量软件。
尽管 Linux 应用程序提供了 GUI ,但是大部分程序员仍偏好于使用命令行(command-line interface),称为shell。用户通常在 GUI 中启动一个 shell 窗口然后就在 shell 窗口下进行工作。
shell 命令行使用速度快、功能更强大、而且易于扩展、并且不会带来肢体重复性劳损(RSI)。
下面会介绍一些最简单的 bash shell。当 shell 启动时,它首先进行初始化,在屏幕上输出一个提示符(prompt),通常是一个百分号或者美元符号,等待用户输入
等用户输入一个命令后,shell提取其中的第一个词,这里的词指的是被空格或制表符分隔开的一连串字符。假定这个词是将要运行程序的程序名,那么就会搜索这个程序,如果找到了这个程序就会运行它。然后shell 会将自己挂起直到程序运行完毕,之后再尝试读入下一条指令。shell
也是一个普通的用户程序。它的主要功能就是读取用户的输入和显示计算的输出。shell 命令中可以包含参数,它们作为字符串传递给所调用的程序。比如
cp src dest
会调用 cp 应用程序并包含两个参数src和dest。这个程序会解释第一个参数是一个已经存在的文件名,然后创建一个该文件的副本,名称为 dest。
并不是所有的参数都是文件名,比如下面
head -20 file
第一个参数 -20,会告诉 head 应用程序打印文件的前 20 行,而不是默认的 10 行。控制命令操作或者指定可选值的参数称为标志(flag),按照惯例标志应该使用-来表示。这个符号是必要的,比如
head 20 file
是一个完全合法的命令,它会告诉 head 程序输出文件名为 20 的文件的前 10 行,然后输出文件名为 file 文件的前 10 行。Linux 操作系统可以接受一个或多个参数。
为了更容易的指定多个文件名,shell 支持魔法字符(magic character),也被称为通配符(wild cards)。比如,*可以匹配一个或者多个可能的字符串
ls *.c
告诉 ls 列举出所有文件名以.c结束的文件。如果同时存在多个文件,则会在后面进行并列。
另一个通配符是问号,负责匹配任意一个字符。一组在中括号中的字符可以表示其中任意一个,因此
ls [abc]*
会列举出所有以a、b或者c开头的文件。
shell 应用程序不一定通过终端进行输入和输出。shell 启动时,就会获取标准输入、标准输出、标准错误文件进行访问的能力。
标准输出是从键盘输入的,标准输出或者标准错误是输出到显示器的。许多 Linux 程序默认是从标准输入进行输入并从标准输出进行输出。比如
sort
会调用 sort 程序,会从终端读取数据(直到用户输入 ctrl-d 结束),根据字母顺序进行排序,然后将结果输出到屏幕上。
通常还可以重定向标准输入和标准输出,重定向标准输入使用进行重定向。允许一个命令中重定向标准输入和输出。例如命令
sort out
会使 sort 从文件 in 中得到输入,并把结果输出到 out 文件中。由于标准错误没有重定向,所以错误信息会直接打印到屏幕上。从标准输入读入,对其进行处理并将其写入到标准输出的程序称为过滤器。
考虑下面由三个分开的命令组成的指令
sort temp;head -30 0){
parent_handle // 父进程代码
}
else {
child_handle // 子进程代码
}
父进程在 fork 后会得到子进程的 PID,这个 PID 即能代表这个子进程的唯一标识符也就是 PID。如果子进程想要知道自己的 PID,可以调用getpid方法。当子进程结束运行时,父进程会得到子进程的 PID,因为一个进程会 fork 很多子进程,子进程也会 fork 子进程,所以 PID 是非常重要的。我们把第一次调用 fork 后的进程称为原始进程,一个原始进程可以生成一颗继承树
Linux 进程间通信
Linux 进程间的通信机制通常被称为Internel-Process communication,IPC下面我们来说一说 Linux 进程间通信的机制,大致来说,Linux 进程间的通信机制可以分为 6 种
信号 signal
[attach]59170[/attach]
结合用户空间和内核空间的优点,设计人员采用了一种内核级线程的方式,然后将用户级线程与某些或者全部内核线程多路复用起来
在这种模型中,编程人员可以自由控制用户线程和内核线程的数量,具有很大的灵活度。采用这种方法,内核只识别内核级线程,并对其进行调度。其中一些内核级线程会被多个用户级线程多路复用。
Linux 调度
下面我们来关注一下 Linux 系统的调度算法,首先需要认识到,Linux 系统的线程是内核线程,所以 Linux 系统是基于线程的,而不是基于进程的。
为了进行调度,Linux 系统将线程分为三类
实时先入先出实时轮询分时
实时先入先出线程具有最高优先级,它不会被其他线程所抢占,除非那是一个刚刚准备好的,拥有更高优先级的线程进入。实时轮转线程与实时先入先出线程基本相同,只是每个实时轮转线程都有一个时间量,时间到了之后就可以被抢占。如果多个实时线程准备完毕,那么每个线程运行它时间量所规定的时间,然后插入到实时轮转线程末尾。
注意这个实时只是相对的,无法做到绝对的实时,因为线程的运行时间无法确定。它们相对分时系统来说,更加具有实时性
Linux 系统会给每个线程分配一个nice值,这个值代表了优先级的概念。nice 值默认值是 0 ,但是可以通过系统调用 nice 值来修改。修改值的范围从 -20 - +19。nice 值决定了线程的静态优先级。一般系统管理员的 nice 值会比一般线程的优先级高,它的范围是 -20 - -1。
下面我们更详细的讨论一下 Linux 系统的两个调度算法,它们的内部与调度队列(runqueue)的设计很相似。运行队列有一个数据结构用来监视系统中所有可运行的任务并选择下一个可以运行的任务。每个运行队列和系统中的每个 CPU 有关。
Linux O(1)调度器是历史上很流行的一个调度器。这个名字的由来是因为它能够在常数时间内执行任务调度。在 O(1) 调度器里,调度队列被组织成两个数组,一个是任务正在活动的数组,一个是任务过期失效的数组。
大致流程如下:
调度器从正在活动数组中选择一个优先级最高的任务。如果这个任务的时间片过期失效了,就把它移动到过期失效数组中。如果这个任务阻塞了,比如说正在等待
I/O 事件,那么在它的时间片过期失效之前,一旦 I/O
操作完成,那么这个任务将会继续运行,它将被放回到之前正在活动的数组中,因为这个任务之前已经消耗一部分 CPU
时间片,所以它将运行剩下的时间片。当这个任务运行完它的时间片后,它就会被放到过期失效数组中。一旦正在活动的任务数组中没有其他任务后,调度器将会交换指针,使得正在活动的数组变为过期失效数组,过期失效数组变为正在活动的数组。使用这种方式可以保证每个优先级的任务都能够得到执行,不会导致线程饥饿。
在这种调度方式中,不同优先级的任务所得到 CPU 分配的时间片也是不同的,高优先级进程往往能得到较长的时间片,低优先级的任务得到较少的时间片。
这种方式为了保证能够更好的提供服务,通常会为交互式进程赋予较高的优先级,交互式进程就是用户进程。
Linux 系统不知道一个任务究竟是 I/O 密集型的还是 CPU 密集型的,它只是依赖于交互式的方式,Linux 系统会区分是静态优先级还是动态优先级。动态优先级是采用一种奖励机制来实现的。奖励机制有两种方式:奖励交互式线程、惩罚占用 CPU 的线程。在 Linux O(1) 调度器中,最高的优先级奖励是 -5,注意这个优先级越低越容易被线程调度器接受,所以最高惩罚的优先级是 +5。具体体现就是操作系统维护一个名为sleep_avg的变量,任务唤醒会增加 sleep_avg 变量的值,当任务被抢占或者时间量过期会减少这个变量的值,反映在奖励机制上。
O(1) 调度算法是 2.6 内核版本的调度器,最初引入这个调度算法的是不稳定的 2.5 版本。早期的调度算法在多处理器环境中说明了通过访问正在活动数组就可以做出调度的决定。使调度可以在固定的时间 O(1) 完成。
O(1) 调度器使用了一种启发式的方式,这是什么意思?
在计算机科学中,启发式是一种当传统方式解决问题很慢时用来快速解决问题的方式,或者找到一个在传统方法无法找到任何精确解的情况下找到近似解。
O(1) 使用启发式的这种方式,会使任务的优先级变得复杂并且不完善,从而导致在处理交互任务时性能很糟糕。
为了改进这个缺点,O(1) 调度器的开发者又提出了一个新的方案,即公平调度器(Completely Fair Scheduler, CFS)。 CFS 的主要思想是使用一颗红黑树作为调度队列。
数据结构太重要了。
CFS 会根据任务在 CPU 上的运行时间长短而将其有序地排列在树中,时间精确到纳秒级。
CFS算法总是优先调度哪些使用 CPU 时间最少的任务。最小的任务一般都是在最左边的位置。当有一个新的任务需要运行时,CFS会把这个任务和最左边的数值进行对比,如果此任务具有最小时间值,那么它将进行运行,否则它会进行比较,找到合适的位置进行插入。然后 CPU运行红黑树上当前比较的最左边的任务。
在红黑树中选择一个节点来运行的时间可以是常数时间,但是插入一个任务的时间是O(loog(N)),其中 N 是系统中的任务数。考虑到当前系统的负载水平,这是可以接受的。
调度器只需要考虑可运行的任务即可。这些任务被放在适当的调度队列中。不可运行的任务和正在等待的各种 I/O 操作或内核事件的任务被放入一个等待队列中。等待队列头包含一个指向任务链表的指针和一个自旋锁。自旋锁对于并发处理场景下用处很大。
Linux 系统中的同步
下面来聊一下 Linux 中的同步机制。早期的 Linux 内核只有一个大内核锁(Big Kernel Lock,BKL)。它阻止了不同处理器并发处理的能力。因此,需要引入一些粒度更细的锁机制。
Linux 提供了若干不同类型的同步变量,这些变量既能够在内核中使用,也能够在用户应用程序中使用。在地层中,Linux 通过使用atomic_set和atomic_read这样的操作为硬件支持的原子指令提供封装。硬件提供内存重排序,这是 Linux 屏障的机制。
具有高级别的同步像是自旋锁的描述是这样的,当两个进程同时对资源进行访问,在一个进程获得资源后,另一个进程不想被阻塞,所以它就会自旋,等待一会儿再对资源进行访问。Linux 也提供互斥量或信号量这样的机制,也支持像是mutex_tryLock和mutex_tryWait这样的非阻塞调用。也支持中断处理事务,也可以通过动态禁用和启用相应的中断来实现。
Linux 启动
下面来聊一聊 Linux 是如何启动的。
当计算机电源通电后,BIOS会进行开机自检(Power-On-Self-Test, POST),对硬件进行检测和初始化。因为操作系统的启动会使用到磁盘、屏幕、键盘、鼠标等设备。下一步,磁盘中的第一个分区,也被称为MBR(Master Boot Record)主引导记录,被读入到一个固定的内存区域并执行。这个分区中有一个非常小的,只有 512 字节的程序。程序从磁盘中调入 boot 独立程序,boot 程序将自身复制到高位地址的内存从而为操作系统释放低位地址的内存。
复制完成后,boot 程序读取启动设备的根目录。boot 程序要理解文件系统和目录格式。然后 boot 程序被调入内核,把控制权移交给内核。直到这里,boot 完成了它的工作。系统内核开始运行。
内核启动代码是使用汇编语言完成的,主要包括创建内核堆栈、识别 CPU 类型、计算内存、禁用中断、启动内存管理单元等,然后调用 C 语言的 main 函数执行操作系统部分。
这部分也会做很多事情,首先会分配一个消息缓冲区来存放调试出现的问题,调试信息会写入缓冲区。如果调试出现错误,这些信息可以通过诊断程序调出来。
然后操作系统会进行自动配置,检测设备,加载配置文件,被检测设备如果做出响应,就会被添加到已链接的设备表中,如果没有相应,就归为未连接直接忽略。
配置完所有硬件后,接下来要做的就是仔细手工处理进程0,设置其堆栈,然后运行它,执行初始化、配置时钟、挂载文件系统。创建init 进程(进程 1 )和守护进程(进程 2)。
getty 程序会在终端上输入
login:
等待用户输入用户名,在输入用户名后,getty 程序结束,登陆程序/bin/login开始运行。login 程序需要输入密码,并与保存在/etc/passwd中的密码进行对比,如果输入正确,login 程序以用户 shell 程序替换自身,等待第一个命令。如果不正确,login 程序要求输入另一个用户名。
Linux 内存管理
Linux 内存管理模型非常直接明了,因为 Linux 的这种机制使其具有可移植性并且能够在内存管理单元相差不大的机器下实现 Linux,下面我们就来认识一下 Linux 内存管理是如何实现的。
基本概念
如果某个进程需要将虚拟页面传入内存,但是此时没有可用的物理页面,那么操作系统必须丢弃物理内存中的另一个页面来为该页面腾出空间。
如果页面已经修改过,那么操作系统必须保留该页面的内容,以便以后可以访问它。这种类型的页面被称为脏页,当将其从内存中移除时,它会保存在称为交换文件的特殊文件中。相对于处理器和物理内存的速度,对交换文件的访问非常慢,并且操作系统需要兼顾将页面写到磁盘的以及将它们保留在内存中以便再次使用。
Linux 使用最近最少使用(LRU)页面老化技术来公平的选择可能会从系统中删除的页面,这个方案涉及系统中的每个页面,页面的年龄随着访问次数的变化而变化,如果某个页面访问次数多,那么该页就表示越年轻,如果某个呃页面访问次数太少,那么该页越容易被换出。
物理和虚拟寻址模式
Linux 假定页表分为三个级别。访问的每个页表都包含下一级页表
图中的 PDG 表示全局页表,当创建一个新的进程时,都要为新进程创建一个新的页面目录,即 PGD。
要将虚拟地址转换为物理地址,处理器必须获取每个级别字段的内容,将其转换为包含页表的物理页的偏移量,并读取下一级页表的页框号。这样重复三次,直到找到包含虚拟地址的物理页面的页框号为止。
Linux 运行的每个平台都必须提供翻译宏,这些宏允许内核遍历特定进程的页表。这样,内核无需知道页表条目的格式或它们的排列方式。
页面分配
在 Linux 中,最直观、最可见的部分就是文件系统(file system)。下面我们就来一起探讨一下关于 Linux 中国的文件系统,系统调用以及文件系统实现背后的原理和思想。这些思想中有一些来源于 MULTICS,现在已经被 Windows 等其他操作系统使用。Linux 的设计理念就是小的就是好的(Small is Beautiful)。虽然 Linux 只是使用了最简单的机制和少量的系统调用,但是 Linux 却提供了强大而优雅的文件系统。
Linux 文件系统基本概念
Linux 在最初的设计是 MINIX1 文件系统,它只支持 14 字节的文件名,它的最大文件只支持到 64 MB。在 MINIX 1 之后的文件系统是 ext 文件系统。ext 系统相较于 MINIX 1 来说,在支持字节大小和文件大小上均有很大提升,但是 ext 的速度仍没有 MINIX 1 快,于是,ext 2 被开发出来,它能够支持长文件名和大文件,而且具有比 MINIX 1 更好的性能。这使他成为 Linux 的主要文件系统。只不过 Linux 会使用VFS曾支持多种文件系统。在 Linux 链接时,用户可以动态的将不同的文件系统挂载倒 VFS 上。
Linux 中的文件是一个任意长度的字节序列,Linux 中的文件可以包含任意信息,比如 ASCII 码、二进制文件和其他类型的文件是不加区分的。
为了方便起见,文件可以被组织在一个目录中,目录存储成文件的形式在很大程度上可以作为文件处理。目录可以有子目录,这样形成有层次的文件系统,Linux 系统下面的根目录是/,它通常包含了多个子目录。字符/还用于对目录名进行区分,例如/usr/cxuan表示的就是根目录下面的 usr 目录,其中有一个叫做 cxuan 的子目录。
下面我们介绍一下 Linux 系统根目录下面的目录名
/bin,它是重要的二进制应用程序,包含二进制文件,系统的所有用户使用的命令都在这里/boot,启动包含引导加载程序的相关文件/dev,包含设备文件,终端文件,USB 或者连接到系统的任何设备/etc,配置文件,启动脚本等,包含所有程序所需要的配置文件,也包含了启动/停止单个应用程序的启动和关闭 shell 脚本/home,本地主要路径,所有用户用 home 目录存储个人信息/lib,系统库文件,包含支持位于 /bin 和 /sbin 下的二进制库文件/lost+found,在根目录下提供一个遗失+查找系统,必须在 root 用户下才能查看当前目录下的内容/media,挂载可移动介质/mnt,挂载文件系统/opt,提供一个可选的应用程序安装目录/proc,特殊的动态目录,用于维护系统信息和状态,包括当前运行中进程信息/root,root 用户的主要目录文件夹/sbin,重要的二进制系统文件/tmp, 系统和用户创建的临时文件,系统重启时,这个目录下的文件都会被删除/usr,包含绝大多数用户都能访问的应用程序和文件/var,经常变化的文件,诸如日志文件或数据库等
在 Linux 中,有两种路径,一种是绝对路径(absolute path),绝对路径告诉你从根目录下查找文件,绝对路径的缺点是太长而且不太方便。还有一种是相对路径(relative path),相对路径所在的目录也叫做工作目录(working directory)。
如果/usr/local/books是工作目录,那么 shell 命令
cp books books-replica
就表示的是相对路径,而
cp /usr/local/books/books /usr/local/books/books-replica
则表示的是绝对路径。
在 Linux 中经常出现一个用户使用另一个用户的文件或者使用文件树结构中的文件。两个用户共享同一个文件,这个文件位于某个用户的目录结构中,另一个用户需要使用这个文件时,必须通过绝对路径才能引用到他。如果绝对路径很长,那么每次输入起来会变的非常麻烦,所以 Linux 提供了一种链接(link)机制。
比如有两个工作账户 jianshe 和 cxuan,jianshe 想要使用 cxuan 账户下的 A 目录,那么它可能会输入/usr/cxuan/A,这是一种未使用链接之后的图。
使用链接后的示意如下
现在,jianshe 可以创建一个链接来使用 cxuan 下面的目录了。‘
当一个目录被创建出来后,有两个目录项也同时被创建出来,它们就是.和..,前者代表工作目录自身,后者代表该目录的父目录,也就是该目录所在的目录。这样一来,在 /usr/jianshe 中访问 cxuan 中的目录就是../cxuan/xxx
Linux 文件系统不区分磁盘的,这是什么意思呢?一般来说,一个磁盘中的文件系统相互之间保持独立,如果一个文件系统目录想要访问另一个磁盘中的文件系统,在 Windows 中你可以像下面这样。
两个文件系统分别在不同的磁盘中,彼此保持独立。
而在 Linux 中,是支持挂载的,它允许一个磁盘挂在到另外一个磁盘上,那么上面的关系会变成下面这样
挂在之后,两个文件系统就不再需要关心文件系统在哪个磁盘上了,两个文件系统彼此可见。
Linux 文件系统的另外一个特性是支持加锁(locking)。在一些应用中会出现两个或者更多的进程同时使用同一个文件的情况,这样很可能会导致竞争条件(race condition)。一种解决方法是对其进行加不同粒度的锁,就是为了防止某一个进程只修改某一行记录从而导致整个文件都不能使用的情况。
POSIX 提供了一种灵活的、不同粒度级别的锁机制,允许一个进程使用一个不可分割的操作对一个字节或者整个文件进行加锁。加锁机制要求尝试加锁的进程指定其要加锁的文件,开始位置以及要加锁的字节
Linux 系统提供了两种锁:共享锁和互斥锁。如果文件的一部分已经加上了共享锁,那么再加排他锁是不会成功的;如果文件系统的一部分已经被加了互斥锁,那么在互斥锁解除之前的任何加锁都不会成功。为了成功加锁、请求加锁的部分的所有字节都必须是可用的。
在加锁阶段,进程需要设计好加锁失败后的情况,也就是判断加锁失败后是否选择阻塞,如果选择阻塞式,那么当已经加锁的进程中的锁被删除时,这个进程会解除阻塞并替换锁。如果进程选择非阻塞式的,那么就不会替换这个锁,会立刻从系统调用中返回,标记状态码表示是否加锁成功,然后进程会选择下一个时间再次尝试。
如果此时一个进程尝试在第 6 个字节处加锁,此时会设置失败并阻塞,由于该区域被 A B C 同时加锁,那么只有等到 A B C 都释放锁后,进程才能加锁成功。
Linux 文件系统调用
许多系统调用都会和文件与文件系统有关。我们首先先看一下对单个文件的系统调用,然后再来看一下对整个目录和文件的系统调用。
为了创建一个新的文件,会使用到creat方法,注意没有e。
这里说一个小插曲,曾经有人问 UNIX 创始人 Ken Thompson,如果有机会重新写 UNIX ,你会怎么办,他回答自己要把 creat 改成 create ,哈哈哈哈。
这个系统调用的两个参数是文件名和保护模式
fd = creat("aaa",mode);
这段命令会创建一个名为 aaa 的文件,并根据 mode 设置文件的保护位。这些位决定了哪个用户可能访问文件、如何访问。
creat 系统调用不仅仅创建了一个名为 aaa 的文件,还会打开这个文件。为了允许后续的系统调用访问这个文件,这个 creat 系统调用会返回一个非负整数, 这个就叫做文件描述符(file descriptor),也就是上面的 fd。
如果在已经存在的文件上调用了 creat 系统调用,那么该文件中的内容会被清除,从 0 开始。通过设置合适的参数,open系统调用也能够创建文件。
下面让我们看一看主要的系统调用,如下所示
系统调用
描述
fd = creat(name,mode)
一种创建一个新文件的方式
fd = open(file, ...)
打开文件读、写或者读写
s = close(fd)
关闭一个打开的文件
n = read(fd, buffer, nbytes)
从文件中向缓存中读入数据
n = write(fd, buffer, nbytes)
从缓存中向文件中写入数据
position = lseek(fd, offset, whence)
移动文件指针
s = stat(name, &buf)
获取文件信息
s = fstat(fd, &buf)
获取文件信息
s = pipe(&fd[0])
创建一个管道
s = fcntl(fd,...)
文件加锁等其他操作