目录
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