一、open
#include#include #include int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);//返回值:成功返回新分配的文件描述符,出错返回-1并设置errno
open函数在C代码里面的实际声明为 int open(const char *pathname, int flags, ...);
其中flags的主先参数有,
-
O_RDONLY
只读打开 -
O_WRONLY
只写打开 -
O_RDWR
可读可写打开
这三个参数是相互排斥的,只能选其中之一,然后可以加上其他的附加参数
-
O_APPEND
表示追加。如果文件已有内容,这次打开文件所写的数据附加到文件的末尾而不覆盖原来的内容。 -
O_CREAT
若此文件不存在则创建它。使用此选项时需要提供第三个参数mode
,表示该文件的访问权限。 -
O_EXCL
如果同时指定了O_CREAT
,并且文件已存在,则出错返回。 -
O_TRUNC
如果文件已存在,并且以只写或可读可写方式打开,则将其长度截断(Truncate)为0字节。 -
O_NONBLOCK
对于设备文件,以O_NONBLOCK
方式打开可以做非阻塞I/O(Nonblock I/O),非阻塞I/O在下一节详细讲解。
注意open
函数与C标准I/O库的fopen
函数有些细微的区别:
-
以可写的方式
fopen
一个文件时,如果文件不存在会自动创建,而open
一个文件时必须明确指定O_CREAT
才会创建文件,否则文件不存在就出错返回。 -
以
w
或w+
方式fopen
一个文件时,如果文件已存在就截断为0字节,而open
一个文件时必须明确指定O_TRUNC
才会截断文件,否则直接在原来的数据上改写。
参数mode表示权限
注:关于linux的文件权限,参考
每个linux 文件都有四种权限,可读r,可写w,可执行x(当为目录时,x 表示可以cd到此目录)和无权限。不同的用户类对同一个文件又享有不同的权限。文件用户分为所有者用户、所有者所属组用户、其他用户。
在shell下,输入 ls -l 命令,则会列出当前文件与目录
ls drwxr-xr-x 24 lynd lynd 4096 2012-09-29 16:32 soft-rw-r--r-- 1 lynd lynd 50 2012-09-28 16:29 svn-commit.tmp
每一行的开头如“drwxr-xr-x”、“-rw-r--r--”,第一位表示文件类型,-表示文件,d表示目录
2-4位表示文件所有者的权限,u权限
5-7位表示文件所有者所属组成员的权限,g权限 8-10位表示所有者所属组之外的用户的权限,o权限 2-10位的权限总和有时称为a权限所以,可以得出:
soft是一个文件夹,所有者用户有全部的权限,所有者所属组用户有读与cd权限,其他用户则只有cd权限。
svn-commit.tmp是一个文件,所有者用户有读写权限,所有者所属组用户有与其他用户只有读的权限。
文件权限的数字表示法: 将r、w和x分别用4、2、1来代表
原始权限 | 转换为数字 | 数字表示法 |
rwxrwxr-x | (421)(421)(401) | 775 |
rwxr-xr-x | (421)(401)(401) | 755 |
回到mode参数,mode就是用来设置所创建文件的权限了,可以用八进制数表示,比如0644表示-rw-r--r--
,也可以用S_IRUSR
、S_IWUSR
等宏定义按位或起来表示
但所使用的mode并不是最终的结果,而是与shell 的 umask 进行&~运算后的结果,如果比如umask为022,而mode为0777,那么最终结果为 -rwxr-xr-x
二、 read
#includessize_t read(int fd, void *buf, size_t count);/*从打开的文件中读数据*/
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0
三、write
#includessize_t write(int fd, const void *buf, size_t count);
返回值:成功返回写入的字节数,出错返回-1并设置errno
四、seek
#include#include off_t lseek(int fd, off_t offset, int whence);
参数offset
和whence
的含义和fseek的参数相同
五、fcntl改变已打开文件的属性
#include#include int fcntl(int fd, int cmd);int fcntl(int fd, int cmd, long arg);int fcntl(int fd, int cmd, struct flock *lock);
例:
#include#include #include #include #include #define MSG_TRY "try again\n"int main(void){ char buf[10]; int n; int flags; flags = fcntl(STDIN_FILENO, F_GETFL); flags |= O_NONBLOCK; if (fcntl(STDIN_FILENO, F_SETFL, flags) == -1) { perror("fcntl"); exit(1); }tryagain: n = read(STDIN_FILENO, buf, 10); if (n < 0) { if (errno == EAGAIN) { sleep(1); write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY)); goto tryagain; } perror("read stdin"); exit(1); } write(STDOUT_FILENO, buf, n); return 0;}
以下程序通过命令行的第一个参数指定一个文件描述符,同时利用Shell的重定向功能在该描述符上打开文件,然后用fcntl
的F_GETFL
命令取出File Status Flag并打印。
#include#include #include #include int main(int argc, char *argv[]){ int val; if (argc != 2) { fputs("usage: a.out \n", stderr); exit(1); } if ((val = fcntl(atoi(argv[1]), F_GETFL)) < 0) { printf("fcntl error for fd %d\n", atoi(argv[1])); exit(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: fputs("invalid access mode\n", stderr); exit(1); } if (val & O_APPEND) printf(", append"); if (val & O_NONBLOCK) printf(", nonblocking"); putchar('\n'); return 0;}
六、ioctl
ioctl用于
传送控制信息,例如,在串口线上收发数据通过read
/write
操作,而串口的波特率、校验位、停止位通过ioctl
设置,A/D转换的结果通过read
读取,而A/D转换的精度和工作频率通过ioctl
设置.
#includeint ioctl(int d, int request, ...);
七、mmap
mmap
可以把文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read
/write
函数。(跟据我的经验,并不是所有的文件系统能够使用mmap,jffs2就不行)
#includevoid *mmap(void *addr, size_t len, int prot, int flag, int filedes, off_t off); int munmap(void *addr, size_t len);
如果addr
参数为NULL
,内核会自己在进程地址空间中选择合适的地址建立映射。如果addr
不是NULL
,则给内核一个提示,应该从什么地址开始映射,内核会选择addr
之上的某个合适的地址开始映射。建立映射后,真正的映射首地址通过返回值可以得到。len
参数是需要映射的那一部分文件的长度。off
参数是从文件的什么位置开始映射,必须是页大小的整数倍(在32位体系统结构上通常是4K)。filedes
是代表该文件的描述符。
prot
参数有四种取值:
-
PROT_EXEC表示映射的这一段可执行,例如映射共享库
-
PROT_READ表示映射的这一段可读
-
PROT_WRITE表示映射的这一段可写
-
PROT_NONE表示映射的这一段不可访问
flag
参数有很多种取值,这里只讲两种,其它取值可查看mmap(2)
-
MAP_SHARED多个进程对同一个文件的映射是共享的,一个进程对映射的内存做了修改,另一个进程也会看到这种变化。
-
MAP_PRIVATE多个进程对同一个文件的映射不是共享的,一个进程对映射的内存做了修改,另一个进程并不会看到这种变化,也不会真的写到文件中去。
如果mmap
成功则返回映射首地址,如果出错则返回常数MAP_FAILED
。当进程终止时,该进程的映射内存会自动解除,也可以调用munmap
解除映射。munmap
成功返回0,出错返回-1。
例
#include#include #include int main(void){ int *p; int fd = open("hello", O_RDWR); if (fd < 0) { perror("open hello"); exit(1); } p = mmap(NULL, 6, PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { perror("mmap"); exit(1); } close(fd); p[0] = 0x30313233; munmap(p, 6); return 0;}
上面的例子也说明,文件关闭之后,并不影响mmap的使用,而munmap才能关闭mmap