Linux File、File Directory IO Operation Summary(undone)

目录

1. 引言
2. Linux下文件操作API
3. Linux下文件目录操作API
4. Linux下的其他设备操作API

 

1. 引言

Linux支持多种文件系统,如ext、ext2、minix、iso9660、msdos、fat、vfat、nfs等。在这些具体文件系统的上层,Linux提供了虚拟文件系统(VFS)来统一它们的行为,虚拟文件系统为不同的文件系统与内核的通信提供了一致的接口

Linux的文件编程有两种途径: 基于Linux系统调用、基于C库函数。这两种编程所涉及到文件操作有新建、打开、读写和关闭,对随机文件还可以定位

 

2. Linux下文件操作API

值得注意的是,在现有的linux文件系统相关编程中,存在着两套文件操作API体系(我们时常可以见到open、也可以见到fopen),我们需要明白它们之间的区别

在Linux平台下对文件编程可以使用两类函数
1. Linux操作系统文件API(非缓冲文件系统API)
    1.1 特点
        1) 依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出
        2) 不设置文件结构体指针,只能通过原始的方式读写二进制文件,但效率高、速度快
        1) 属于低级IO 
        2) 调用open()后返回一个文件描述符(用户程序区的)
        3) 无缓冲,与read,write等配合使用   
        5) 操作系统设备需要使用open,文件描述符是linux下的一个概念,linux下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作文件,因此,设备文件不可以当成流式文件来用,只能用open 
    1.2 包含API
        1) open
        2) close
        3) read
        4) write
        5) getc
        6) getchar
        7) putc
        8) putchar 

2. C语言I/O库函数(缓冲文件系统API)
    2.1 特点
        1) 缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问
        2) 可以对包括字符、字符串、格式化数据、二进制数据进行读写
        3) 在读写的时候会在内存开辟一个"缓冲区",供程序中的每一个文件使用
        4) 当执行读文件的操作时,从磁盘文件将数据先读入内存"缓冲区", 装满后再从内存"缓冲区"依此读入接收的变量
        3) 当执行写文件的操作时,先将数据写入内存"缓冲区",待内存"缓冲区"装满后再写入文件,内存"缓冲区"的大小,影响着实际操作外存的次数,内存"缓冲区"越大,则操作外存的次数就少,执行速度就快、效率高
        4) 属于高级IO
        5) 调用fopen()后返回一个文件指针
        6) 与fread,fwrite等配合使用
        7) 缓冲文件系统API是在非缓冲文件系统API的基础上扩充而来的,在大多数情况下,我们编程使用"缓冲文件系统API"
        8) 一般情况下fopen打开普通文件,用open打开设备文件 
    2.2 包含API
        1) fopen
        2) fclose
        3) fread
        4) fwrite
        5) fgetc
        6) fgets
        7) fputc
        8) fputs
        9) freopen
        10) fseek
        11) ftell
        12) rewind

0x1: 文件的创建

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

1. int open(const char *pathname, int flags);
2. int open(const char *pathname, int flags, mode_t mode);

1. pathname
pathname是我们要打开的文件名(包含路径名称,缺省是认为在当前路径下面)
2. flags
flags可以去下面的一个值或者是几个值的组合(使用"|"将多个flag组合起来)  
    1) O_RDONLY: 以只读的方式打开文件
    2) O_WRONLY: 以只写的方式打开文件  
    3) O_RDWR: 以读写的方式打开文件
    4) O_APPEND: 以追加的方式打开文件
    5) O_CREAT: 如果文件不存在,则创建一个文件
    6) O_EXEC: 如果使用了O_CREAT而且文件已经存在,就会发生一个错误
    7) O_NOBLOCK: 以非阻塞的方式打开一个文件  
    8) O_TRUNC: 如果文件已经存在,则删除文件的内容 
    9) O_ASYNC: 使用一个"signal-driven I/O(信号驱动的IO模式)"打开文件,当文件描述符可用时(文件系统发出信号),open的调用者将获得事件通知
    10) O_CLOEXEC
    11) 
3. mode
要注意的是: mode参数只有在我们在flags中传入了"O_CREAT"才有意义,即只有在新建新文件的时候才需要显示指定mode,用来表示文件的访问权限
    1) S_IRUSR: 用户可以读
    2) S_IWUSR: 用户可以写 
    3) S_IXUSR: 用户可以执行
    4) S_IRWXU: 用户可以读写执行 
    5) S_IRGRP: 组可以读    
    6) S_IWGRP: 组可以写 
    7) S_IXGRP: 组可以执行
    8) S_IRWXG: 组可以读写执行 
    9) S_IROTH: 其他人可以读
    10) S_IWOTH: 其他人可以写 
    11) S_IXOTH: 其他人可以执行
    12) S_IRWXO: 其他人可以读写执行 
    13) S_ISUID: 设置用户执行ID
    14) S_ISGID: 设置组的执行ID 

如果我们打开文件成功,open会返回一个"文件描述符(file discriptor)",我们以后对文件的所有操作就可以对这个文件描述符进行操作了

code

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

main()
{
    int fd, len;
    char str[LENGTH]; 
     fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /* 创建并打开文件 */
     if (fd) 
     {
          printf("open file successful!!\n");
          close(fd);
     } 
     close(fd);
    return;
}

Relevant Link:

http://www.tutorialspoint.com/unix_system_calls/open.htm

