3.13 fcntl函数 f c n t l函数可以改变已经打开文件的性质。 #include<sys/types.h> #include<unistd.h> #include<fcntl.h> int fcntl(intf i l e d e s,int c m d,.../* inta rg * / ) ; 返回:若成功则依赖于c m d(见下),若出错为- 1 在本节的各实例中,第三个参数总是一个整数,与上面所示函数原型中的注释部分相对应。但 是1 2 . 3节说明记录锁时,第三个参数则是指向一个结构的指针。 f c n t l函数有五种功能: • 复制一个现存的描述符(c m d=F D U P F D)。 • 获得/设置文件描述符标记(c m d = F G E T F D或F S E T F D)。 • 获得/设置文件状态标志(c m d = F G E T F L或F S E T F L)。 • 获得/设置异步I / O有权(c m d = F G E TO W N或F S E TO W N)。 • 获得/设置记录锁(c m d = F G E T L K , F S E T L K或F S E T L K W)。 我们先说明这十种命令值中的前七种( 1 2 . 3节说明后三种,它们都与记录锁有关)我们将涉 及与进程表项中各文件描述符相关联的文件描述符标志,以及每个文件表项中的文件状态标志, 见图3 - 1。 • F_DUPFD 复制文件描述符f i l e d e s,新文件描述符作为函数值返回。它是尚未打开的各 描述符中大于或等于第三个参数值(取为整型值)中各值的最小值。新描述符与filedes 共享同 一文件表项(见图3 - 3)。但是,新描述符有它自己的一套文件描述符标志,其F D C L O E X E C 文件描述符标志则被清除(这表示该描述符在exec 时仍保持开放,我们将在第8章对此进行 讨论)。 第3章文件I/O 4 7 下载 • F_GETFD 对应于filedes 的文件描述符标志作为函数值返回。当前只定义了一个文件描 述符标志F D C L O E X E C。 • F_SETFD 对于filedes 设置文件描述符标志。新标志值按第三个参数(取为整型值)设置。 应当了解很多现存的涉及文件描述符标志的程序并不使用常数F D C L O E X E C,而是将此 标志设置为0 (系统默认,在e x e c时不关闭)或1 (在e x e c时关闭)。 • F_GETFL 对应于filedes 的文件状态标志作为函数值返回。在说明o p e n函数时,已说明 了文件状态标志。它们列于表3 - 2中。 表3-2 对于f c n t l的文件状态标志 文件状态标志说明 O R D O N L Y 只读打开 O W R O N L Y 只写打开 O R D W R 读/写打开 O A P P E N D 写时都添加至文件尾 O N O N B L O C K 非阻塞方式 O S Y N C 等待写完成 O A S Y N C 异步I / O(仅4 . 3 + B S D) 不幸的是,三个存取方式标志( O R D O N LY, O W R O N LY,以及O R D W R )并不各占1位。(正 如前述,这三种标志的值各是0、1和2,由于历史原因。这三种值互斥—一个文件只能有这 三种值之一。)因此首先必须用屏蔽字O A C C M O D E取得存取方式位,然后将结果与这三种值 相比较。 • F_SETFL 将文件状态标志设置为第三个参数的值(取为整型值)。可以更改的几个标志是: O A P P E N D,O N O N B L O C K,O S Y N C和O A S Y N C。 • F_GETOWN 取当前接收S I G I O和S I G U R G信号的进程I D或进程组I D。1 2 . 6 . 2节将论述这 两种4 . 3 + B S D异步I / O信号。 • F_SETOWN 设置接收S I G I O和S I G U R G信号的进程I D或进程组I D。正的a rg指定一个进 程I D,负的a rg表示等于a rg绝对值的一个进程组I D。 f c n t l的返回值与命令有关。如果出错,所有命令都返回- 1,如果成功则返回某个其他值。 下列三个命令有特定返回值:F_DUPFD,F_GETFD, F_GETFL以及F G E TO W N。第一个返回新 的文件描述符,第二个返回相应标志,最后一个返回一个正的进程I D或负的进程组I D。 实例 程序3 - 4取指定一个文件描述符的命令行参数,并对于该描述符打印其文件标志说明。 程序3-4 对于指定的描述符打印文件标志 4 8 U N I X环境高级编程 下载 注意,我们使用了功能测试宏_ P O S I X S O U R C E,并且条件编译了P O S I X . 1中没有定义的 文件存取标志。下面显示了从K o r n S h e l l调用该程序时的几种情况: $ a.out 0 < /dev/tty read only $ a.out 1 > temp.foo $ cat temp.foo write only $ a.out 2 2>>temp.foo write only, append $ a.out 5 5<>temp.foo read write K o r n S h e l l子句5 < > t e m p . f o o表示在文件描述符5上打开文件t e m p . f o o以供读、写。 实例 在修改文件描述符标志或文件状态标志时必须谨慎,先要取得现在的标志值,然后按照希 望修改它,最后设置新标志值。不能只是执行F S E T F D或F S E T F L命令,这样会关闭以前设 置的标志位。 程序3 - 5是一个对于一个文件描述符设置一个或多个文件状态标志的函数。 程序3-5 对一个文件描述符打开一个或多个文件状态标志 第3章文件I/O 4 9 下载 如果将中间的一条语句改为: val &= ˜flags; /*turn flags off*/ 就构成了另一个函数,我们称其为c l r f l,并将在后面某个例子中用到它。此语句使当前文件 状态标志值v a l与f l a g s的反码逻辑与运算。 如果在程序3 - 3的开始处,加上下面一行以调用s e t f l,则打开了同步写标志。 set_fl(STDOUT_FILENO, O_SYNC); 这就造成每次w r i t e都要等待,直至数据已写到磁盘上再返回。在U N I X中,通常w r i t e只是 将数据排入队列,而实际的I / O操作则可能在以后的某个时刻进行。数据库系统很可能需要使用 O S Y N C,这样一来,在系统崩溃情况下,它从w r i t e返回时就知道数据已确实写到了磁盘上。 程序运行时,设置O S Y N C标志会增加时钟时间。为了测试这一点,运行程序3 - 3,它从 磁盘上的一个文件中将1 . 5 M字节复制到另一个文件中。然后,在此程序中设置O S Y N C标志, 使其完成上述同样的工作,将两者的结果进行比较,见表3 - 3。 表3-3 用同步写( O S Y N C )的时间结果 操作用户C P U(秒) 系统C P U(秒) 时钟时间(秒) 取自表3-1 BUFFSIZE=8192的读时间0 . 0 0 . 3 0 . 3 盘文件的正常w r i t e 0 . 0 1 . 0 2 . 3 O S Y N C设置的盘文件w r i t e 0 . 0 1 . 4 1 3 . 4 表3 - 3中的3行都是在B U F F S I Z E为8 1 9 2的情况下测量得到的。表3 - 1中的结果所测量的情况 是读一个磁盘文件,然后写到/ d e v / n u l l,所以没有磁盘输出。表3 - 3中的第2行对应于读一个磁 盘文件,然后写到另一个磁盘文件中。这就是为什么表3 - 3中第1,2行有差别的原因。在写磁 盘文件时,系统时间增加了,其原因是内核需要从进程中复制数据,并将数据排入队列以便由 磁盘驱