Linux文件IO操作
文件与目录
文件除了文件内容外,还有一个名字和一些属性,即:“管理信息”,以便于进行管理。文件的属性被保存在文件的索引节点(inode)中。在Unix/Linux文件系统中Inode是文件系统中的一个特殊的数据块,用于保存文件的属性信息,这些属性信息包括:文件使用的设备号、索引节点号、文件访问权限和文件类型、文件的硬链接数、UID、GID、设备文件的设备号、文件大小、包含该文件的磁盘的块大小、文件占用的磁盘块的数、最后访问时间、最后修改时间、文件状态最后改变时间。
目录目录的内容主要由:文件名和索引节点号组成。目录中每一对文件名和索引节点号称为一个“连接”。在删除目录中文件时,实质为删除该目录和文件的硬链接数。
输入输出
通过程序读写文件之前,必须先建立程序和文件之间的连接,即打开文件,Linux中有两种方式建立连接:文件描述符和流。
文件描述符:int类型的对象,应用文件描述符的函数多为系统调用,它们提供底层的输入输出操作接口,主要用于对特定的设备进行操作。【STDIN_FILENO(标准输入)、STDOUT_FILENO(标准输出)、STDERR_FILENO(标准错误输出)】
流:建立在文件描述符之上。流操作函数都是C的标准库函数,任何运行ANSI C的系统上均支持流。
System系统调用
<函数调用时应加载头文件stdlib.h>,调用fork()产生子进程,子进程调用/bin/sh -c string来执行字符串中的命令。执行完后返回原进程。
int system(char *cmdstr);
采用以上的命令格式,cmdstr为命令行下的命令字符。
#include<stdio.h>
#include<stdlib.h>//加载system头文件
int main(){
int newset;
newset = system("ls -al .");//调用ls -al .命令
return 0;
}
system执行成功则返回shell命令后的返回值,调用/bin/sh失败则返回127;其他原因失败则返回-1;参数string为空(NULL)则返回非零值。
文件权限
int chmod (char *path , mode_t mode);
包含头文件<sys/types.h>和<sys/stat.h>,修改path指定文件的权限。权限修改成功返回0,失败返回-1。错误原因存在errno
#include<sys/types.h>
#include<sys/stat.h>
int main(){
chmod("/etc/passwd",S_IRUSR | S_IRGRP | S_IROTH);//修改文件权限
return 0;
}
mode为一个32位无符号整数,多个mode权限通过或连接添加。以下是mode的权限。
取值 | 权限 |
---|---|
S_IRUSR | 所有者读权限 |
S_IWUSR | 所有者写权限 |
S_IXUSR | 所有者执行权限 |
S_IRGRP | 组成员读权限 |
S_IWGRP | 组成员写权限 |
S_IXGRP | 组成员执行权限 |
S_IROTH | 其他人读权限 |
S_IWOTH | 其他人写权限 |
S_IXOTH | 其他人执行权限 |
USR为user所有者,GRP为group组,OTH为other其他用户,R为读,W为写,X为执行。
mode_t umask(mode_t cmask);
修改当前进程环境的权限掩码为cmask:八进制值。返回修改之前的掩码。
使用所需头文件同chmod函数。rwx-分别对应4210,权限相加为所得对象的权限。相较于掩码mask,对于文件来说建立文件的真正权限为0666-mask,对于文件夹来说建立文件夹的真正权限为0777-mask。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
int main(){
mode_t new_umask,old_umask;
new_umask = 0666;//八进制
old_umask = umask(new_umask);//返回修改前的掩码
system("touch liu1");//liu1权限:0000;
new_umask = 0444;
old_umask = umask(new_nmask);
system("touch liu2");//liu2权限:0222;
return 0;
}
其他属性
int fstat(int fd,struct stat* buf);
获取指定文件描述符文件的属性信息,并填充到buf
int stat(const char *path,struct stat *buf);
int lstat(const char *path,struct stat *buf);
获取指定文件文件的属性信息,并填充到buf。区别在于,若文件为符号链接,stat返回的是该链接指向的文件的信息,而lstat返回的是符号链接的属性信息。成功返回0,失败返回-1.
struct stat结构变量:
变量 | 信息 |
---|---|
mode_t st_mode | 文件类型和权限信息 |
ino_t st_ino | 文件结点号 |
dev_t st_dev | 文件所在设备的文件系统标识号 |
dev_t st_rdev | 文件所表示的特殊设备文件的设备标识 |
nlink_t st_nlink | 硬链接数 |
uid_t st_uid | 文件用户标识 用户ID |
gid_t st_gid | 文件用户组标识 组ID |
off_t st_size | 总大小,字节为单位 |
time_t st_atime | 文件内容最后访问的时间 |
time_t st_mtime | 文件内容最后修改时间 |
time_t st_ctime | 文件结构最后状态改变时间 |
blksize_t st_blksize | 文件系统的块大小 |
blkcnt_t st_blocks | 文件所占块的数量 |
类型判断宏
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
struct stat buf;
char filename[20] = "file.c";
if(stat(filename,&buf)<0)//运行成功为0,失败为-1
printf("%s\n",filename);
if(S_ISLNK(buf.st_mod)) printf("l");//是否为符号链接文件
else if(S_ISREG(buf.st_mod)) printf("-");//是否为常规文件
else if(S_ISDIR(buf.st.mod)) printf("d");//是否为目录文件
else if(S_ISCHR(buf.st_mod)) printf("c");//是否为字符设备
else if(S_ISBLK(buf.st_mod)) printf("b");//是否为块设备
else if(S_ISFIFO(buf.st_mod)) printf("f");//是否为命名管道
else if(S_ISSOCK(buf.st_mod)) printf("s");//是否为套接字
if(buf.st_mod&S_IRUSR) printf("r");//检验是否有所有者读权限
else printf("-");
return 0;
}
不带缓存的文件IO操作——系统调用
所需头文件:fcntl.h
文件创建
int creat(char *pathname,mod_t mode);
pathname:待创建文件路径; mode_t:新建文件的初始权限
成功返回一个最小可用的文件描述符,否则返回-1.
文件打开与关闭
int open(char *pathname, int flags[, mode_t mode])
flags:打开方式
O_RDONLY(只读打开)、O_WRONLY(写入打开)、O_RDWR(读写打开)、O_APPEND(在文件尾写入)、O_TRUNC(清空原有内容)、O_CREAT:同时通过mode指定权限、O_EXCL与CREATE一起使用,若新建文件存在则打开失败。
int close(int fd)
关闭文件,fd:文件描述符,头文件:unistd.h
文件读写
ssize_t read(int fd, void *buf, size_t count)
从fd中读取count个字节到缓存buf中,返回实际读取的字节数,出错返回-1。
ssize_t write(int fd, void *buf, size_t count)
将buf中count个字节写入到fd指向的文件中;返回实际写入的字节数,出错返回-1。
lseek(int fd, off_t offset, int whence)
定位指针,whence:SEEK_SET(从距文件开头offset位移量为新的读写位置)、
SEEK_CUR(以目前的读写位置往后增加offset个位移量)、
SEEK_END(从文件尾增加offset个位移量).
#include<stdio.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
int fsrc,fdes,nbytes,wr;
int flag = O_CREAT | O_TRUNC | O_WRONLY;
int buff[1048];
fsrc = open("/etc/passwd",O_RDONLY);//打开文件,返回文件描述符
if(fsrc<0){
printf("打开源文件错误.\n");
exit(1);
}
if((fdes = open("./pass",flag,0644))<0){//打开目标文件,方式+权限
printf("打开目的文件错误.\n");
exit(1);
}
while((nbytes=read(fsrc,buff,1024))>0){//文件复制
wr = write(fdes,buff,nbytes);
if(wr<0)
printf("写入文件错误\n");
}
close(fsrc);//关闭文件
close(fdes);
return 0;
}
锁
强制锁
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd[, flags|lockType]);
cmd:操作命令。F_GETFL:获取指定描述符的状态标识;F_SETFL:重设文件状态标识:O_APPEND、O_NONBLOCK、O_ASYNC。
lockType: 一个struct flock 类型变量
flags = fcntl(fd,F_GETFL,0);//获取文件的flags,即open的第二个参数
flags|=O_NONBLOCK;
fcntl(fd,F_SETFL,flags);//设置为非阻塞
flags&=~O_NONBLOCK;
fcntl(fd,F_SETFL,flags);//取消非阻塞状态->阻塞
文件默认以阻塞方式打开;非阻塞方式在打开文件时,在flags中添加O_NONBLOCK即可。
以非阻塞方式操作时,若不能读写,则读写函数立即返回-1,并置errno为EWOULDBLOCK。
建议锁——flock
建议性锁,不具备强制性。一个进程使用flock将文件锁住,另一个进程可以直接操作正在被锁的文件,修改文件中的数据,原因在于flock只是用于检测文件是否被加锁,针对文件已经被加锁,另一个进程写入数据的情况,内核不会阻止这个进程的写入操作,也就是建议性锁的内核处理策略。
进程使用flock尝试锁文件时,如果文件已经被其他进程锁住,此时有两种工作模式:阻塞直到锁被释放;非阻塞反回错误,errno为EWOULDBLOCK。
#include<sys/file.h>
int flock(int fd, int operation);//成功返回0,失败返回-1.
operation:锁类型
LOCK_SH:共享锁;多个进程可同时对同一个文件建立共享锁;
LOCK_EX:互斥锁;一个文件只能有一个互斥锁。
LOCK_UN:解锁。
LOCK_NB:指示若无法建立锁则立即返回进程;通常与LOCK_SH和LOCK_EX同时使用
struct flock
{
shot l_type; //锁类型:F_WRLCK(互斥锁)、F_RDLCK(共享锁)、F_UNLCK
shot l_whence; //l_start计算方式
off_t l_start; //加锁起始点:SEEK_SET、SEEK_CUR、SEEK_END
off_t l_len; //加锁长度,0:表示整个文件
pid_t l_pid; //加锁进程号,由内核设置
}
带缓存的文件流操作
又称为标准I/O操作,符合ANSI C标准,是在内存中开辟一个“缓存区”,为程序中的每个文件使用。
函数:fopen、fclose、fgetc、fputc、fgets、fputs、fread、fwrite、fseek、rewind、ftell
特殊流:stdin、stdout、stderr
特殊文件操作
目录操作
#include<dirent.h>
int mkdir(char *path,mode_t mode);//创建目录
DIR opendir(char *name);//打开目录
struct dirent* readdir(DIR *dir);//返回下一个文件的入口,到达末尾或出现错误则返回NULL
int closedir(DIR *dir);//关闭目录
目录相关的结构体
struct dirent{
Ino_t d_ino; //此目录的i节点号
ff_t d_off; //开始目录至此目录的偏移量
unsigned short int d_reclen; //文件名长度
unsigned char d_type; //文件类型
char d_name [NAME_MAX+1]; //文件名
}
DIR 同 FILE 为内部结构体,不需了解内部成员。
创建链接
int symlink(char *oldpath, char *newpath) //符号链接
符号链接:类似于windows中的“快捷方式”。符号链接是一个独立的文件,有自己的i节点,内容保存的是指向真实文件的路径。可以跨越文件系统,创建时原文件可以不存在。
int link(char *oldpath, char *newpath) //硬链接
硬链接:与原文件共享同一个i节点,相当于原文件的一个别名;硬链接不可以跨越文件系统,创建时原文件必须存在,不能给目录创建目录链接。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
system("mkdir bin");
system("cp -r /bin/ls ./bin/");
symlink("/bin/ls","ls1");
link("./bin/ls","ls2");
system("ls -l ls1");
system("ls -l ls2");
system("ls -l /bin/ls");
return 0;
}
```c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(){
system("mkdir bin");
system("cp -r /bin/ls ./bin/");
symlink("/bin/ls","ls1");
link("./bin/ls","ls2");
system("ls -l ls1");
system("ls -l ls2");
system("ls -l /bin/ls");
return 0;
}