3. int creat(const char *pathname, mode_t mode);

creat() is equivalent to open() with flags equal to O_CREAT|O_WRONLY|O_TRUNC
creat(const char *pathname, mode_t mode) == open(const char *pathname, O_CREAT|O_WRONLY|O_TRUNC, mode_t mode)

4. int openat(int dirfd, const char *pathname, int flags);
5. int openat(int dirfd, const char *pathname, int flags, mode_t mode);

The openat() system call operates in exactly the same way as open

6. FILE *fopen(const char *path, const char *mode);

1. filename: 文件路径,默认从当前目录中查找
2. mode 打开模式:                                            
    1) r: 只读方式打开一个文本文件                           
        2) rb: 只读方式打开一个二进制文件                         
        3) w: 只写方式打开一个文本文件                           
        4) wb: 只写方式打开一个二进制文件                         
        5) a: 追加方式打开一个文本文件                           
        6) ab: 追加方式打开一个二进制文件                         
        7) r+: 可读可写方式打开一个文本文件                       
        8) rb+: 可读可写方式打开一个二进制文件                     
        9) w+: 可读可写方式创建一个文本文件                       
        10) wb+: 可读可写方式生成一个二进制文件                     
        11) a+: 可读可写追加方式打开一个文本文件                   
        12) ab+: 可读可写方式追加一个二进制文件

code

#include <stdio.h>

int main ()
{
    FILE * pFile;
    pFile = fopen ("myfile.txt","w");
    if (pFile!=NULL)
    {
        fputs ("fopen example",pFile);
        fclose (pFile);
    }
    return 0;
}

7. FILE *fdopen(int fd, const char *mode);

fdopen,将文件描述词转为文件指针(即实现从非缓存读写到缓存模式读写的转换)

1. fd
函数fdopen()会将参数fd的文件描述词,转换为对应的文件指针后返回,随后就可以使用缓存文件模式对文件进行读写
2. mode
mode代表着文件指针的流形态,此形态必须和原先文件描述词读写模式相同  
要注意的是: mode参数只有在我们在flags中传入了"O_CREAT"才有意义,即只有在新建新文件的时候才需要显示指定mode,用来表示文件的访问权限
    1) S_IRUSR: 用户可以读
    2) S_IWUSR: 用户可以写 
    3) S_IXUSR: 用户可以执行
    4) S_IRWXU: 用户可以读写执行 
    5) S_IRGRP: 组可以读    
    6) S_IWGRP: 组可以写 
    7) S_IXGRP: 组可以执行
    8) S_IRWXG: 组可以读写执行 
    9) S_IROTH: 其他人可以读
    10) S_IWOTH: 其他人可以写 
    11) S_IXOTH: 其他人可以执行
    12) S_IRWXO: 其他人可以读写执行 
    13) S_ISUID: 设置用户执行ID
    14) S_ISGID: 设置组的执行ID 

code

#include <stdio.h>

main()
{
    //这里0代表基本输出的文件描述符
    FILE * fp =fdopen(0, "w+");
    fprintf(fp, "%s\n", "hello!");
    fclose(fp);
}

8. FILE *freopen(const char *path, const char *mode, FILE *stream);

freopen: 打开文件 

1. path
包含欲打开的文件路径及文件名
2. mode
mode代表着文件指针的流形态,此形态必须和原先文件描述词读写模式相同  
要注意的是: mode参数只有在我们在flags中传入了"O_CREAT"才有意义,即只有在新建新文件的时候才需要显示指定mode,用来表示文件的访问权限
    1) S_IRUSR: 用户可以读
    2) S_IWUSR: 用户可以写 
    3) S_IXUSR: 用户可以执行
    4) S_IRWXU: 用户可以读写执行 
    5) S_IRGRP: 组可以读    
    6) S_IWGRP: 组可以写 
    7) S_IXGRP: 组可以执行
    8) S_IRWXG: 组可以读写执行 
    9) S_IROTH: 其他人可以读
    10) S_IWOTH: 其他人可以写 
    11) S_IXOTH: 其他人可以执行
    12) S_IRWXO: 其他人可以读写执行 
    13) S_ISUID: 设置用户执行ID
    14) S_ISGID: 设置组的执行ID 

3. stream
为已打开的文件指针。Freopen()会将原stream所打开的文件流(文件指针)关闭,然后打开参数path的文件 

code:

#include <stdio.h>

main()
{
    FILE * fp;
    fp = fopen("/etc/passwd", "r");
    fp = freopen("/etc/group", "r", fp);
    fclose(fp);
} 

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

lseek()移动文件的读写位置
每一个已打开的文件都有一个读写位置,当打开文件时通常其读写位置是指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或write()时,读写位置会随读写的进行而增加,lseek()便是用来控制该文件的读写位置

1. fd
已打开的文件描述符
2. offset
需要移动读写位置位移数(这是一个绝对数,而具体怎么解释这个数值,需要根据whence而定)
3. whence
whence为下列其中一种:
    1) SEEK_SET: 参数offset即为新的读写位置
        1.1) 欲将读写位置移到文件开头
        lseek(int fildes,0,SEEK_SET);
    2) SEEK_CUR: 以目前的读写位置往后增加offset个位移量(允许负值)
        2.1) 想要取得目前文件位置
        lseek(int fildes, 0, SEEK_CUR);
    3) SEEK_END: 将读写位置指向文件尾后再增加offset个位移量(允许负值)
        3.1) 欲将读写位置移到文件尾
        lseek(int fildes, 0, SEEK_END);

