Linux 系统中的各类输入输出,设计为“一切皆文件”。各类各样的IO统一用文件形式访问。node
Linux 系统的大部分系统资源都以文件形式提供给用户读写。这些文件能够分为:linux
文件的通用操做为:打开、关闭、读、写、建立。对应 Linux 系统的 API 接口函数分别为 open()
、close()
、read()
、write()
、create()
。这些函数经过文件描述符 File Descriptor 实现 IO 操做。web
在进程中,经过 open 函数打开文件或经过 create 函数建立文件后,会返回一个整数,这个整数就是表明这个文件的文件描述符。经过 ulimit -n
命令能够查看每一个进程最大支持同时打开多少文件。缓存
操做系统在进程控制块(PCB,Process Control Block)中,帮每一个进程维护了一个文件描述符表,进程打开的全部文件,都会在这个表里登记。打开文件获得的整型返回值,实际上就是指向表里某条记录的索引。后续执行读写操做时,经过传入的文件描述符,在文件描述符表进行查找,从而定位到文件的具体位置。bash
Linux 系统在启动时,标准 IO 会占用掉前 3 个文件描述符的位置:网络
部分函数须要同时引入多个头文件,是由于这些函数中用到的常量定义,跟函数定义不在同一个头文件里。socket
#include <sys/types.h> /* 定义数据类型,如 ssize_t,off_t 等 */ #include <fcntl.h> /* 定义 open,creat 等函数原型,建立文件权限的符号常量 S_IRUSR 等 */ #include <unistd.h> /* 定义 read,write,close,lseek 等函数原型 */ #include <errno.h> /* 与全局变量 errno 相关的定义 */ #include <sys/ioctl.h> /* 定义 ioctl 函数原型 */
函数头文件:async
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>
函数原型:svg
int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); // 若是 flags 包含 O_CREAT,则相对于 creat 函数,必须指定 mode 参数
int creat(const char *pathname, mode_t mode);
参数:函数
返回值:报错时返回 -1,不然返回文件描述符。
示例:
#include <fcntl.h> #include <stdio.h> int main() { int fd; char name[] = "666.txt"; // 若是文件不存在,就建立文件,权限为全部者RWX,组和其余人无权限 fd = open(name, O_RDONLY | O_CREAT, S_IRWXU); // fd 小于 0 表示出错,须要处理 if (fd < 0) //... printf("%d\n", fd); close(fd); // 若是文件已经存在,会把文件内容清空。权限为用户RW,组用户W,其余人R,即 0x321 fd = cerat(name, S_IRUSR | S_IWUSR | S_IWGRP | S_IROTH); return 0; }
Linux 系统中,文件能够屡次打开,例如多个进程同时打开一个文件,一个进程反复屡次打开。内核记录了文件的打开次数,只要还有进程没关闭文件,就不会关闭文件。
close(fd);
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
read 函数会尝试从文件描述符 fd 中读取 count 个字节到缓冲区 buf 中。
返回值:
成功时返回读到的字节数,0表示读到文件末尾了。若是返回字节数小于指定的字节数,不必定出错,有可能文件就剩这么多数据了。出错时返回 -1,并设置 errno 为合适值。
示例:
#include <unistd.h> #include <stdio.h> #include <fcntl.h> int main() { int fd, res; char buf[20]; char name[] = "666.txt"; fd = open(name, O_RDONLY); res = read(fd, buf, sizeof(buf)); printf("%s\n", buf); return 0; }
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
write 函数会尝试从缓冲区 buf 中读取 count 个字节,写到文件描述符 fd 中。
返回值:
成功时返回写入的字节数。若是返回字节数小于指定的字节数,不必定出错,有多是磁盘满了。出错时返回 -1,并设置 errno 为合适值。
示例:
#include <unistd.h> #include <stdio.h> #include <fcntl.h> int main() { int fd, res; char str[] = "hello world!"; char name[] = "666.txt"; fd = open(name, O_WRONLY); // 必需要有写权限 res = write(fd, str, sizeof(str)); printf("%d\n", res); return 0; }
磁盘读写速度很慢,为了优化性能,Linux 在写磁盘时,加了一层缓存,数据攒够必定数量或程序结束后才将数据写入磁盘。write 函数每次只是将数据写到缓存,若是须要强制其写入磁盘,须要使用 fsync 命令。
头文件:
#include <unistd.h>
函数原型:
int fsync(int fd); int fdatasync(int fd);
返回值:
操做成功返回 0,不然返回 -1,同时设置全局变量 errno。
sync() 函数同步整个系统修改过的缓存数据,而 fsync() 则只针对一个具体文件。
有的设备支持随机读写文件,例如磁盘,而有的则只支持顺序读写,例如管道、套接字和 FIFO。支持随机读写的设备,能够经过 lseek 函数移动读写位置。以后的读写操做,将会从这个新位置开始。
头文件:
#include <unistd.h>
函数原型:
off_t lseek(int fd, off_t offset, int whence);
参数:
返回值:
操做成功则返回新的读写位置,不然返回 -1。按顺序读写的文件不支持 lseek 操做,对这类文件调用 lseek(),将返回-1,且 errno=ESPIPE。
若是只是想测试设备是否支持该操做,能够执行这个语句,只有返回值大于 -1,就是支持的:
res = lseek(fd, 0, SEEK_CUR);
示例:
#include <unistd.h> #include <fcntl.h> #include <stdio.h> int main() { int fd, res; char name[] = "666.txt"; char buf[20]; fd = open(name, O_RDONLY); lseek(fd, 5, SEEK_SET); res = read(fd, buf, 10); printf("%s\n", buf); return 0; }
ioctl 是文件 IO 的杂项函数,能够实现一些设备相关的操做,例如修改寄存器的值。
头文件:
#include <sys/ioctl.h>
函数原型:
int ioctl(int d, int request, ...);
参数:
...
表示从参数是可选的、类型不肯定的。不一样的文件,cmd 通常不一样,好比嵌入式系统中的设备文件,蜂鸣器(BUZZER)和模数转换(ADC)。返回值:
操做成功则返回0,不然返回 -1。部分设备可能返回正数表示参数。
跟 Linux 终端中的 stat 命令做用同样,stat 函数也用来查看文件属性。
# stat tmux-client-14353.log File: ‘tmux-client-14353.log’ Size: 54 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 256424 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2018-11-17 14:56:09.963358724 +0800 Modify: 2018-11-17 14:56:16.992381417 +0800 Change: 2018-11-17 14:56:16.992381417 +0800 Birth: -
头文件及函数原型:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *path, struct stat *buf); /* 查看 path 文件名指向的文件的属性,放到 buf 中*/ int fstat(int fd, struct stat *buf); /* 文件名变成文件描述符 */ int lstat(const char *path, struct stat *buf); /* 文件名是一个符号连接,查看这个符号连接的属性 */
返回值:
struct stat { dev_t st_dev; /* 文件的设备编号,ID of device containing file */ ino_t st_ino; /* Inode 编号,inode number */ mode_t st_mode; /* 文件类型和权限,protection */ nlink_t st_nlink; /* 硬连接个数,number of hard links */ uid_t st_uid; /* 用户ID,user ID of owner */ gid_t st_gid; /* 组ID,group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* 文件大小,total size, in bytes */ blksize_t st_blksize; /* 块大小,blocksize for file system I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* 最后一次修改时间,time of last modification */ time_t st_ctime; /* time of last status change */ };
示例:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> int main() { char name[] = "666.txt"; struct stat buf; int ret = stat(name, &buf); if (ret < 0) { printf("get file status error, errorno is:%d ", errno); } printf("UID is: %d\nGID is: %d\nsize is: %d\n", (int)buf.st_uid, (int)buf.st_gid, (int)buf.st_size); return 0; }
检查当前用户对文件是否具备某个权限,还能够判断文件是否存在。
头文件及函数原型:
#include <unistd.h> int access(const char *pathname, int mode);
参数:
示例:
#include <stdio.h> #include <unistd.h> int main() { char name[] = "666.txt"; int ret = access(name, R_OK); if (ret == -1) { printf("you can not read \"%s\"\n", name); } printf("you can read \"%s\"\n", name); }
修改文件权限,修改全部者和所属用户。
#include <sys/stat.h> int chmod(const char *path, mode_t mode); int fchmod(int fd, mode_t mode);
#include <unistd.h> int chown(const char *path, uid_t owner, gid_t group); int fchown(int fd, uid_t owner, gid_t group); int lchown(const char *path, uid_t owner, gid_t group);
改变文件的名字或路径。
#include <stdio.h> int rename(const char *oldpath, const char *newpath);
获取当前的工做目录。能够经过返回值或入参 buf 返回当前的绝对路径。
#include <unistd.h> char *getcwd(char *buf, size_t size); char *getwd(char *buf); /* 已经废弃 */ char *get_current_dir_name(void);
更改当前目录,建立新目录。
#include <unistd.h> int chdir(const char *path); int fchdir(int fd);
示例:
#include <unistd.h> int main() { char name[] = "new_dir"; char buf[100]; mkdir(name); chdir(name); char *pwd = getcwd(buf, 100); printf("%s\n", pwd); printf("%s\n", buf); return 0; }
打开目录,读目录。man 2 opendir
没找到描述,最好别用,能够用封装好的 C 库函数。
int readdir(unsigned int fd, struct old_linux_dirent *dirp, unsigned int count);
复制文件描述符。
#include <unistd.h> int dup(int oldfd); int dup2(int oldfd, int newfd);
修改文件描述符。
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
#include <stdio.h> #include <unistd.h> #include <sys/ioctl.h> #include <fcntl.h> #include <errno.h> int main(int argc, char* argv[]) { int fd, res; char str[] = "hello world!"; char buf[20] = {0}; char name[] = "666.txt"; fd = open(name, O_WRONLY); if (fd < 0) { printf("open file %s failed, errorno = %d\n", name, errno); return -1; } res = write(fd, str, sizeof(str)); printf("write %d bytes to \"%s\"\n", res, name); fsync(fd); close(fd); fd = open(name, O_RDONLY); if (fd < 0) return -1; res = read(fd, buf, sizeof(buf)); printf("read output is:\n%s\n", buf); printf("read %d bytes from \"%s\"", res, name); return 0; }