Linux系统编程 .pdf

上传人:C****o 文档编号:39731800 上传时间:2022-09-07 格式:PDF 页数:41 大小:358.52KB
返回 下载 相关 举报
Linux系统编程 .pdf_第1页
第1页 / 共41页
Linux系统编程 .pdf_第2页
第2页 / 共41页
点击查看更多>>
资源描述

《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

26、;Nr=write(fd,buffer,strlen(buffer);If(nr=-1)/错误 Unsigned long word=1720;Size_t count;Ssize_t nr;Count=sizeof(word);Nr=write(fd,&word,count);If(nr=-1)/*错误,并且设置 errno*/If(nr!=count)/*可能错误,但是errno 不设置*/写入操作中的追加模式,当fd 使用追加模式打开时O_APPEND,写操作将会在文件的末尾进行.非挂起写操作,当文件以非挂起模式O_NONBLOCK标记时,写写操作就会挂起,这种情况发生在socket时,

27、其他的错误代码,errno 标记当 write 返回-1 时,errno 的标记。EBADF,给定的文件描述符不可用,或者文件未打开Write()调用的行为,当一个 write 调用返回的时候,首先内核先将buffer 中的数据拷贝到一个内核buffer 中,但是不保证已经将buffer 中的数据已经拷贝到目的文件中了,当一个程序调用了一个write,Linux 内核将会进行一些检查,然后将将数据拷贝到buffer 中,然后在后台,内核将会将所有的buffer 汇集起来然后进行优化,然后将buffer 中的数据写入磁盘,这就有这么个问题就是,在调用完write 时,程序立即返回了,但是 buf

28、fer 中的数据并没有写入到磁盘上去,这是由于处理器的速度和磁盘的速度差别引起的,这个解决方式就是,让write 将 buffer 中的数据拷贝到一个内核提供的buffer 中去,然后write 返回,内核负责将其维持的 buffer 中的数据写到磁盘上去。任何的I/O 错误就爱那个会在writeback 阶段发生,如果是驱动器的错误,那么错误将不会被报告到请求write 调用的进程,的确,buffer 并没有与进程关联,多个进程可能同时共享同一块存数据的buffer,并且进程可能在将数据写入buffer 的时候,但是在数据从buffer 写入磁盘之前退出,所以为了保证风险最小,内核建立了一个

29、最大缓冲值,当向buffer 中写入某个数值的数据时,内核 将 会 将buffer中 的 数 据 写 入 磁 盘,用 户 能 够 配 置 这 个 值 通 过 修 改 文 件/proc/sys/vm/dirty_expire_centiseseconds,这个值是百分之一秒。同样同步的将文件缓冲中的数据写入磁盘与 write 写入 buffer 同步是可以实现的。下面介绍同步I/O,当使用 O_SYNC 标记打开文件时,暗示I/O 操作将会被同步。Int fd;fd=open(file,O_WRONLY|O_SYNC);If(fd=-1)perror(“open”);exit(1);Write

30、调用一般不是同步的,函数调用以及数据从缓冲写入磁盘这两个过程是不同步的,没有任何的关系,但是使用O_SYNC 标志打开文件的话就会强迫使这两个过程有关系。一种看O_SYNC 的角度就是当执行了一个 write 操作之后,将会隐式的调用一下fsync()。名师资料总结-精品资料欢迎下载-名师精心整理-第 5 页,共 41 页 -POSIX 定义了两个其他的与I/O 同步相关的open()标志,O_DSYNC,以及 O_RSYNC,linux 将这两个标记定义为与 O_SYNC 标记相同的含义。直接 I/O,现代操作系统实现了一个复杂的缓冲层,提供I/O 管理,这个缓冲层架设在应用程序以及设备之间

31、,但是高性能的软件可能希望绕开这个缓冲层,而直接的对设备进行操作,实现自己方式的I/O 管理。提供 O_DIRECT 标志打开文件将会指示内核,最小化的干预I/O 管理,当这个标志被提供的时候,将会表明,I/O 操作将会直接的从用户空间的缓冲区直接到设备,而绕过了操作系统的页面缓冲,所有的I/O 操作都将会是同步的,在未完成操作之前函数不会返回。关闭文件,当完成工作时,内核将会取消文件描述符到文件的映射关系,这个通过使用close 系统调用进行,#include int close(int fd);d调用成功的话就返回0,否则返回-1,并设置 errno 标志。当程序希望在关闭文件之前确保文件

32、已经被写入磁盘了,就需要使用上面的I/O 同步选项,当最后的打开的文件描述符被关闭时,内核中代表文件的结构体将会被释放。一般情况下,I/O 操作会沿着文件线性的向下进行,一些程序希望在文件中进行跳转,lseek 系统调用满足这个需要,#include#include off_t lseek(int fd,off_t pos,int origin);lseek函数的行为和其原始的origin 参数有关系,SEEK_CUR,将当前的文件位置设置为当前值加上pos,,pos 为 0 将会使得位置设置为当前位置,SEEK_END,当前的位置被设置为当钱的文件长度加上pos,pos 为 0 将会表明文件

33、位置为文件末尾,SEEK_SET 表明当前的文件位置为pos,pos 为 0 代表文件位置为文件开头,调用成功的话将会返回新的文件位置,否则返回-1。然后设置errno 标志。看代码:Off_t ret;Ret=seekl(fd,0,SEEK_SET);If(ret=(off_t)-1)/错误;Off_t ret;Ret=seekl(fd,0,SEEK_END);If(ret=(off_t)-1)/error;Off_t ret;Ret=seekl(fd,0,SEEK_CUR);If(ret=(off_t)-1)/error;当 seek 的位置超过了文件末尾的时候,从新位置进行的读操作将会返

34、回0.就是 EOF,如果在这个位置进行写操作,新的空间将会被追加,长度是文件的老长度以及新位置的这段,而且这段位置将会被用0 填充。这一段叫做文件空洞,文件空洞不占用任何的磁盘空间,这个就隐喻文件的总大小可能超过磁盘的最大空间,有空洞的文件叫做稀疏文件,稀疏文件能够节省空间以及提供性能因为在空洞部分的I/O 操作不进行任何的设备调用,在文件空洞部分读请求将会返回0,一旦函数调用失败,返回-1,然后设置errno 标记。EBADF,表示 fd 文件描述符不可用,EINVAL,origin 参数的不属于SEEK_CUR,SEEK_SET,SEEK_END,或者结果的文件位置为负值,E_OVERFL

35、OW,结果的文件位置不能够使用off_t 表示,这个只可能在32 位机上发生,ESPIPE,给定的文件描述符与一个不可seek的对象关联,例如pipe,或者 FIFO,或者是 socket。最大的文件位置就是off_t 的类型大小,很多的及其结构都是将这个定义为C long 类型的大小,Linux 下即使机器字长,作为lseek 的替代,Linux 系统提供两个read 和 write 的变种,与前任不同的是这两个增加了一个文件位置参数,但是函数调用完成后,文件位置不会更新。这两个函数为pread 以及 pwrite.#include#define _XOPEN_SOURCE 500 Ssiz

36、e_t pread(int fd,void*buffer,size_t,off_t pos);名师资料总结-精品资料欢迎下载-名师精心整理-第 6 页,共 41 页 -表示的是从 pos 位置开始读取count 数目的字节到buffer 中,对应的pwrite 调用是:#include#define _XOPEN_SOURCE 500 Ssize_t pwrite(int fd,const void*buffer,size_t count,off_t pos);从 pos 开始从 buffer 写入 count 字节到 fd 中,注意这两个函数都不会改变文件位置。一旦调用成功pread 与 p

37、write 都会返回读或者写的字节数目,返回值 0 表明 EOF,文件到头了,对于 pwrite,返回 0 表明未写入任何字节,一旦调用错误的话两个函数都会返回-1,并且设置errno 标志。Trancating,截断文件。Linux 提供两个用来截断文件的函数.#include#include Int ftruncate(int fd,off_t len);#include#include Int truncate(const char*path,off_t len);这两个函数都会将文件截断成指定len 长度的大小,不同的是ftruncate 的第一个参数是一个文件描述符,而 trunca

38、te 的第一个参数是文件路径,文件必须是可写的,这两个函数如果返回成功的话都会返回0,错误的话返回-1,然后设置 errno 标记,最常用的 truncate 操作就是讲文件截断成比文件元长度小的文件长度,一旦调用成功的话,文件长度就会被设置成len,然后 len 位置与原文件长度的位置部分的数据就会被忽略,然后就不能通过一个read 操作读取这段数据了,同样能够将文件截断成更大的大小,类似于seek空洞部分,而且这一段会被使用0 填充,这些操作都不会改变当前的文件位置.多路 I/O,程序通常需要监听不止一个文件描述符,文件需要监听键盘,内部进程通讯,等等,当在没有现成的情况下,单个进程无法同

39、时为好几个文件描述符服务,当同时打开好几个文件描述符的时候,但是当有一个文件描述符没有准备好时,比如,一个read调用,但是没有任何接收到的数据,程序将会挂起,其他的文件描述符就不能得到程序的服务了,即使挂起只有几秒,用户也可能感到收到了忽视,而且如果一直没有数据,程序将永远的挂起来,想想当有很多的文件描述符需要得到服务的时候,而一个调用使程序挂起,使其他的文件描述符不能得到服务,那么结果会很糟。前面描述的非挂起I/O,是这个问题的一种解决方案,使用非挂起调用,当调用时,函数将返回一个特殊的标记,而不是挂起等待,但是这个解决方案是不好的,原因之一,程序可能需要持续调用I/O 相关的操作,等待其

40、打开的一个文件描述符等待I/O,第二,如果程序能够休眠,将处理器分配给其他的进程,仅仅只当某系文件描述符需要进行I/O 操作时,程序才激活。多路 I/O 允许进程一次监听多个文件描述符,然后当其中的某一个准备好进行I/O 操作时接收到通知多路I/O 因此对于程序设计来说变得十分的重要。多路 I/O,将会在所监听的任意的一个文件描述符准备好进行I/O 操作时通知我们使得程序处于休眠状态,直到有通知打断休眠,从而将处理器释放到其他的工作上,使程序激活并知道那个文件描述符准备好了控制所有的文件描述符准备进行I/O 操作Linux 中提供了 3 个进行多路程序设计的接口,select(),poll()

41、,以及 epoll 系统调用。Select 系统调用能够提供一种机制,这种机制能够实现同步多路I/O 操作。#include#include 名师资料总结-精品资料欢迎下载-名师精心整理-第 7 页,共 41 页 -#include Int select(int n,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,struct timeval*timeout);FD_CLR(int fd,fd_set*set);FD_ISSET(int fd,fd_set*set);FD_SET(int fd,fd_set*set);FD_ZERO(fd_set

42、*set);Select 系统调用的行为是,如果函数被调用,在指定的文件描述符未准备好进行I/O 操作前,或者是一个设计人员制定的时间未到达,程序会被挂起。受到监视的文件描述符分为3 个集合,每一种到在等待一种特定的事件的发生,列在 readfds 集合中的 fd,将会监听是否一用来进行读操作的数据已经准备好了,。列在writefds 中的 fd 将会监听是否有写操作完成,列在 exceptfds 中的 fd 将会监听是否有异常发生,如果将其中的某一个设置为NULL,那么该事件将会不被监听。一旦成功返回,每一个 fd 集合,将会被修改,修改的结果就是,集合中只包含的是那些已经准备好进行I/O操