code

#include <stdio.h> 
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

char    buf1[] = "abcdefghij";
char    buf2[] = "ABCDEFGHIJ";

int main(void)
{
    int fd, size;
    if ((fd = creat("file.hole", S_IRUSR|S_IWUSR)) < 0)
    {
        printf("creat error\n");
        return -1;
    }
    size = sizeof buf1 - 1;
    if (write(fd, buf1, size) != size)
    {
        printf("buf1 write error\n");
        return -1;
    }
    /* offset now = 10 */
    if (lseek(fd, 16384, SEEK_SET) == -1)
    {
        printf("lseek error\n");
        return -1;
    }
    /* offset now = 16384 */
    size = sizeof buf2 - 1;
    if (write(fd, buf2, size) != size)
    {
        printf("buf2 write error\n");
        return -1;
    }
    /* offset now = 16394 */
    return 0;
}

10. int fseek(FILE *stream, long offset, int whence);
fseek()移动"文件流"(注意是文件流)的读写位置

1. *stream
已打开的文件指针
2. offset
和lseek中的offset意义一样,offset为根据参数whence来移动读写位置的位移数
3. whence
    1) SEEK_SET: 从距文件开头offset位移量为新的读写位置
        1.1) 欲将读写位置移动到文件开头时
        fseek(FILE*stream, 0, SEEK_SET);
    2) SEEK_CUR: 以目前的读写位置往后增加offset个位移量(允许负值)
    3) SEEK_END: 将读写位置指向文件尾后再增加offset个位移量(允许负值)
        3.1) 欲将读写位置移动到文件尾时
        fseek(FILE*stream, 0, SEEK_END);

code:

#include <stdio.h>

main()
{
    FILE * stream;
    long offset;
    fpos_t pos;
    stream = fopen("/etc/passwd", "r");
    fseek(stream, 5, SEEK_SET);
    //fseek()不像lseek()会返回读写位置,因此必须使用ftell()来取得目前读写的位置
    printf("offset=%d\n", ftell(stream));
    rewind(stream);
    fgetpos(stream, &pos);
    printf("offset=%d\n", pos);
    pos = 10;
    fsetpos(stream, &pos);
    printf("offset = %d\n", ftell(stream));
    fclose(stream);
} 

0x2: 文件的读取

1. ssize_t read(int fd, void *buf, size_t );

read()从已打开的"文件描述符"中读取数据,对于read的使用,我们需要记住以下几点

1. read()会把参数fd所指的文件传送count个字节到buf指针所指的内存中
2. 若参数count为0,则read()不会有作用并返回0
3. 返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,如果顺利read()会返回实际读到的字节数,最好能将返回值与参数count作比较,若返回的字节数比要求读取的字节数少,则有可能读到了文件尾、
从管道(pipe)或终端机读取,或者是read()被信号中断了读取动作
4. 文件读写位置会随读取到的字节移动(即读多少,前进多少) 5. 当读取过程中有错误发生时则返回-1,错误代码存入errno中,而文件读写位置则无法预期

参数说明

1. fd
已经打开的文件描述符
2. *buf
保存读取数据的一段内存空间
3. count
需要读取的字节数

code:

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

main()
{     
    int fd, size;
    char s[] = "Linux Programmer!\n", d[] = "Modified the file!\n", buffer[80];
    struct stat stat_modified, stat_notmodified;
 
    fd = open("/tmp/just4fun", O_RDWR | O_CREAT);
    write(fd, s, sizeof(s));
    close(fd); 

    printf("read the file! \n");
    fd = open("/tmp/just4fun", O_RDONLY);
    size = read(fd, buffer, sizeof(buffer));
    close(fd);
 
    return;
}

2. size_t fread(void * ptr,size_t size, size_t nmemb, FILE * stream);

fread()从"文件流"读取数据
fread()用来从"文件流"(注意和read()从文件描述符中读取数据作区分)中读取数据
Fread()会返回实际读取到的nmemb数目,如果此值比参数nmemb 来得小,则代表可能读到了文件尾或有错误发生,这时必须用feof()或ferror()来决定发生什么情况

1. ptr
指向欲存放读取进来的数据空间
2. size
每个字段为size字节
3. nmemb
读取加n个字段(读取的字符数以参数size * nmemb来决定)
4. stream
已打开的文件指针

code:

#include <stdio.h>
#define nmemb 3

struct test
{
    char name[20];
    int size;
}s[nmemb];

main()
{
    FILE * stream;
    int i;
    stream = fopen("/tmp/fwrite","r");
    fread(s, sizeof(struct test), nmemb, stream);
    fclose(stream);
    for(i=0;i<nmemb;i++)
    {
        printf("name[%d]=%-20s:size[%d]=%d\n", i, s[i].name, i, s[i].size);
    } 
} 

0x3: 文件的写入

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

write()将数据写入已打开的文件内
write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。同时,文件读写位置也会随之移动。如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中

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

main()
{
    int fd,size;
    char s[] = "Linux Programmer!\n", buffer[80];
    fd = open("/tmp/temp", O_WRONLY|O_CREAT);
    write(fd,s,sizeof(s));
    close(fd);  
    return;
}

