File文件IO
文件描述符
在 Linux 的世界里,一切设备皆文件。我们可以系统调用中 I/O 的函数(I:input,输入;O:output,输出),对文件进行相应的操作( open()、close()、write() 、read() 等)。
打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。
程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件被打开,对应的文件描述符 0、1、2 记录在表中。程序运行起来后这三个文件描述符是默认打开的。
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
在程序运行起来后打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。
最大打开的文件个数
Linux 中一个进程最多只能打开 NR_OPEN_DEFAULT (即1024)个文件,故当文件不再使用时应及时调用 close() 函数关闭文件。
cat /proc/sys/fs/file-max
:查看当前系统允许打开最大文件个数ulimit -a
: 当前默认设置最大打开文件个数1024ulimit -n 4096
:修改默认设置最大打开文件个数为4096
错误处理函数
errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义。查看错误代码errno是调试程序的一个重要方法。
当Linux C api函数发生异常时,一般会将errno全局变量赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
FILE* fp = fopen("xixi.txt", "r");
if (NULL == fp) {
printf("%d\n", errno); // 打印错误码
printf("%s\n", strerror(errno)); // 把errno的数字转换成相应的文字
perror("fopen err:"); // 打印错误原因的字符串
}
return 0;
}
errno.h
/*
* Error codes
*/
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* Input/output error */
#define ENXIO 6 /* Device not configured */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file descriptor */
#define ECHILD 10 /* No child processes */
#define EDEADLK 11 /* Resource deadlock avoided */
/* 11 was EAGAIN */
#define ENOMEM 12 /* Cannot allocate memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define ENOTBLK 15 /* Block device required */
#endif
#define EBUSY 16 /* Device / Resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* Operation not supported by device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* Too many open files in system */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Inappropriate ioctl for device */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
/* math software */
#define EDOM 33 /* Numerical argument out of domain */
#define ERANGE 34 /* Result too large */
/* non-blocking and interrupt i/o */
#define EAGAIN 35 /* Resource temporarily unavailable */
#define EWOULDBLOCK EAGAIN /* Operation would block */
#define EINPROGRESS 36 /* Operation now in progress */
#define EALREADY 37 /* Operation already in progress */
/* ipc/network software -- argument errors */
#define ENOTSOCK 38 /* Socket operation on non-socket */
#define EDESTADDRREQ 39 /* Destination address required */
#define EMSGSIZE 40 /* Message too long */
#define EPROTOTYPE 41 /* Protocol wrong type for socket */
#define ENOPROTOOPT 42 /* Protocol not available */
#define EPROTONOSUPPORT 43 /* Protocol not supported */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define ESOCKTNOSUPPORT 44 /* Socket type not supported */
#endif
#define ENOTSUP 45 /* Operation not supported */
#if !__DARWIN_UNIX03 && !defined(KERNEL)
#define EOPNOTSUPP ENOTSUP /* Operation not supported on socket */
#endif /* !__DARWIN_UNIX03 && !KERNEL */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EPFNOSUPPORT 46 /* Protocol family not supported */
#endif
#define EAFNOSUPPORT 47 /* Address family not supported by protocol family */
#define EADDRINUSE 48 /* Address already in use */
#define EADDRNOTAVAIL 49 /* Can't assign requested address */
/* ipc/network software -- operational errors */
#define ENETDOWN 50 /* Network is down */
#define ENETUNREACH 51 /* Network is unreachable */
#define ENETRESET 52 /* Network dropped connection on reset */
#define ECONNABORTED 53 /* Software caused connection abort */
#define ECONNRESET 54 /* Connection reset by peer */
#define ENOBUFS 55 /* No buffer space available */
#define EISCONN 56 /* Socket is already connected */
#define ENOTCONN 57 /* Socket is not connected */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define ESHUTDOWN 58 /* Can't send after socket shutdown */
#define ETOOMANYREFS 59 /* Too many references: can't splice */
#endif
#define ETIMEDOUT 60 /* Operation timed out */
#define ECONNREFUSED 61 /* Connection refused */
#define ELOOP 62 /* Too many levels of symbolic links */
#define ENAMETOOLONG 63 /* File name too long */
/* should be rearranged */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EHOSTDOWN 64 /* Host is down */
#endif
#define EHOSTUNREACH 65 /* No route to host */
#define ENOTEMPTY 66 /* Directory not empty */
/* quotas & mush */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EPROCLIM 67 /* Too many processes */
#define EUSERS 68 /* Too many users */
#endif
#define EDQUOT 69 /* Disc quota exceeded */
/* Network File System */
#define ESTALE 70 /* Stale NFS file handle */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EREMOTE 71 /* Too many levels of remote in path */
#define EBADRPC 72 /* RPC struct is bad */
#define ERPCMISMATCH 73 /* RPC version wrong */
#define EPROGUNAVAIL 74 /* RPC prog. not avail */
#define EPROGMISMATCH 75 /* Program version wrong */
#define EPROCUNAVAIL 76 /* Bad procedure for program */
#endif
#define ENOLCK 77 /* No locks available */
#define ENOSYS 78 /* Function not implemented */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EFTYPE 79 /* Inappropriate file type or format */
#define EAUTH 80 /* Authentication error */
#define ENEEDAUTH 81 /* Need authenticator */
/* Intelligent device errors */
#define EPWROFF 82 /* Device power is off */
#define EDEVERR 83 /* Device error, e.g. paper out */
#endif
#define EOVERFLOW 84 /* Value too large to be stored in data type */
/* Program loading errors */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EBADEXEC 85 /* Bad executable */
#define EBADARCH 86 /* Bad CPU type in executable */
#define ESHLIBVERS 87 /* Shared library version mismatch */
#define EBADMACHO 88 /* Malformed Macho file */
#endif
#define ECANCELED 89 /* Operation canceled */
#define EIDRM 90 /* Identifier removed */
#define ENOMSG 91 /* No message of desired type */
#define EILSEQ 92 /* Illegal byte sequence */
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define ENOATTR 93 /* Attribute not found */
#endif
#define EBADMSG 94 /* Bad message */
#define EMULTIHOP 95 /* Reserved */
#define ENODATA 96 /* No message available on STREAM */
#define ENOLINK 97 /* Reserved */
#define ENOSR 98 /* No STREAM resources */
#define ENOSTR 99 /* Not a STREAM */
#define EPROTO 100 /* Protocol error */
#define ETIME 101 /* STREAM ioctl timeout */
#if __DARWIN_UNIX03 || defined(KERNEL)
/* This value is only discrete when compiling __DARWIN_UNIX03, or KERNEL */
#define EOPNOTSUPP 102 /* Operation not supported on socket */
#endif /* __DARWIN_UNIX03 || KERNEL */
#define ENOPOLICY 103 /* No such policy registered */
#if __DARWIN_C_LEVEL >= 200809L
#define ENOTRECOVERABLE 104 /* State not recoverable */
#define EOWNERDEAD 105 /* Previous owner died */
#endif
#if __DARWIN_C_LEVEL >= __DARWIN_C_FULL
#define EQFULL 106 /* Interface output queue is full */
#define ELAST 106 /* Must be equal largest errno */
#endif
常用IO函数
int open(const char \*pathname, int flags);
int open(const char \*pathname, int flags, mode_t mode);
ssize_t read(int fd, void \*buf, size_t count);
ssize_t write(int fd, const void \*buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);
int close(int fd);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/**
* @brief
*
* @param fd 文件描述符
* @param offset 偏移
*
* @param whence 起始(从哪里开始)SEEK_SET SEEK_CUR SEEK_END
* @return off_t
*/
// off_t lseek(int fd, off_t offset, int whence);
int main(int argc, char* argv[]) {
// int open(const char *pathname, int flags);
int fd = open("xixi.txt", O_RDWR);
char buf[1024] = {0};
// ssize_t read(int fd, void *buf, size_t count);
int len = read(fd, buf, 1024);
if (len < 0) {
perror("Failed to read:");
return len;
}
// ssize_t write(int fd, const void *buf, size_t count);
int ret = write(fd, "xixi", 5);
if (ret < 0) {
perror("Failed to write:");
return ret;
}
// hehe xixi
printf("buf = %s\n", buf);
// int close(int fd);
close(fd);
return 0;
}
lseek_demo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
/**
* @brief
*
* @param fd 文件描述符
* @param offset 偏移
*
* @param whence 起始(从哪里开始)SEEK_SET SEEK_CUR SEEK_END
* @return off_t
*/
// off_t lseek(int fd, off_t offset, int whence);
int main(int argc, char* argv[]) {
// int open(const char *pathname, int flags);
int fd = open("xixi.txt", O_RDWR); // hehexixi
char buf[1024] = {0};
// ssize_t read(int fd, void *buf, size_t count);
int len = read(fd, buf, 2);
if (len < 0) {
perror("Failed to read:");
return len;
}
printf("first read: %s\n", buf); // hehe
// off_t lseek(int fd, off_t offset, int whence);
int ret = lseek(fd, 0, SEEK_SET);
if (ret == -1) {
perror("Failed to lseek:");
close(fd);
return ret;
}
printf("current position: %d\n", ret);
len = read(fd, buf, 1024);
// hehexixi
printf("second read = %s\n", buf); // hehexixi
// int close(int fd);
close(fd);
return 0;
}
文件描述符的复制
dup() 和 dup2() 是两个非常有用的系统调用,都是用来复制一个文件的描述符,使新的文件描述符也标识旧的文件描述符所标识的文件。
这个过程类似于现实生活中的配钥匙,钥匙相当于文件描述符,锁相当于文件,本来一个钥匙开一把锁,相当于,一个文件描述符对应一个文件,现在,我们去配钥匙,通过旧的钥匙复制了一把新的钥匙,这样的话,旧的钥匙和新的钥匙都能开启这把锁。
对比于 dup(), dup2() 也一样,通过原来的文件描述符复制出一个新的文件描述符,这样的话,原来的文件描述符和新的文件描述符都指向同一个文件,我们操作这两个文件描述符的任何一个,都能操作它所对应的文件。
函数:
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup和dup2区别:dup是内核分配一个最小可用的文件描述符;dup2是自己指定newfd,如果newfd被占用了,会先close这个newfd,然后在复用这个newfd
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
int dup(int oldfd);
int newfd = dup(STDOUT_FILENO);
write(newfd, "xixi", 4);
// int dup2(int oldfd, int newfd);
int fd = open("xixi.txt", O_RDWR);
int ret = dup2(fd, STDOUT_FILENO);
int len = write(fd, "from fd\n", 8);
len = write(STDOUT_FILENO, "from stdout", 11);
if (len == -1) {
perror("Failed to read:");
return len;
}
close(fd);
close(STDOUT_FILENO);
return 0;
}
操作文件的特性
操作文件描述符:
int fcntl(int fd, int cmd, ... /_ arg _/);
操作命令cmd:
- 复制一个现有的描述符(cmd=F_DUPFD)==> 相当于dup
- 获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD)
- 获得/设置文件状态标记(cmd=F_GETFL或F_SETFL)
- 获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN)
- 获得/设置记录锁(cmd=F_GETLK, F_SETLK或F_SETLKW)
fcntl_demo.c
把stdin改成nonblocking
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// 修改stdin成非阻塞
// read stdin --> 阻塞(等待键盘输入) (网络...)
// read 文件 --> 非阻塞()
int main(int argc, char* argv[]) {
char buf[1024] = {0};
// 1.block(default)
int len = read(STDIN_FILENO, buf, 1024);
if (len < 0) {
perror("Failed to read\n");
return len;
}
printf("buf = %s\n", buf);
return 0;
// 2. fcntl
// int fcntl(int fd, int cmd, ... /* arg */ );
int flag = fcntl(STDIN_FILENO, F_GETFL);
flag |= O_NONBLOCK; // 追加flag:O_NONBLOCK
int ret = fcntl(STDIN_FILENO, F_SETFL, flag);
if (ret < 0) {
perror("Failed to set fd flag");
return ret;
}
while (true) {
int len = read(STDIN_FILENO, buf, 1024); // non-blocking
if (len < 0) {
if (errno == EAGAIN) { // 或EWOULDBLOCK
sleep(2);
printf("try again\n");
continue;
}
perror("Failed to read\n");
return len;
} else if (len > 0) {
break;
}
}
printf("buf = %s\n", buf);
return 0;
}