目录
标准C库IO函数与LINUX的IO
标准C库IO
调用fopen函数打开文件,会返回一个FILE指针,该指针里有个fd用来定位要操作的文件,这个fd在进程的内核区保存,由PCB(进程控制块)管理,PCB里有一个数组存储文件描述符,大小默认1024。 前三个文件描述符默认的是绑定当前终端(将当前终端虚拟为一个文件)。
ps:一个文件被打开fopen多次,他的文件描述符是不同的。直到close()才被释放。
标准C库IO函数带有缓冲区,先写进缓冲区,再写进磁盘。
Linux的IO函数没有,直接写进磁盘,所以更适合网络通信。
虚拟地址空间
受保护的地址 有NULL, nullptr等。
用户要想操作内核区需要调用系统API,执行LINUX的系统调用。 (write,,read....)
mmu内存管理单元会将虚拟地址空间里的数据映射到物理内存。
LINUX系统IO函数
打开关闭文件
int fd = open("a.txt",O_RDONLY); //只读打开文件 返回一个文件描述符 失败=-1
//第二个必选项 是4字节int。
close(fd); 关闭文件
int fd=open("a.txt",O_RDWR|OCREAT,0777);
//O_RDONLY O_WRONLY O_RDWR互斥 可选项 O_APPEND O_ASYNC O_CREAT 等
//第三个是8进制的数,表示创建出的新文件的读写权限。 最终权限 mode&~umask(0775) 如0777->0775 抹
//去 group的写权限 umask:抹去某些权限 使赋予权限更合理一点。
umask 可以自己设置。
读写打开文件
int main()
{
using namespace std;
int fd=open("test.cpp",O_RDONLY);
if(fd==-1)
perror("open:");
int fd2=open("test.txt",O_WRONLY|O_CREAT,0664);
if(fd2==-1)
perror("write:");
char buf[1024]={0};
int l=0;
do{
l=read(fd,buf,sizeof(buf));
write(fd2,buf,l);
}while(l>0);
close(fd);
close(fd2);
return 0;
}
lseek函数
off_t lseek(int fd, off_t offset, int whence)
whence: SEEK_SET 偏移量=文件开始加offset
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 lseek(fd,100,SEEK_END)// (拓展后写一次才会真正扩大)
//比如下载时先创建一个这么大小的空间,再下载,就不会出现下载到一半空间不够的问题。
stat lstat函数
命令行:stat 文件名 可以查看文件信息
int stat(const char *pathname, struct stat *statbuf); 将文件信息存入 后面的结构体
st_mode&S_IFMT 是真正的文件类型
st_mode& S_IRUSR ==1 ? 等于则user有read权限 S_IWUSR ==1则user有write权限。
实现ll
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <iostream>
#include <pwd.h>
#include <grp.h>
#include <time.h>
int main()
{
using namespace std;
struct stat statbuf;
stat("test.txt",&statbuf);
cout<<statbuf.st_uid<<" "<<statbuf.st_size<<endl;
cout<<getpwuid(statbuf.st_uid)->pw_name<<endl; //user name
cout<<getgrgid(statbuf.st_gid)->gr_name<<endl; //group name
cout<<ctime(&statbuf.st_mtime)<<endl; //最后一次修改时间
}
文件属性操作函数
int access(const char* pathname,int mode) 判断文件对当前进程是否 mode
mode: R_OK 读权限 W_OK 写权限 X_OK执行权限 F_OK 是否存在
int chmod (pathname, mode_t mode) 修改权限
mode可以直接写入要修改的8进制权限值
int chown(pathname,uid_t owner,gid_t group) 修改所属owner,group
vim etc/passwd查看用户id
vim etc/group查看用户组
id 用户名 可以查看其 user goup ...
sudo useradd gaogao 添加一个用户gaogao
int truncate(pathname,off_t length)修改文件尺寸至指定大小
目录操作函数
mkdir(pathname,mode_t mode) mkdir("test.txt",0777)(也会与掩码操作)
rmdir(pathname) 移除某空目录
rename(oldpath,newpath)重命名
chdir(path) 修改进程当前工作目录为path(绝对路径) chdir("/home/nowcoder/projects/linuxtest/lesson2");
getcwd(char*buf,size_t size) 获取当前工作目录到buf
char buf[502];
getcwd(buf,sizeof(buf));
目录遍历函数
/* DIR *opendir(const char *name); //打开目录
struct dirent* readdir(DIR*) // 读取目录里的内容
int closedir(DIR*) //关闭目录*/
#include<dirent.h>
//获取目录下所有普通文件个数
int getFileNumber(const char* path)
{
DIR* dir = opendir(path);
if(dir==nullptr)
{
perror("open");
exit(0);//直接退出 而非返回
}
struct dirent* ptr;
int totaln=0;
while((ptr=readdir(dir))!=nullptr)
{
char* dname=ptr->d_name;
// cout<<dname<<endl;
if(strcmp(dname,".")==0||strcmp(dname,"..")==0)
{
continue;
}
//判断普通文件或目录
if(ptr->d_type==DT_DIR)
{
char newpath[256];
sprintf(newpath,"%s/%s",path,dname);
totaln += getFileNumber(newpath);
}
if(ptr->d_type==DT_REG)
totaln++;
}
closedir(dir);
return totaln;
}
dup dup2
int dup(int oldfd) 复制文件描述符,指向同一个文件,返回值为fd表中最小的空闲的fd。
int dup2(int oldfd,int newfd) 重定向文件描述符,调用后,newfd对应文件close, newfd指向oldfd对应文件,当值相同,相当什么都没做。 返回的是newfd的fd;
fcntl
int fcntl(int fd,int cmd,.../*arg*/)
cmd:对 fd 如何操作
F_DUPFD 复制描述符,返回一个新的文件描述符
F_GETFL 获取指定文件描述符的flag :O_RDONLY等
F_SETFL 设置fd状态flag:必选:O_RDONLY等,可选 O_APPEND O_NONBLOCK等
int main(int argc,char* argv[])
{
nt fd=open("test.txt",O_RDONLY);
int fd2=fcntl(fd,F_DUPFD);//复制fd
cout<<fd<<" "<<fd2<<endl;
int flag=fcntl(fd2,F_GETFL); //获取fd2的flag
cout<<flag<<endl;
int fd3=fcntl(fd2,F_SETFL,flag|O_APPEND);//fd2的flag追加O_APPEND
flag=fcntl(fd2,F_GETFL); //再次获取fd2的flag
cout<<flag<<endl;
return 0;
}