一、open

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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才会创建文件,否则文件不存在就出错返回。

  • ww+方式fopen一个文件时,如果文件已存在就截断为0字节,而open一个文件时必须明确指定O_TRUNC才会截断文件,否则直接在原来的数据上改写。

参数mode表示权限

注:关于linux的文件权限,参考http://dhjboy.blog.hexun.com/28912287_d.html

  每个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_IRUSRS_IWUSR等宏定义按位或起来表示

 但所使用的mode并不是最终的结果,而是与shell 的 umask 进行&~运算后的结果,如果比如umask为022,而mode为0777,那么最终结果为 -rwxr-xr-x

 

二、 read

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);/*从打开的文件中读数据*/
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0

三、write

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

返回值:成功返回写入的字节数,出错返回-1并设置errno

四、seek

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

参数offsetwhence的含义和fseek的参数相同

五、fcntl改变已打开文件的属性

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
int fcntl(int fd, int cmd, struct flock *lock);

例:

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>

#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的重定向功能在该描述符上打开文件,然后用fcntlF_GETFL命令取出File Status Flag并打印。

#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    int val;
    if (argc != 2) {
        fputs("usage: a.out <descriptor#>\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设置.

#include <sys/ioctl.h>

int ioctl(int d, int request, ...);

 

七、mmap

mmap可以把文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存地址,对文件的读写可以直接用指针来做而不需要read/write函数。(跟据我的经验,并不是所有的文件系统能够使用mmap,jffs2就不行)

#include <sys/mman.h>

 void *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 <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>

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

 

 

 

 

posted on 2012-10-23 22:22  林德伟  阅读(287)  评论(0编辑  收藏  举报