一、文件描述符
文件描述符是非负整数,当open或creat文件时,向进程返回文件描述符。
文件描述符0,1,2分别与标准输入,标准输出,标准出错相对应。
在POSIX标准中,0,1,2分别换成符号常数STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。这些常数定义在中。
二、open函数
#include <fcntl.h> int open(const char *pathname,int oflag,```); //仅当open创建文件时才使用第三个参数
|
oflag参数主要有:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
这些参数都定义在中。
open返回的文件描述符一定是最小的未用描述符数字。
三、creat函数
#include <fcntl.h> int creat(const char *pathname,mode_t mode); //若成功返回只写打开的文件描述符
|
等价于
#include <fcntl.h> int open(pathname,O_WRONLY|O_CREAT|O_TRUNC,mode);
|
其不足是创建的文件只能只写打开,可改为如下方式:
#include <fcntl.h> int open(pathname,O_RDWR|O_CREAT|O_TRUNC,mode);
|
三、close函数
#include <unistd.h> //注意头文件 int close(int filedes);
|
四、lseek函数
#include <unistd.h> off_t lseek(int filedes,off_t offset,int whence); //若成功返回新文件偏移量
|
参数offset与whence有关
若whence为SEEK_SET,偏移量为文件开始处的offset字节。
若whence为SEEK_CUR,偏移量为文件当前值加offset字节,offset可正可负。
若whence为SEEK_END,偏移量为文件长度加offset字节,offset可正可负。
#include "apue.h" #include <fcntl.h>
char buf1[] = "abcdefghij"; char buf2[] = "ABCDEFGHIJ";
int main(void) { int fd; if ((fd = creat("file.hole", FILE_MODE)) < 0) //别忘创建文件的"" err_sys("creat error"); if (write(fd, buf1, 10) != 10) err_sys("buf1 write error"); /* offset now = 10 */ if (lseek(fd, 16384, SEEK_SET) == -1) err_sys("lseek error"); /* offset now = 16384 */ if (write(fd, buf2, 10) != 10) err_sys("buf2 write error"); /* offset now = 16394 */ exit(0); }
|
(1)FILE_MODE在apue.h中定义:
#define FILE_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
S_IRUSR
Permits the file's owner to read it.
S_IWUSR
Permits the file's owner to write to it.
S_IRGRP
Permits the file's group to read it.
S_IWGRP
Permits the file's group to write to it.
(2)err_sys在apue.h中定义,也可用perror。
(3)测试lseek返回值最好测试其是否为-1,而不是测试其是否小于0,因为有些文件偏移量可以小于0。
五、read函数
#include <unistd.h> ssize_t read(int filedes,void *buf,size_t nbytes);
|
返回值为读取的字节数,如果读到文件的结尾则返回0,出错返回-1.
六、write函数
#include <unistd.h> ssize_t write(int filedes,const void *buf,size_t nbytes);
|
返回值通常等于nbytes的值,否则出错。
七、原子操作
pread和pwrite函数
#include <unistd.h> ssize_t pread(int filedes,void *buf,size_t nbytes,off_t offset); //返回值和read相同 ssize_ pwrite(int filedes,void *buf,size_t nbytes,off_t offset); //返回值和write相同
|
调用pread相当于顺序调用lseek和read函数。但有点不同:
• 调用pread时,无法中断其定位和读操作。
• 不更新文件指针。
同理,调用pwrite相当于调用lseek和write函数。
一般而言,原子操作指的是由多步组成的操作。如果原子操作执行,所有的子操作都执行。不执行的话所有的子操作都不执行。
八、dup和dup2函数
以下函数复制一个存在的文件描述符
#include <unistd.h> int dup(int filedes); int dup2(int filedes,int filedes2); //成功则均返回新的文件描述符,出错返回-1
|
对于dup2,我们通过filedes2参数指明新的文件描述符,如果filedes2已经打开,则先关闭。如果filedes等于filedes2,则dup2返回filedes2而不关闭它。
九、fcntl函数
fcntl函数能够改变一个已打开文件的性质
#include <fcntl.h> int fcntl(int filedes,int cmd,.../*int arg*/);
|
fcntl有以下几个作用(根据cmd参数的不同):
1.复制存在的描述符(cmd=F_DUPFD)
2.获得/设置文件描述符标志(F_GETFE/F_SETFD)
3.获得/设置文件状态标志(F_GETFL/F_SETFL)
4.获得/设置异步I/O所有权(F_GETOWN/F_SETOWN)
5.获得/设置记录锁(F_GETLK/F_SETLK或F_SETLKW)
对于这两种操作,可以获得或者设置文件的状态标志。F_GETFL时可以获得O_RDONLY、O_WRONLY、O_RDWR、O_APPEND、O_NONBLOCK、O_SYNC和O_ASYNC。对于前三种标志,必须使用屏蔽字O_ACCMODE,然后将结果与这三种标志一一比较。F_SETFL时只能设置后四种标志。
accmode = val & O_ACCMODE;
if (accmode == O_RDONLY) …
else if (accmode == O_WRONLY) …
else if (accmode == O_RDWR) …
else err_dump("unknown access mode");
if (val & O_APPEND) …
if (val & O_NONBLOCK) …
……
以下程序第二个参数指定文件描述符,并对于该描述符打印其所选择的文件标志说明。
#include "apue.h" #include <fcntl.h>
int main(int argc ,char *argv[]) { int val; if(argc!=2) err_quit("usage:a.out "); if((val=fcntl(atoi(argv[1]),F_GETFL,0))<0) err_sys("fcntl error for fd %d",atoi(argv[1])); switch(val&O_ACCMODE) { case O_RDONLY:printf("read only");break; case O_WRONLY:printf("write only");break; case O_RDWR:printf("read write");break; default:err_dump("unknown access mode"); } if(val&O_APPEND) printf(",append"); if(val&O_NONBLOCK) printf(",nonblocking"); #if defined(O_SYNC) if (val&O_SYNC) printf(",synchronous writes"); #endif #if !defined(_POSIX_C_SOURCE)&&defined(O_FSYNC) if(val&O_FSYNC) printf(",synchronous writes"); #endif putchar('\n'); exit(0); }
|
这里各标志位均在/usr/include/bits/fcntl里定义。
程序的各种运行结果如下:
$ ./a.out 0 < /dev/tty //省略了<前面的0 read only
|
Shell在执行a.out
时将它的标准输入重定向到/dev/tty
,并且是只读的。argv[1]
是0,因此取出文件描述符0(也就是标准输入)的File Status Flag,用掩码O_ACCMODE
取出它的读写位,结果是O_RDONLY
。注意,Shell的重定向语法不属于程序的命令行参数,这个命行只有两个参数,argv[0]
是"./a.out",argv[1]
是"0",重定向由Shell解释,在启动程序时已经生效,程序在运行时并不知道标准输入被重定向了。
$ ./a.out 1 > temp.foo //省略了>前面的1 $ cat temp.foo write only
|
Shell在执行a.out
时将它的标准输出重定向到文件temp.foo
,并且是只写的。程序取出文件描述符1的File Status Flag,发现是只写的,于是打印write only
,但是打印不到屏幕上而是打印到temp.foo
这个文件中了。
$ ./a.out 2 2>>temp.foo write only, append
|
Shell在执行a.out时将它的标准错误输出重定向到文件temp.foo,并且是只写和追加方式。程序取出文件描述符2的File Status Flag,发现是只写和追加方式的。
$ ./a.out 5 5<>temp.foo read write
|
Shell在执行a.out时在它的文件描述符5上打开文件temp.foo,并且是可读可写的。程序取出文件描述符5的File Status Flag,发现是可读可写的。
十、ioctl函数
#include <sys/ioctl.h> int ioctl(int filedes, int request, ...);
|
阅读(977) | 评论(0) | 转发(0) |