0x4: 文件属性的操作

1. mode_t umask(mode_t mask);

umask()的作用在于设置建立新文件时的权限遮罩,关于文件权限的相关知识,请参阅另一篇文章
http://www.cnblogs.com/LittleHann/p/3862652.html
umask()会将系统umask值设成参数mask&0777后的值,然后将先前的umask值返回。简单来说,就是改变当前文件系统的"默认权限",这将直接影响在新建文件时,文件的默认文件权限

1. mask
    1) S_IRWXU: 00700: user (file owner) has read, write and execute permission
    2) S_IRUSR: 00400 user has read permission
    3) S_IWUSR: 00200 user has write permission
    4) S_IXUSR: 00100 user has execute permission
    5) S_IRWXG: 00070 group has read, write and execute permission
    6) S_IRGRP: 00040 group has read permission
    7) S_IWGRP: 00020 group has write permission
    8) S_IXGRP: 00010 group has execute permission
    9) S_IRWXO: 00007 others have read, write and execute permission
    10) S_IROTH: 00004 others have read permission
    11) S_IWOTH: 00002 others have write permission
    12) S_IXOTH: 00001 others have execute permission

code

#include <unistd.h>
#include <sys/stat.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

int main(void)
{
    mode_t mask = umask(0);
    if(creat("foo",RWRWRW)<0)
    {
        printf("create error for foo\n");
    } 
    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if(creat("bar",RWRWRW)<0)
    {
        printf("creat error for bar\n");
    } 
    exit(0);
}

2. int access(const char * pathname,int mode);

access()会检查是否可以读/写某一已存在的文件

1. pathname
带检查的文件路径
2. mode
参数mode可以取以下任意值或其组合
    1) R_OK: 测试读许可权
    2) W_OK: 测试写许可权
    3) X_OK: 测试执行许可权
    4) F_OK: 测试文件是否存在
3. 返回值 
若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1
4. 错误代码
    1) EACCESS: 参数pathname所指定的文件不符合所要求测试的权限
    2) EROFS: 欲测试写入权限的文件存在于只读文件系统内
    3) EFAULT: 参数pathname指针超出可存取内存空间
    4) EINVAL: 参数mode不正确
    5) ENAMETOOLONG参数pathname太长
    6) ENOTDIR: 参数pathname为一目录
    7) ENOMEM: 核心内存不足
    8) ELOOP: 参数pathname有过多符号连接问题
    9) EIO I/O: 存取错误。
/*
值得注意的是:
由于access()只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为"可写入",表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理
*/

code

#include <stdio.h>
#include <unistd.h>

int main(void)
{
    if(access("test.txt", R_OK)==0)  
    {
        printf("READ OK\n");
    } 
    if(access("test.txt", W_OK)==0)  
    {
        printf("WRITE OK\n");
    } 
    if(access("test.txt", X_OK)==0)  
    {
        printf("EXEC OK\n");
    }
    if(access("test.txt", F_OK)==0)   
    {
        printf("File exist\n");
    } 
}

3. int stat(const char * file_name, struct stat *buf);

函数说明stat()用来将参数file_name所指的"文件状态属性",复制到参数buf所指的结构中
struct stat的数据结构如下

struct stat
{
    dev_t st_dev;            /*文件的设备编号*/
    ino_t st_ino;            /*文件的i-node*/

    /*
    文件的类型和存取的权限
    st_mode 则定义了下列数种情况
        1) S_IFMT: 0170000: 文件类型的位遮罩
        2) S_IFSOCK: 0140000: scoket
        3) S_IFLNK: 0120000: 符号连接
        4) S_IFREG: 0100000: 一般文件
        5) S_IFBLK: 0060000: 区块装置
        6) S_IFDIR: 0040000: 目录
        7) S_IFCHR: 0020000: 字符装置
        8) S_IFIFO: 0010000: 先进先出
        9) S_ISUID: 04000: 文件的(set user-id on execution)位
        10) S_ISGID: 02000: 文件的(set group-id on execution)位
        11) S_ISVTX: 01000: 文件的sticky位
        若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名
        12) S_IRUSR(S_IREAD): 00400: 文件所有者具可读取权限
        13) S_IWUSR(S_IWRITE): 00200: 文件所有者具可写入权限
        14) S_IXUSR(S_IEXEC): 00100: 文件所有者具可执行权限
        15) S_IRGRP: 00040: 用户组具可读取权限
        16) S_IWGRP: 00020: 用户组具可写入权限
        17) S_IXGRP: 00010: 用户组具可执行权限
        18) S_IROTH: 00004: 其他用户具可读取权限
        19) S_IWOTH: 00002: 其他用户具可写入权限
        20) S_IXOTH: 00001: 其他用户具可执行权限
    上述的文件类型在POSIX中定义了检查这些类型的宏定义
        1) S_ISLNK(st_mode): 判断是否为符号连接
        2) S_ISREG(st_mode): 是否为一般文件
        3) S_ISDIR(st_mode): 是否为目录
        4) S_ISCHR(st_mode): 是否为字符装置文件
        5) S_ISBLK(s3e): 是否为先进先出
        6) S_ISSOCK(st_mode): 是否为socket
    */ 
    mode_t st_mode;            
    nlink_t st_nlink;        /*连到该文件的硬连接数目,刚建立的文件值为1*/
    uid_t st_uid;            /*文件所有者的用户识别码*/
    gid_t st_gid;            /*文件所有者的组识别码*/
    dev_t st_rdev;            /*若此文件为装置设备文件,则为其设备编号*/
    off_t st_size;            /*文件大小,以字节计算*/
    unsigned long st_blksize;    /*文件系统的I/O 缓冲区大小*/
    unsigned long st_blocks;    /*占用文件区块的个数,每一区块大小为512 个字节*/
    