43、作的 fd,例如,有两个文件描述符,7,9 放在 readfds 集合中,当调用返回时,如果7 仍然在集合中,就表明这个文件描述符已经准备好进行read 操作了,然后9 没有包含在集合中,就表明9 这个文件描述符没有准备好进行I/O 操作,select 函数的第一个参数是n,表示的意思是值最大的文件描述符,函数的调用者负责检查那个文件描述符的值是最大的,然后将这个值加上1,然后传递给函数Timeout 参数是一个timeval 结构体类型的指针#include Struct timeval Long tv_sec;/秒Long tv_usec;/毫秒;如果这个参数被设置为NULL,select

44、 调用在经过tv_sec 秒,tv_usec 毫秒之后将会返回,即使没有文件描述符已经准备好进行I/O 操作,一旦select 调用返回的话,在不同的Unix 系统上这个结构体的状态是不统一的,因此他必须被重新的初始化,以及这个文件描述符集合,事实上,当前的Linux 版本将会自动的修改这些参数,因此,如果timeout 被设置成 5,那么在 2 的时候如果有一个文件描述符准备好进行I/O 操作的时候,tv.tv_sec 就在函数返回的时候的值为2。如果这两个值tv_sec,以及 tv_usec,都被设置成0,调用将会立即返回,报告任何的在函数调用时发生的事件,而不会等待任何的接下来的事件文件

