标准C库IO函数与Linux系统IO函数
- 标准C库IO函数相较于Linux系统IO函数,具有跨平台的优势,它可以针对不同的系统调用相应的API来实现同样的操作。(JAVA通过虚拟机实现跨平台)
- 标准C库IO函数与Linux系统IO函数是调用与被调用的关系
- 标准C库IO函数比Linux系统IO函数效率高(缓冲区的功劳)
- 一般网络通信用Linux系统IO函数,对磁盘读写的时候用标准C库的IO函数
标准C库IO函数的核心在于缓冲区,如果直接用Linux系统内核的read和write函数,每次读写都要重新访问一次磁盘,访问磁盘需要花费很多时间,IO的缓冲区很大程度减少了对磁盘的访问次数,提高了read和write函数的使用效率
标准C库函数
FILE 结构体
文件指针FILE结构体代码如下
struct _IO_FILE
{
int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
/* The following pointers correspond to the C++ streambuf protocol. */
char *_IO_read_ptr; /* Current read pointer */
char *_IO_read_end; /* End of get area. */
char *_IO_read_base; /* Start of putback+get area. */
char *_IO_write_base; /* Start of put area. */
char *_IO_write_ptr; /* Current put pointer. */
char *_IO_write_end; /* End of put area. */
char *_IO_buf_base; /* Start of reserve area. */
char *_IO_buf_end; /* End of reserve area. */
/* The following fields are used to support backing up and undo. */
char *_IO_save_base; /* Pointer to start of non-current get area. */
char *_IO_backup_base; /* Pointer to first valid character of backup area */
char *_IO_save_end; /* Pointer to end of non-current get area. */
struct _IO_marker *_markers;
struct _IO_FILE *_chain;
int _fileno;//文件描述符
int _flags2;
__off_t _old_offset; /* This used to be _offset but it's too small. */
/* 1+column number of pbase(); 0 is unknown. */
unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];
_IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};
补充知识
虚拟地址空间
文件描述符
Linux系统IO函数
不同page存储不同手册:
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);
int open(const char *pathname, int flags, mode_t mode);
/*
参数:
- pathname:要创建的文件的路径
- flags:对文件的操作权限和其他的设置
- 必选项:O_RDONLY, O_WRONLY, O_RDWR 这三个之间是互斥的
- 可选项:O_CREAT 文件不存在,创建新文件
- mode:八进制的数,表示创建出的新的文件的操作权限,比如:0775
最终的权限是:mode & ~umask
0777 -> 111111111
& 0775 -> 111111101
----------------------------
111111101
按位与:0和任何数都为0
umask的作用就是抹去某些权限。
flags参数是一个int类型的数据,占4个字节,32位。
flags 32个位,每一位就是一个标志位。
*/
open有两个函数,但是这并不是函数重载。mode_t mode是一个可选参数
前者主要用于打开已有文件
后者主要用于创建一个新的文件,因为是创建新文件,所以我们还要设置各个用户组对这个文件的权限
如果发生错误,我们可以使用perror函数打印发生的错误
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// 打开一个文件
int fd = open("a.txt", O_RDONLY);
if(fd == -1) {
perror("open");
}
// 读写操作
// 关闭
close(fd);
return 0;
}
对比前者,这个open多了个mode_t mode参数用来设置文件权限
采用3位8进制来表示不同用户/用户组对这个文件的权限
rwx 每个位上的1表示可用0表示不可用
r 读权限
w 写权限
x 执行权限
但是我们直接设置权限可能发生不合理的情况,因此再传入mode参数后,还需要进一步处理
最终的值为mode & ~umask
umask用于抹去一些不合理的权限设置,我们可以直接输入umask查看当前用户的umask
#root用户默认是0022,普通用户默认是 0002
read&write
使用read和write函数实现文件拷贝
/*
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:需要读取数据存放的地方,数组的地址(传出参数)
- count:指定的数组的大小
返回值:
- 成功:
>0: 返回实际的读取到的字节数
=0:文件已经读取完了
- 失败:-1 ,并且设置errno
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:要往磁盘写入的数据,数据
- count:要写的数据的实际的大小
返回值:
成功:实际写入的字节数
失败:返回-1,并设置errno
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
// 1.通过open打开english.txt文件
int srcfd = open("english.txt", O_RDONLY);
if(srcfd == -1) {
perror("open");
return -1;
}
// 2.创建一个新的文件(拷贝文件)
int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if(destfd == -1) {
perror("open");
return -1;
}
// 3.频繁的读写操作
char buf[1024] = {0};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0) {
write(destfd, buf, len);
}
// 4.关闭文件
close(destfd);
close(srcfd);
return 0;
}
lseek
控制文件指针,实现众多功能
- 移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
- 获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
- 获取文件的长度
lseek(fd, 0, SEEK_END);
- 拓展文件的长度,当前文件10b,增加了100个字节,最后位110b
lseek(fd, 100, SEEK_END);
//所属头文件
#include <sys/types.h>
#include <unistd.h>
//函数原型
off_t lseek(int fd, off_t offset, int whence);
/*
标准C库的函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
Linux系统函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd:文件描述符,通过open得到的,通过这个fd操作某个文件
- offset:偏移量
- whence:
SEEK_SET
设置文件指针的偏移量
SEEK_CUR
设置偏移量:当前位置 + 第二个参数offset的值
SEEK_END
设置偏移量:文件大小 + 第二个参数offset的值
返回值:返回文件指针的位置
作用:
1.移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
2.获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
3.获取文件长度
lseek(fd, 0, SEEK_END);
4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
lseek(fd, 100, SEEK_END)
注意:需要写一次数据
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open("hello.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
// 扩展文件的长度
int ret = lseek(fd, 100, SEEK_END);
if(ret == -1) {
perror("lseek");
return -1;
}
// 写入一个空数据
write(fd, " ", 1);
// 关闭文件
close(fd);
return 0;
}
文件属性操作函数
access
/*
#include <unistd.h>
int access(const char *pathname, int mode);
作用:判断某个文件是否有某个权限,或者判断文件是否存在
参数:
- pathname: 判断的文件路径
- mode:
R_OK: 判断是否有读权限
W_OK: 判断是否有写权限
X_OK: 判断是否有执行权限
F_OK: 判断文件是否存在
返回值:成功返回0, 失败返回-1
*/
#include<unistd.h>
#include<stdio.h>
int main(){
int ret = access("./a.txt",R_OK);
if(ret == -1){
perror("access");
}
else printf("a.txt can read!\n");
ret = access("./b.txt",W_OK);
if(ret == -1){
perror("access");
}
else printf("b.txt can write!\n");
ret = access("./c.txt",X_OK);
if(ret == -1){
perror("access");
}
else printf("c.txt can X!\n");
ret = access("./d.txt",F_OK);
if(ret == -1){
perror("access");
}
else printf("d.txt exited!\n");
return 0;
}
chmod
/*
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
修改文件的权限
参数:
- pathname: 需要修改的文件的路径
- mode:需要修改的权限值,八进制的数
返回值:成功返回0,失败返回-1
*/
#include<sys/stat.h>
#include<stdio.h>
void if_printf(int val){
if(val == 0) printf("a.txt can");
else printf("a.txt can not");
}
int main(){
int ret = chmod("./a.txt",0222);
if(ret == -1){
perror("chmod");
// return -1;
}
else{
int ret1 = access("./a.txt",R_OK);
if_printf(ret1);
printf("read\n");
ret1 = access("./a.txt",W_OK);
if_printf(ret1);
printf("write\n");
ret1 = access("./a.txt",X_OK);
if_printf(ret1);
printf("X\n");
}
return 0;
}
chown
使用下面两个命令可以查询用户和组的id
vim /etc/passwd
vim /etc/group
chown也就是change owner改变拥有者和用户组
int chown(const char *pathname, uid_t owner, gid_t group);
truncate
/*
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
作用:缩减或者扩展文件的尺寸至指定的大小
参数:
- path: 需要修改的文件的路径
- length: 需要最终文件变成的大小
返回值:
成功返回0, 失败返回-1
*/
#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
int main(){
int fd = open("./a.txt",O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
int ret1 = lseek(fd,0,SEEK_END);
if(ret1 == -1){
perror("lseek");
}else printf("a.txt size is = %d\n",ret1);
int ret = truncate("./a.txt",5);
if(ret == -1){
perror("truncate");
return -1;
}else {
ret1 = lseek(fd,0,SEEK_END);
printf("a.txt size now is = %d\n",ret1);
}
return 0;
}
目录操作函数
mkdir
/*
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
作用:创建一个目录
参数:
pathname: 创建的目录的路径
mode: 权限,八进制的数
返回值:
成功返回0, 失败返回-1
*/
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
int main(){
int ret = mkdir("new_mkdir",0777);
if(ret == -1){
perror("mkdir");
return -1;
}else printf("new_mkdir create!\n");
return 0;
}
rmdir
/*
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
作用:更改目录名
*/
#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(){
int ret = rmdir("./new_mkdir");
if(ret == -1){
perror("rmdir");
return -1;
}else printf("empty new_mkdir is deleted!\n");
return 0;
}
chdir & getcwd
chdir修改进程的工作目录,类似shell当中的cd
getcwd:类似shell中的pwd,获取当前的工作目录
/*
#include <unistd.h>
int chdir(const char *path);
作用:修改进程的工作目录
比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
参数:
path : 需要修改的工作目录
#include <unistd.h>
char *getcwd(char *buf, size_t size);
作用:获取当前工作目录
参数:
- buf : 存储的路径,指向的是一个数组(传出参数)
- size: 数组的大小
返回值:
返回的指向的一块内存,这个数据就是第一个参数
*/
#include<unistd.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<stdio.h>
#include<fcntl.h>
int main(){
char buff[256];
getcwd(buff,sizeof(buff));
printf("now the dir is: %s\n",buff);
int ret = chdir("../13_chdir_getcwd_new");
if(ret == -1){
perror("chdir");
return -1;
}else printf("change success!\n");
int fd = open("chdir.txt",O_CREAT|O_RDWR,0664);
if(fd == -1){
perror("open");
return -1;
}
close(fd);
char buff1[256];
getcwd(buff1,sizeof(buff1));
printf("now the dir is:%s\n",buff1);
return 0;
}
目录遍历函数
万物皆文件,目录也可以看作是一个文件 对于一个目录我们也有打开,读取,关闭的相关函数,他们分别为
- opendir 打开目录
- readdir 读取目录
- closedir 关闭目录
统计目录下文件数量
首先,我们需要判断输入参数是否合法,如果合法进入功能函数。 因为一个目录下还有目录,所以我们需要有个递归。 然后我们要一个个统计文件,所以我们函数里要有循环,那么这个函数的基本结构就是下面这样的
首先打开目录
进入循环,循环出去条件为读取到末尾 循环内部一个判断分支 如果是目录,就递归这个函数 如果是文件,计数器加1
最后关闭目录
返回计数器
/*
// 打开一个目录
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
参数:
- name: 需要打开的目录的名称
返回值:
DIR * 类型,理解为目录流
错误返回NULL
// 读取目录中的数据
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
- 参数:dirp是opendir返回的结果,后面有其中具体内容
- 返回值:
struct dirent,代表读取到的文件的信息
读取到了末尾或者失败了,返回NULL
// 关闭目录
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
*/
#include<iostream>
#include<sys/types.h>
#include<dirent.h>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
using namespace std;
int getFileNum(const char * path){
DIR * dir = opendir(path);
if(dir == NULL){
perror("opendir");
return 0;
}
struct dirent *ptr;
int total = 0;
while((ptr = readdir(dir)) != NULL){
char * dname = ptr->d_name;
if(strcmp(dname,".") == 0 || strcmp(dname,"..") == 0){
continue;
}
if(ptr->d_type == DT_DIR){
char newpath[256];
sprintf(newpath, "%s/%s", path, dname);
total += getFileNum(newpath);
}
if(ptr->d_type == DT_REG){
total++;
}
}
closedir(dir);
return total;
}
int main(int argc, char **argv){
int num = getFileNum(argv[1]);
printf("普通文件的个数为:%d\n",num);
return 0;
}
struct dirent 结构体
文件描述符相关函数
dup
/*
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符
fd=3, int fd1 = dup(fd),
fd指向的是a.txt, fd1也是指向a.txt
从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
*/
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
int main(){
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1){
perror("open");
return -1;
}
int fd1 = dup(fd);
if(fd1 == -1){
perror("dup");
return -1;
}
printf("fd : %d, fd1 : %d\n", fd, fd1);
close(fd);
char *str = "hello world!\n";
int ret = write(fd1, str, strlen(str));
if(ret == -1){
perror("write");
return -1;
}
close(fd1);
return 0;
}
dup2
重定向文件描述符 两个文件a,b,两个文件描述符fd,fd1 原来fd指向a,fd1指向b 使用dup2之后可以吧fd1指向a,以后fd1读写操作就是对a文件的操作
/*
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件描述符
oldfd 指向 a.txt, newfd 指向 b.txt
调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
oldfd 必须是一个有效的文件描述符
oldfd和newfd值相同,相当于什么都没有做
*/
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<cstring>
int main(){
int fd = open("b.txt", O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
int fd1 = open("c.txt", O_RDWR);
if(fd1 == -1){
perror("open");
return -1;
}
printf("fd: %d. fd1: %d\n",fd,fd1);
int ret = dup2(fd,fd1);
if(ret == -1){
perror("dup2");
// return -1;
}else printf("exchang success!\n");
char *str = "hello,dup2!\n";
int len = write(fd1, str, strlen(str));
if(len == -1){
perror("write");
return -1;
}
printf("fd: %d, fd1: %d, ret: %d\n",fd,fd1,ret);
close(fd);
close(fd1);
return 0;
}
fcntl
/*
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
参数:
fd : 表示需要操作的文件描述符
cmd: 表示对文件描述符进行如何操作
- F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
int ret = fcntl(fd, F_DUPFD);
- F_GETFL : 获取指定的文件描述符文件状态flag
获取的flag和我们通过open函数传递的flag是一个东西。
- F_SETFL : 设置文件描述符文件状态flag
必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
可选性:O_APPEND, O_NONBLOCK
O_APPEND 表示追加数据
NONBLOK 设置成非阻塞
阻塞和非阻塞:描述的是函数调用的行为。
阻塞: 阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回干不完不准回来
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
*/
F_DUPFD
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(int argc, char ** argv){
int fd = open("test.txt",O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
int ret = fcntl(fd, F_DUPFD);
if(ret == -1){
perror("fcntl");
return -1;
}
close(fd);
char buff[1024] = {0};
int len = read(ret,buff,sizeof(buff));
if(len == -1){
perror("read");
return -1;
}
printf("%s",buff);
close(ret);
return 0;
}
F_GETFL & F_SETFL
#include<unistd.h>
#include<fcntl.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<string.h>
//F_GETFL F_SETFL
int main(){
int fd = open("test.txt", O_RDWR);
if(fd == -1){
perror("open");
return -1;
}
int flag = fcntl(fd, F_GETFL);
if(flag == -1){
perror("fcntl");
return -1;
}
flag |= O_APPEND;
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1){
perror("fcntl");
return -1;
}
char *str = "hello\n";
write(fd, str, strlen(str));
close(fd);
}