    /*
    文件最近一次被存取或被执行的时间,一般只有在用
    1. mknod
    2. utime
    3. read
    4. write
    5. tructate
    时才会改变这个字段的值
    */
    time_t st_atime; 

    /* 
    文件最后一次被修改的时间,一般只有在用
    1. mknod
    2. utime
    3. write时
    才会改变这个字段的值
    */
    time_t st_mtime; 

    /* 
    time of last change 
    i-node最近一次被更改的时间
    */
    time_t st_ctime; 
};

返回值: 执行成功则返回0,失败返回-1,错误代码存于errno

错误代码

1. ENOENT: 参数file_name指定的文件不存在
2. ENOTDIR: 路径中的目录存在但却非真正的目录
3. ELOOP: 欲打开的文件有过多符号连接问题,上限为16符号连接
4. EFAULT: 参数buf为无效指针,指向无法存在的内存空间
5. EACCESS: 存取文件时被拒绝
6. ENOMEM: 核心内存不足
7. ENAMETOOLONG: 参数file_name的路径名称太长

code

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>

#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <string.h> 

void fileperm(struct stat *buf, char *perm)
{
    strcpy(perm, "----------");
    perm[0] = '?';
    mode_t mode;
        mode = buf->st_mode;
        switch (mode & S_IFMT)
        {
        case S_IFSOCK:
        perm[0] = 's';
                break;
        case S_IFLNK:
        perm[0] = 'l';
                break;
        case S_IFREG:
        perm[0] = '-';
                break;
        case S_IFBLK:
        perm[0] = 'b';
                break;
        case S_IFDIR:
        perm[0] = 'd';
                break;
        case S_IFCHR:
        perm[0] = 'c';
                break;
        case S_IFIFO:
        perm[0] = 'p';
                break;
    }

    if (mode & S_IRUSR)
        perm[1] = 'r';
    if (mode & S_IWUSR)
        perm[2] = 'w';
    if (mode & S_IXUSR)
        perm[3] = 'x';
    if (mode & S_IRGRP)
        perm[4] = 'r';
    if (mode & S_IWGRP)
        perm[5] = 'w';
    if (mode & S_IXGRP)
        perm[6] = 'x';
    if (mode & S_IROTH)
        perm[7] = 'r';
    if (mode & S_IWOTH)
        perm[8] = 'w';
    if (mode & S_IXOTH)
        perm[9] = 'x';
    perm[10] = '\0';
}


int main(int argc, char *argv[])
{
    struct stat sb;
    struct passwd *passwd;
    struct group *group;
    char perm[11] = {0};

    if (argc != 2) 
    {
      fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
      exit(EXIT_FAILURE);
    }

    if (stat(argv[1], &sb) == -1) 
    {
      perror("stat");
      exit(EXIT_SUCCESS);
    }
    passwd = getpwuid (sb.st_uid);
    group = getgrgid (sb.st_gid); 

    printf("File type:                ");

    switch (sb.st_mode & S_IFMT) 
    {
       case S_IFBLK:  printf("block device\n");            break;
       case S_IFCHR:  printf("character device\n");        break;
       case S_IFDIR:  printf("directory\n");               break;
       case S_IFIFO:  printf("FIFO/pipe\n");               break;
       case S_IFLNK:  printf("symlink\n");                 break;
       case S_IFREG:  printf("regular file\n");            break;
       case S_IFSOCK: printf("socket\n");                  break;
       default:       printf("unknown?\n");                break;
    }

       printf("I-node number:            %ld\n", (long) sb.st_ino);

       printf("Mode:                     %lo (octal)\n", (unsigned long) sb.st_mode);

       printf("Link count:               %ld\n", (long) sb.st_nlink);
       printf("Ownership:                UID=%ld   GID=%ld\n", (long) sb.st_uid, (long) sb.st_gid);
       printf("Ownership:                UID=%s   GID=%s\n", passwd->pw_name, group->gr_name);

       printf("File Permission Bits:     %o %s\n", sb.st_mode & 07777, perm);

       printf("Preferred I/O block size: %ld bytes\n", (long) sb.st_blksize);
       printf("File size:                %lld bytes\n", (long long) sb.st_size);
       printf("Blocks allocated:         %lld\n", (long long) sb.st_blocks);

       printf("Last status change:       %s", ctime(&sb.st_ctime));
       printf("Last file access:         %s", ctime(&sb.st_atime));
       printf("Last file modification:   %s", ctime(&sb.st_mtime));

       exit(EXIT_SUCCESS);
}

4. int fstat(int fd, struct stat *buf);

Fstat()与stat()作用完全相同,不同处在于传入的参数为已打开的"文件描述符" 
返回值 执行成功则返回0,失败返回-1,错误代码存于errno

code

#include <sys/stat.h>
#include <unistd.h>
#include <fcntk.h>