45、描述符集合不是可以直接的操控的,而是通过一些功能宏进行操控的,很多的系统将集合实现为简单的位数组,FD_ZERO 将会从某个特定的文件描述符集合中删除所有的文件描述符,这个宏应该在select函数调用之前调用,fd_set writefds;FD_ZERO(&writefds);宏 FD_SET 将会给某个特定的文件描述符集合中添加一个文件描述符,宏 FD_CLR 将会从一个给定的文件描述符集合中去除某个文件描述符。FD_SET(fd,&writefds);/添加一个文件描述符给集合中。FD_CLR(fd,&writefds);/从文件描述符中去除fd 涉及良好的代码应该很少使用FD_CLR

46、FD_ISSET,将会测试是否是否一个文件描述符是某个给定集合的医一员,如果是其中的一员的话将会返回非零值整数,如果不是其一员的话将会返回0,FD_ISSET 在 select 调用返回之后调用,这个用来测试是否某个给定的文件描述符已经准备好进行I/O 操作了,名师资料总结-精品资料欢迎下载-名师精心整理-第 8 页,共 41 页 -If(FD_ISSET(fd,&writefds)/fd已经准备好了由于文件描述符是静态创建的,所以给里面添加的最大文件描述符的数目是限制的,以及限定了一个文件描述符的最大值,这两个值在Linux 系统上都是1024。一旦成功返回,返回值包含准备好进行I/O 操作

47、的文件描述符的数目,在所有的3 个集合中,如果timeout值被提供了返回值可能是0,一旦失败,返回-1,然后设置 errno 标记,EBADF set 中提供了一个非法的文件描述符EINTR 在等待的过程中一个信号发生了,函数能够被重新调用EINV AL 参数 n 是负数,或者是给定的 timeout 不合法。ENOMEM,完成请求的内存不可用。下面介绍一个select 函数的使用实例。#include#include#include#include#define TIMEOUT 5/*select timeout in seconds*/#define BUF_LEN 1024/*read

48、 buffer in bytes*/int main(void)struct timeval tv;fd_set readfds;int ret;/*Wait on stdin for input.*/FD_ZERO(&readfds);FD_SET(STDIN_FILENO,&readfds);/*Wait up to five seconds.*/tv.tv_sec=TIMEOUT;tv.tv_usec=0;/*All right,now block!*/ret=select(STDIN_FILENO+1,&readfds,NULL,NULL,&tv);if(ret=-1)perror(s

49、elect);return 1;else if(!ret)printf(%d seconds elapsed.n,TIMEOUT);return 0;/*Is our file descriptor ready to read?*(It must be,as it was the only fd that*we provided and the call returned 名师资料总结-精品资料欢迎下载-名师精心整理-第 9 页,共 41 页 -*nonzero,but we will humor ourselves.)*/if(FD_ISSET(STDIN_FILENO,&readfds)c

50、har bufBUF_LEN+1;int len;/*guaranteed to not block*/len=read(STDIN_FILENO,buf,BUF_LEN);if(len=-1)perror(read);return 1;if(len)buflen=0;printf(read:%sn,buf);return 0;fprintf(stderr,This should not happen!n);return 1;Select 系统调用能够用来产生一定数量时间的程序休眠,使用的方式就是提供一个非0 的 timeout,以及 0值的 sets,例如:struct timeval tv

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高考资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com