Linux系统编程 .pdf
《Linux系统编程 .pdf》由会员分享,可在线阅读,更多相关《Linux系统编程 .pdf(41页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、Linux 系统编程在 linux 系统中所有的东西都是文件,很多的系统会话都是通过对文件的读写进行的,所以操作文件是很重要的,而操作文件的第一步就是要先打开文件,一个打开的文件通过一个文件描述符(file descriptor)的东西引用,在Linux 系统中文件描述符是由一个整数控制的,文件描述符可以直接被用户使用来进行文件操作,一般都有的几步就是打开,执行操作,关闭。我们叫做文件的东西,Linux 将其标签为regular files(常规文件),一个 regular file(常规文件)包含的 n字节的数据,这些字节被组织进一个线性数组,叫做字节流,这些字节可能有任意的值,可能以任意的
2、方式组织进文件中,Linux 系统不强制任何超越字节流的文件结构,文件中的任意字节能够被读以及写,这读写操作都是从一个特别的字节开始的,在概念上理解为文件的位置,术语叫做文件位置或者是文件偏移,当一个文件首次被打开时,文件位置是0,由于文件中的字节是一字节一字节的读或者写的,性质上文件位置也是增加的,文件位置也可以手动的设置为某一值,在文件末尾后端写入字节将会导致这些超越的直接被填充成0,虽然可以在文件末尾后面写入字节,但是不能在文件开头之前写入字节,向文件中的某一个字节写入新的值将会导致该字节的老值被覆盖,而不会导致文件被扩大,文件的大小其实就是该文件所占的字节数组的大小,文件的大小可以使用
3、一个叫做切断(truncation)的操作来进行,切断能够能够将一个文件切割的比原来小,也可以将一个文件变的比原来小,一个文件能够不止一次的被相同的或者不同的进程打开,每一个被打开的文件都被分配了一个独立的文件描述符,进程之间能够共享他们拥有的文件描述符,多个进程允许同时读取或者写入同一个文件,虽然文件通常是通过文件名来获取,但是事实上他们不是通过这些名字,文件通过一个inode 来引用,这个inode 被分配了一个独立的数字值,这个值叫做inode号,inode 中存贮的是与文件相关的原始数据,比如修改事件记录,文件所有人,文件类型,文件大小,以及文件数据位置,但是就是不存文件名!Inode
4、 即是一个实实在在存在的对象,存在于 Unix 风格的文件系统中,并且还是一个概念上的事物,在Linux 内核中使用了一个数据结构来表示。文件夹的作用就是提供一个文件名和其inode 号的映射,在磁盘上这个映射存在的方式就是一个表,哈希表,文件夹同样被认为是一个一般的文件,只不过这个文件中包含的是文件名到inode 号的映射,内核直接使用这个映射完成文件名到inode 号的解析。当用户请求打开一个文件时,首先内核先打开包含该文件名的文件夹,然后搜索给定的文件名,从文件名中,内核获取的是inode 号,从 inode 号,内核找到inode,inode 包含该文件的关联信息,包括这个文件中的数据
5、在磁盘上的位置,最初,在linux 系统上只有唯一的一个文件夹,root 文件夹,但是是加上一个系统中往往含有很多的文件夹,那么系统是怎样的指导怎样在这些文件夹中寻找某个文件的。前面提到,文件夹是文件,他们拥有关联的inode,文件夹中的链接能够指向其他文件夹的inode,意味着文件夹能嵌入到其他的文件夹中,构成一个文件夹层次系统。当内核要打开一个路径名时,他将便利该路径中的每一个文件夹来寻找下一个文件夹的inode,在前面的实例/home/blackbeard/landscanping.txt 中,内核从/开始,得到了home 的 inode,然后获得,blackbeard 的 inode然
6、后得到 landscanping.txt 的 inode,这叫做文件夹或者路径名解析,Linux 系统使用一个缓冲,来存储文件夹解析的结果,路径名从root 文件夹开始的叫做绝对路径名,有的是相对的,例如(X/Y),当提供了一个相对路径时,内核从当前工作文件夹开始解析,从当前的文件夹开始查询。虽然文件被当做是一般的文件,但是系统不允许将他们像一般的文件一样打开操作,要操作文件夹,必须使用特定的系统调用,这些系统调用将会进行一定的链接增添和删除操作,设想如果用户能轻易地操作文件夹的话,一个简单的错误将会摧毁整个文件系统。概念上来说,多个文件名对应到一个inode 上是允许的当有多个文件名被映射到
7、同一个文件inode 时,叫做硬链接,硬链接容忍复杂的文件系统结构,硬链接能在同一个文件夹下,或者这两个以上的文件夹下,内核,仅仅将路径名解析到正确的inode 下,例如,一个特定的志向一个特定的数据块能够被硬链接到/home/bluebeard/map.txt 以及/home/blackbeard/treasure.txt 删除一个文件包括,从文件夹中解除链接,这个只需要仅仅从文件夹中去除文件名以及对应的inode 对。由于 linux 支持硬链接,但是,文件系统不能摧毁inode 以及其对应的关联数据,为了保证在该文件被删除名师资料总结-精品资料欢迎下载-名师精心整理-第 1 页,共 41
8、 页 -之前将所有链接都删除,所以每一个inode 都含有一个链接计数器,跟踪连接到该inode 上的链接数目,当一个路径名被消除链接之后,链接计数器减一,当计数器到0 时,该文件被删除。硬链接不能跨越文件系统,因为一个inode 号,在越过该inode 自己的文件系统后就显得无意义了,为了能使链接能跨文件系统象征链接是,有其自己的inode 以及数据块,其包含完整的连接到文件的路径名,意味着象征链接能够指向任意位置,包括在任意文件系统上的文件以及文件夹,甚至能链接到一个不存在的文件特殊文件是使用文件表示的内核对象,Linux 支持 4 总不同的特殊文件,块设备文件,字符设备文件,名字管道,以
9、及 Unix domain sockets,特殊文件使特殊的抽象与文件系统融合,Linux 提供创建特殊文件的系统调用,Unix 系统中的设备交互是通过操作设备文件进行的,设备文件看起来像一般的文件,设备文件能够打开,读取,写入,允许用户操作设备,无论设备是虚拟设备还是真正的设备,Unix 设备一般情况下被分为块设备以及字符流设备,每一种都有自己的特殊的设备文件。字符设备能够以现行字节队列的形式进行读取,设备驱动将字节一个接一个放如队列,用户接收到的字节顺序是字节被放入队列的顺序,键盘是一个字节设备的例子,如果用户打?peg?,程序希望从键盘读取p,e,g当读取完成时,设备返回EOF,丢失字符
10、,后者顺序错误都不是希望的结果,字符设备使用字符设备文件读取。块设备相反,以字节数组的形式读取,设备驱动将字节映射到一个可搜索的设备上,用户空间用户能够自由的在字节数组中读取任意一个字节,以任意的顺序,可能是字节12,然后 3,然后 5 等等。块设备一般都是存储设备,比如,硬盘,软盘,光盘,以及闪存,以及其他类型的块设备,他们都是通过块设备文件读取的,名字管道,通常叫做 FIFOs,是一种内部通讯机制(IPC),其提供文件描述符(file descriptror)之间通讯的管道,一般的管道是用来将一个程序的输出作为另外的一个管道的输入,他们通过一个系统调用在内存中创建,不存在于任何的文件系统中
11、,名字管道就像一般的管道一样,到那时是通过文件进行的,叫做 FIFO 特殊文件,相互无关的进程可以通过读取这些文件来进行通讯,socket 是另外的一种特殊文件,socket 是另外的一种高级的IPC,用来在两个不同的进程之间通讯,不仅仅是在同一台计算机,而且可以在不同的计算机之间进行,事实上,socket 构成了基本的网络编程的基础,Unix domain socket,用来在本地计算机通讯,而互联网通讯则需要主机名以及端口号等等来确定计算机位置。最小的块设备可寻址单元是扇区,扇区大小是2 的幂,而文件系统最小的可寻址单元叫做块,块是文件系统的一种抽象,而不是真正存在的,不像扇区一样,块的大
12、小是扇区的大小的2 的幂次倍,块一般比扇区大,但是必定比页面大小小,页面是内存最小可寻址单元。每一个进程包含多个执行线程,一个线程是一个进程中的活动单位,大部分的进程只有一个线程,叫做单线程进程,包含多个线程的进程叫做多进程,一个线程包含一个栈,这个栈存储的是局部变量,就像无线程系统中的进程栈一样,以及处理器状态,以及当前的对象代码中的位置标签,通常存贮在处理器的指令指针中,一个进程的大部分的剩余的部分都是线程共享的。Linux 系统将线程看作是一些需要共享资源的一般进程,更确切的说是进程空间,Linux 依照 POSIX1003.1标准实现线程,当前的Linux 线程实现方式是glibc 库
13、德一部分,叫做POSIX 线程库(NPTL)每一个进程都由一个独立的正整数标记,这个整数叫做进程ID(pid),Linux系统中进程组成了一个严格的层次叫做进程树,Linux 进程树的根部是第一个进程叫做init 进程,新的进程通过系统调用fork()来产生,这个系统调用.允许进程之间交换信息是很重要的,Linux 支持的 IPC 机制包括管道,名字管道,消息队列,共享内存头文件,Linux 系统编程包含一些有用的头文件,内核以及glibc 都提供一些系统级别编程的头文件,其包含标准 C 头文件,以及通常的Unix 头文件。错误控制,系统编程中的错误是通过函数的返回值来进行辨别的,然后通过一个
14、特殊的变量描述,errno。Glibc 完全的为库以及系统调用提供errno 支持,函数通过返回值为其调用者回报错误信息,通常是返回-1,但是不提供什么引起错误,errno 变量用来找错误原因。函数 perror(const char*str)将会在标准错误终端打印当前errno 表示的错误信息前面加上参数前缀,名师资料总结-精品资料欢迎下载-名师精心整理-第 2 页,共 41 页 -例如 if(close(fd)=-1)perror(“close”);一般参数带的是失败函数的函数名。C 库提供了 strerror 以及 strerror_r()函数,原型为 char*strerror(itn
15、 errnum)以及 int strerror_r(int errnum,char*buf,size_t len);前面的函数返回一个字符串指针,描述的是errnum 给的错误,这个字符串可能不能被程序修改,但是能被随后的perror(),以及 strerror()函数修改内核为每一个进程维持了一个打开的文件记录表,这个记录表通过非负整数索引,列表中的每一项包含一个打开的文件的信息,包含一个指向该文件inode 以及相关原始数据的指针,例如文件位置以及读取模式。默认情况下,子进程接收到一个父进程的文件记录表的拷贝。打开的文件记录表,以及读取模式,当前的文件位置,都是相同的,但是有一个不同是,子
16、进程关闭了文件的话不会影响其他进程的文件记录表,到那时可以看到让子进程与父进程共享父进程的文件记录表是可能的,文件描述符(file descriptor)使用 C int类型表示,每一个Linxu 进程都有一个最大能打开文件的上限,文件描述符从0 开始到最大值减一,除非现实的关闭,每一个进程默认情况下都有至少3 个文件描述符,0 是标准输入,1 为标准输出,2 为标准错误。最近本的读取文件的方式就是通过read(),write()系统调用,在一个文件能能够够背读取之前,他必须被打开或者穿件,通过open(),或者 create()系统调用,一旦完成了任务,必须通过close 系统调用关闭文件,
17、系统调用 open(),open()函数能够将给定的路径名映射到一个文件描述符上,文件位置被设置为0,文件根据提供的参数被打开。#include#include#include int open(const char*name,int flags);int open(const char*name,int flags,mode_t mode);flags 参数必须是O_RDONLY,O_WRONLY,O_RDWR 之一,标志该文件被打开来写,读,与读写,例如,打开/home/kidd/mam 来读:int fd;fd=open(“/home/kidd/mam”,O_RDONLY);if(fd=
18、-1)perror(“open”);一个被用来写的文件不能读,等等。还有一些其他的标志,如O_APPEND 表示打开文件并将文件指针放在文件末尾。O_ASYNC,一个信号(SIGIO)将会被产生,当某个特定的文件变得可读或者可写,这个标志只对终端或者sockets是可用的,不用于常规文件。O_CREAT,如果提供的文件名不存在在,内核将自动创建一个文件,如果存在,该标记将被忽略,除非O_EXCL 标记被给出。O_DIRECT 这个文件将会被打开来进行直接的I/O。O_DIRECTORY 如果提供的名字不是文件夹,open()调用将会失败,这个标志被opendir 库调用。O_EXCL 当给定
19、O_CREAT,这个标记将会导致open()失败如果系统调用 open()以及 creat()都将返回文件描述符如果成功的话,都将返回-1 如果调用失败。read()调用用来进行文件的读取,#include ssize_t read(int fd,void*buf,size_t len);每一次调用都将会从 fd 读取 len 长度的字节,然后将其放入缓存中,成功的话将返回实际读取的字节数,失败的话讲返回-1,然后 errno 被设置,然后文件中指针的位置会向前前进实际读取的字节长度。Read能够返回一个比len小的返回值,这表明小于len 长度的字节是可读的,这个系统调用可能会被中断信号中断
20、。当使用 read 时如果其返回值为0,表明 EOF,暗示的意思是,文件指针的位置超过了最大的偏移数,因此不能读取任何东西了,如果需要读取len 长度,但是实际上没有那么多的可读长度,read 调用将会挂起来名师资料总结-精品资料欢迎下载-名师精心整理-第 3 页,共 41 页 -直到某些字节变得可用。EOF 和挂起来是不一样的饿,EOF 表明文件已经到头了,挂起来表明read调用正在等待更多的数据,比如从socket 或者是设备文件读取数据。如果 read 调用被中断信号阻止,将会返回-1,然后 errno 将会被设置成EINTR,read 调用会引起很多的可能结果,比如,返回值等于len
21、表明正如我们所希望的那样,返回值小于len,但是大于0,表明读取的字节存储在缓存中,这种情况的原因可能是由于在文件读取的过程中中断发生,read 终止,导致读取的字节小于 len 但是大于 0,或者是要读取len 时 EOF 到了表明文件完了。返回值是 0,表明是 EOF,没东西读取了。Read 调用将会被挂起来,如果当前没有可用数据的话,调用见会被挂起来,比如是socket 的时候,从服务器端发送信息到客户端,在没有得到数据之前,客户端的程序就处于挂起状态等待数据的输入,但是在非挂起模式下将不会出现这种情况。在任何的字节被读取之前,中断发生将会导致,没有字节读入,调用返回-1,errno 标
22、志被设置为EINTR.Read 由于没有数据读入而被挂起,请求需要重新被认证,。这时调用返回-1,errno 被设置为EANGIN,这个只在非挂起模式。非挂起的 read 调用,在挂起模式下,当没有数据可读时,程序将会挂起等待数据的到来,现在不希望程序挂起,而使当没有数据到来时程序返回,这叫做免挂起I/O,一个 errno 值是值得检查的,就像前面讨论的如果某个文件时使用非挂起模式打开的,O_NONBLOCK标志,当没有数据读取时read 调用将会返回-1,然后将 errno 标记设置为EAGAIN,当执行非挂起读操作时,必须检查EAGAIN 标记Ssize_t nr;Nr=read(fd,b
23、uffer,len);If(nr=-1)If(errno=EINTR)Continue;If(errno=EAGAIN)/do something 当 read 调用失败的时候,也有一些其他的errno 标志,EBADF,给定的文件描述符不可用,或者没有打开。EFAULT,buffer 指针没有在调用进程的地址空间。EINV AL,文件描述符被映射到一个不允许进行读操作的对象上。EIO,一个更低级的I/O 错误发生了。限制大小的读操作,size_t 类型是用来存字节大小值,而 ssize_t 类型是 size_t的有符号版本,表明可以取负值,一般用来表示错误,在32 位的系统中,分别对应的就是
24、uint,以及 int,size_t 允许取的最大值就是SIZE_MAX,ssize_t 允许的最大值就是SSIZE_MAX,如果 len 比 SSIZE_MAX大的话,一次返回的值将可能超过 SSIZE_MAX 的值,将会使返回值不可确定,大多数的 Linux 系统中 SSIZE_MAX的值是 LONG_MAX,32 位系统中是 0 x7fffffff,这个值是相当大的,但是为了保险起见,添加下面的代码能够帮助你If(len SSIZE_MAX)len=SSIZE_MAX;使用 write 调用写文件,最基本的写文件系统调用就是write(),名师资料总结-精品资料欢迎下载-名师精心整理-第
25、 4 页,共 41 页 -#include ssize_t write(int fd,const void*buf,size_t count);write操作将会从buffer 中提起最多count字节,然后写到当前的文件位置上,不提供搜寻操作的文件将一直会从文件开头进行写操作,write 调用一旦成功,将返回写入的字节数目,并且将文件位置更新,一旦失败,返回-1,并设置 errno 标志,一个 write调用也能返回0,但是这个并不像read 返回 0 那样有意义,仅仅表明写入了0 字节,看例子:Const char*buffer=“we will come back”;Ssize_t nr
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux系统编程 2022 Linux 系统 编程
限制150内