main()
{
    struct stat buf;
    int fd;
    fd = open("/etc/passwd", O_RDONLY);
    fstat(fd, &buf);
    printf("/etc/passwd file size +%d\n", buf.st_size);
}

5. lstat(const char *pathname, struct stat *buf);

lstat()与stat()作用完全相同,都是取得参数file_name所指的文件状态,其差别在于,当文件为"符号连接"时,lstat()会返回该link本身的状态(而不会去继续跟踪符号连接的终点)
返回值 执行成功则返回0,失败返回-1,错误代码存于errno

0x5: 文件的错误处理

1. void clearerr(FILE * stream);

clearerr()清除参数stream指定的文件流所使用的错误旗标

code

#include <stdio.h>

int main ()
{
    FILE * pFile;
    //这里以只读方式打开
    pFile = fopen("myfile.txt","r");
    if (pFile==NULL)
    {
        perror ("Error opening file");
    } 
    else 
    {    
        //往只读的文件描述符中写入数据,引发错误
        fputc ('x',pFile);
        if (ferror (pFile)) 
        {
            printf ("Error Writing to myfile.txt\n");
            //清除错误
            clearerr (pFile);
        }
        fgetc (pFile);
        if (!ferror (pFile))
        {
            printf ("No errors reading myfile.txt\n"); 
        }
        
        fclose (pFile);
    }
    return 0;
}

0x6: 文件的删除

1. int unlink(const char *pathname);

unlink()会删除参数pathname指定的文件

1. 如果该文件名为最后连接点,但有其他进程打开了此文件,则在所有关于此文件的文件描述词皆关闭后才会删除
2. 如果参数pathname为一符号连接,则此符号连接会被删除

返回值
1. 成功则返回0
2. 失败返回-1

错误原因存于errno
错误代码EROFS文件存在于只读文件系统内
1. EFAULT: 参数pathname指针超出可存取内存空间
2. ENAMETOOLONG: 参数pathname太长
3. ENOMEM: 核心内存不足
4. ELOOP: 参数pathname有过多符号连接问题
5. EIO: I/O存取错误

2. int unlinkat(int dirfd, const char *pathname, int flags);
0x7: 文件的关闭

1. int close(int fd);

close()关闭文件
当使用完文件后若已不再需要则可使用close()关闭该文件,close()会让数据写回磁盘,并释放该文件所占用的资源

1. fd
为先前由open()或creat()所返回的文件描述符
若文件顺利关闭则返回0,发生错误时返回-1 

code:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#define LENGTH 100

main()
{
     int fd, len;
     char str[LENGTH]; 
     fd = open("hello.txt", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); /* 创建并打开文件 */
     if (fd) 
     {
          write(fd, "Hello, Software Weekly", strlen("Hello, software weekly")); /* 写入 Hello, software weekly字符串 */
      close(fd);
     }

     fd = open("hello.txt", O_RDWR);
     len = read(fd, str, LENGTH); /* 读取文件内容 */
     str[len] = '\0';
     printf("%s\n", str);
     close(fd);
}

2. int fclose (FILE *stream);

fclose()用来关闭先前fopen()打开的文件。此动作会让缓冲区内的数据写入文件中,并释放系统所提供的文件资源。若关文件动作成功则返回0,有错误发生时则返回EOF并把错误代码

Relevant Link:

http://www.iteedu.com//os/linux/linuxprgm/linuxcfunctions/index.php
http://dev.yesky.com/468/7601968.shtml
http://fanqiang.chinaunix.net/a4/b2/20010508/113315.html
http://redwolf.blog.51cto.com/427621/154255

 

3. Linux下文件目录操作API

0x1: 新建目录

1. int mkdir(const char *path, mode_t mode);

1. path
要新建的目录路径
2. mode
新建目录的访问权限属性(linux下文件、目录的属性操作都是一样的)
http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysstat.h.html

code

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

main()
{
    /*
    mkdir会根据传入的路径参数造一个新目录出来 
    1. 如果该目录或档案已经存在,则操作失败
    2. mode 设置为0700,开始的0表示八进制 
    */
    if(mkdir("/home/zxc/z"0700) == -1)
    {     
        perror("Couldn't create the directory.\n");     
        return  
    }
}

0x2: 打开目录

1. int open( const char * pathname, int flags);

open除了可以用来打开文件之外,同样可以打开目录,只要在调用的时候传入O_DIRECTORY的flag即可

2. DIR * opendir(const char * name);

//DIR结构体类似于FILE,是一个内部结构,用于保存当前正在被读取的目录的有关信息
struct __dirstream   
{   
    void *__fd;    
    char *__data;    
    int __entry_data;    
    char *__ptr;    
    int __entry_ptr;    
    size_t __allocation;    
    size_t __size;    
    __libc_lock_define (, __lock)    
};    
typedef struct __dirstream DIR;  

opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流,和open()类似,接下来对目录的读取和搜索都要使用此返回值

返回值 
1. 成功则返回DIR*型态的目录流
2. 打开失败则返回NULL

错误代码
1. EACCESS: 权限不足
2. EMFILE: 已达到进程可同时打开的文件数上限
3. ENFILE: 已达到系统可同时打开的文件数上限
4. ENOTDIR: 参数name非真正的目录
5. ENOENT: 参数name指定的目录不存在,或是参数name为一空字符串
6. ENOMEM: 核心内存不足

3. struct dirent readdir(DIR *);

//dirent不仅仅指向目录,还指向目录中的具体文件
struct dirent   
{
    long d_ino; /* inode number 索引节点号 */  
     
    off_t d_off; /* offset to this dirent 在目录文件中的偏移 */  
     
    unsigned short d_reclen; /* length of this d_name 文件名长 */  
     
    unsigned char d_type; /* the type of d_name 文件类型 */  
     
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 当前目录下的所有文件名(包括目录本身),最长255字符 */  
} 

打开指定目录的子路径,可以反复调用本函数来得到制定目录的所有子路径信息。当执行到最后一个目录或者文件的时候,将返回NULL

#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>

int main(void)
{
    struct stat fStat;
    DIR *dir;    
    struct dirent *fileInfo = NULL;    

    if (-1 == lstat("test.txt", &fStat))
    {
        perror("");
        return -1;
    }
    
    if (S_ISDIR(fStat.st_mode))
    {
        printf("INFO: The path is a directory!\n");
    }
    else
    {
        printf("INFO: The path is a file!\n");
        return 1;
    }

    //If it is a dir, print the files' names in this directory. 
    dir = opendir("testdir");
    if (NULL == dir)
    {
        perror("");
        return -1;
    }

    fileInfo = readdir(dir);
    while (NULL != fileInfo)
    {
        printf("INFO: File name is %s!\n", fileInfo->d_name);
        fileInfo = readdir(dir);    
    }

    return 1;
}

4. void rewinddir(DIR *dr);

重置读取目录的位置为开头,在readdir循环遍历当前目录所有文件时配合使用

5. int chdir(const char *path);

chdir接受目录路径名,成功时当前工作目录改变,并均返回0。失败,返回-1.并设置errno
6. int fchdir(int fd);

fchdir接收指向已打开目录的文件描述符,成功时当前工作目录改变,并均返回0。失败,返回-1.并设置errno

0x3: 读取目录属性

1. S_ISDIR(stat.st_mode)

判断是否是目录,传入参数是(struct stat)

2. int stat(const char * file_name, struct stat *buf);

stat除了可以用来获取文件的属性之外,同样也可以获取目录的属性,事实上,linux下目录和文件并没有太大的区别,所有的文件、设备都被抽象为了"文件"来看待

0x4: 删除目录

1. int remove(const char *pathname) ;

remove()会删除参数pathname指定的文件

1. 如果参数pathname为一文件,则调用unlink()处理
2. 若参数pathname为一目录,则调用rmdir()来处理(remove只是一个中转函数)

返回值
1. 成功则返回0
2. 失败则返回-1

错误原因存于errno
1. EROFS: 欲写入的文件存在于只读文件系统内
2. EFAULT: 参数pathname指针超出可存取内存空间
3. ENAMETOOLONG: 参数pathname太长
4. ENOMEM: 核心内存不足
5. ELOOP: 参数pathname有过多符号连接问题
6. EIO: I/O存取错误

2. int rmdir(const char *path);

标准的POSIX调用rmdir()将目录从文件系统层次上移除
1. 调用成功,rmdir从文件系统移除path,并返回0.path指向的目录必须唯一
2. 调用失败时,rmdir()返回-1,并设置errno

0x5: 获取当前目录

1. char *getcwd(char *buffer,size_t size);

1. 执行成功
成功调用getcwd()会以"绝对路径名"形式复制当前"工作目录"至由buf指向的长度size字节的缓冲区。并返回一个指向buf的指针
2. 执行失败
失败时,调用返回NULL,并设置errno
    1) EFAULT
    2) EINVAL
    3) ENOENT
    4) ERANGE

code

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

void main()
{
        char cwd[255];
        getcwd(cwd, sizeof(cwd));
        if(!cwd) 
    {
                perror("getcwd");
                exit(EXIT_FAILURE);
        }
        printf("cwd = %s\n",cwd); 
}

2. char * getwd(char *buf);

调用getwd会复制当前"工作目录"至长度至少PATH_MAX字节的buf。成功调用返回buf指针,失败返回NULL
getwd()这个函数是为了向后兼容,因此不建议使用,推荐getcwd()

3. char * get_current_dir_name(void);

这个函数和 getcwd()的作用一样,都是获得当前"工作目录"的"绝对路径"

#include <stdio.h>
#include <stdlib.h>
#define _GNU_SOURCE
#include <unistd.h>

int main ()
{
    char *path; 

    path = get_current_dir_name(); 

    printf ("当前路径为:%s\n", path); 

    free (path);

    return (0);
}

4. int readlink(const char * path, char * buf, size_t bufsiz);

readlink()会将参数path的符号连接内容(真正指向的目标文件的路径)存到参数buf 所指的内存空间, 返回的内容不是以NULL作字符串结尾, 但会将字符串的字符数返回. 若参数bufsiz小于符号连接的内容长度, 过长的内容会被截断.

返回值
1. 执行成功则传符号连接所指的文件路径字符串
2. 失败则返回-1, 错误代码存于errno.

错误代码:
1. EACCESS: 取文件时被拒绝,权限不够
2. EINVAL: 参数bufsiz为负数
3. EIO: I/O存取错误
4. ELOOP: 欲打开的文件有过多符号连接问题
5. ENAMETOOLONG: 参数path的路径名称太长
6. ENOENT: 参数path所指定的文件不存在
7. ENOMEM: 核心内存不足
8. ENOTDIR: 参数path路径中的目录存在但却非真正的目录

code

#include <stdio.h> 
#include <unistd.h> 

int main() 
{ 
        char path[100], buf[100]; 
        sprintf(path, "/proc/%d/exe", getpid()); 
        readlink(path, buf, sizeof(buf)); 
        printf("%s/n", buf); 
        return 0; 
}

Relevant Link:

http://wenku.baidu.com/view/788915a8d1f34693daef3e6c.html
http://www.averainy.info/linux-system-programming-directory-operation-function-summary/
http://www.360doc.com/relevant/162835139_more.shtml
http://www.jb51.net/article/40594.htm
http://icarusliu.iteye.com/blog/1528703

 

4. Linux下的其他设备操作API

0x1: int ioctl(int fd, unsigned long request, ../* void *arg */.); (硬件信息相关)

1. fd
ioctl函数的第一个参数fd,可以表示一个打开的文件(文件句柄)或网络套接字

2. request
第二个和第三个参数体现了函数的家族特色,参数二request根据函数功能分类定义了多组宏,这里以和网卡相关信息的宏为例子
    1) SIOCGIFCONF    struct ifconf    获得所有接口列表
     2) SIOCGIFADDR    struct ifreq    获得接口地址
     3) SIOCGIFFLAGS    struct ifreq    获得接口标志
     4) SIOCGIFBRDADDR    struct ifreq    获得广播地址
     5) SIOCGIFNETMASK    struct ifreq    获得子网掩码
更多关于这个参数列表请参阅
http://manpages.courier-mta.org/htmlman2/ioctl_list.2.html

3. arg 
参数三总是一个指针,指针的类型依赖于参数二request

code

#include <arpa/inet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
 
#define MAXINTERFACES 16    /* 最大接口数 */
 
int fd;         /* 套接字 */
int if_len;     /* 接口数量 */
struct ifreq buf[MAXINTERFACES];    /* ifreq结构数组 */
struct ifconf ifc;                  /* ifconf结构 */
 
int main(argc, argv)
{
    /* 建立IPv4的UDP套接字fd */
    if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
    {
        perror("socket(AF_INET, SOCK_DGRAM, 0)");
        return -1;
    }
 
    /* 初始化ifconf结构 */
    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = (caddr_t) buf;
 
    /* 获得接口列表 */
    if (ioctl(fd, SIOCGIFCONF, (char *) &ifc) == -1)
    {
        perror("SIOCGIFCONF ioctl");
        return -1;
    }
 
    if_len = ifc.ifc_len / sizeof(struct ifreq); /* 接口数量 */
    printf("接口数量:%d/n/n", if_len);
 
    while (if_len– > 0) /* 遍历每个接口 */
    {
        printf("接口:%s/n", buf[if_len].ifr_name); /* 接口名称 */
 
        /* 获得接口标志 */
        if (!(ioctl(fd, SIOCGIFFLAGS, (char *) &buf[if_len])))
        {
            /* 接口状态 */
            if (buf[if_len].ifr_flags & IFF_UP)
            {
                printf("接口状态: UP/n");
            }
            else
            {
                printf("接口状态: DOWN/n");
            }
        }
        else
        {
            char str[256];
            sprintf(str, "SIOCGIFFLAGS ioctl %s", buf[if_len].ifr_name);
            perror(str);
        }
 
 
        /* IP地址 */
        if (!(ioctl(fd, SIOCGIFADDR, (char *) &buf[if_len])))
        {
            printf("IP地址:%s/n",
                    (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
        }
        else
        {
            char str[256];
            sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
            perror(str);
        }
 
        /* 子网掩码 */
        if (!(ioctl(fd, SIOCGIFNETMASK, (char *) &buf[if_len])))
        {
            printf("子网掩码:%s/n",
                    (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
        }
        else
        {
            char str[256];
            sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
            perror(str);
        }
 
        /* 广播地址 */
        if (!(ioctl(fd, SIOCGIFBRDADDR, (char *) &buf[if_len])))
        {
            printf("广播地址:%s/n",
                    (char*)inet_ntoa(((struct sockaddr_in*) (&buf[if_len].ifr_addr))->sin_addr));
        }
        else
        {
            char str[256];
            sprintf(str, "SIOCGIFADDR ioctl %s", buf[if_len].ifr_name);
            perror(str);
        }
 
        /*MAC地址 */
        if (!(ioctl(fd, SIOCGIFHWADDR, (char *) &buf[if_len])))
        {
            printf("MAC地址:%02x:%02x:%02x:%02x:%02x:%02x/n/n",
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[0],
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[1],
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[2],
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[3],
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[4],
                    (unsigned char) buf[if_len].ifr_hwaddr.sa_data[5]);
        }
        else
        {
            char str[256];
            sprintf(str, "SIOCGIFHWADDR ioctl %s", buf[if_len].ifr_name);
            perror(str);
        }
    }//–while end
 
    //关闭socket
    close(fd);
    return 0;
}

Relevant Link:

http://manpages.courier-mta.org/htmlman2/ioctl_list.2.html
http://www.360doc.com/content/12/0314/15/5782959_194281431.shtml

 

Copyright (c) 2014 LittleHann All rights reserved

 

转载于:https://www.cnblogs.com/LittleHann/p/3905608.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值