第六章 文件操作
6.1 系统编程概述:
shell命令提供给普通用户
系统调用是操作系统提供给程序员使用的接口函数
C语言的库函数封装了系统调用,方便使用。
系统调用以函数库的形式提供
对GCC不会自动链接的库要手动进行链接,如lpthread线程库。
可以通过man lseek 来查看函数名信息,对于一些函数,即是Linux命令,也是系统调用,这时可以通过输入man 2 mkdir 来进行查看, man mkdir 只是帮助信息。
对于库函数,输入man 3 <库函数名>查看
6.2 Linux的文件结构
文件包括:
1.本身包含数据
2.文件的属性
目录也是一种文件
根目录下:
1./bin——存放普通用户的可执行命令,系统中的任何用户都可以执行该目录中的命令
2./boot——Linux内核及启动系统时所需的文件,独立分区
3./etc——用户的配置文件,如帐号等
4./home——普通用户的主目录
5./lib——用于存放系统的配置文件
6./root ——超级用户root 的主目录
7./sbin——管理系统的命令
8./usr——用于存放系统应用程序及相关性文件,如说明文档,帮助文件等。
9./var——存放系统中经常变化的文件,如日志文件,用户邮箱等。
10./proc——虚拟文件系统
文件结构:大多数用虚拟文件系统VFS,少数通过设备驱动本身提供的接口。VFS 可以支持多种不同的文件系统。
文件分类:1.普通文件 2.目录文件 3.字符特殊文件 4.块特殊文件 5.FIFO 管道
6.套接字 7.符号链接
访问权限: r-read-4 w-white -2 x -可执行-1
前三个 :所有者的权限
中间三个:与所有者同组的权限
后三个 :其他用户的权限
实现chmod:
/*************************************************************************
> File Name: my_chmod.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 08时20分54秒
************************************************************************/
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
int main(int argc,char *argv[])
{
int mode;
int mode_u;//文件所属用户权限 user
int mode_g;//与文件所有者同组的权限 group
int mode_o;//其他用户操作权限 other
char *path;
if(argc<3){ //如果用户输入不到3个参数,提醒用户输入相应参数
printf("%s <mode num> <target file>\n",argv[0]);
exit(0);
}
mode=atoi(argv[1]);//把字符转换成整形
if(mode>777||mode<0){ //如果mode超界,返回错误
printf("mode num error!");
exit(0);
}
mode_u = mode/100; //拆分
mode_g = mode%100/10;
mode_o = mode%10;
mode = mode_u*8*8+mode_g*8+mode_o; //把八进制转化为对应的十进制
path = argv[2]; //路径赋值
if( chmod(path,mode) == -1){ //调用系统函数
perror("chmod error");
exit(1);
}
return 0;
}
6.3 文件的输入和输出
文件的创建与关闭:open() :可以有多种打开方式
creat() :只能以只写的方式打开
close() :注意关闭文件
文件的读写 : read() :注意,其读出的数据是否符合其要读出的数据
write() :注意,其写入的数据是否符合其要写入的数据
文件读写指针的移动:
lseek() :其可以定位到当前位置,开始位置,结束位置,并进行相应的偏移
这里注意其返回值,为当前位置到开始位置的字节数。
下面来看实例:
/*************************************************************************
> File Name: file_read.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 09时24分34秒
************************************************************************/
#include<sys/types.h>
#include<stdio.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
typedef long long ll;
void my_err(const char * err_string,int line)
{
fprintf(stderr,"line %d:\t",line);//错误输出,输出到终端
perror(err_string); //错误函数提示
exit(1);
}
int my_read(int fd)
{
int len;
int ret;
int i;
char read_buf[64];
if(lseek(fd,0,SEEK_END) == -1){
my_err("lseek",__LINE__);
}
if((len = lseek(fd,0,SEEK_CUR)) == -1){//利用lseek计算文件大小
my_err("lseek",__LINE__);
}
if(lseek(fd,0,SEEK_SET) == -1){
my_err("lseek",__LINE__);
}
printf("len:%d\n",len);
if((ret = read(fd,read_buf,len))<0){ //读入数据
my_err("read",__LINE__);
}
for(int i=0;i<len;i++) //输出数据
printf("%c",read_buf[i]);
printf("\n");
return ret;
}
int main(int argc,char *argv[])
{
int fd;
char write_buf[32]="Hellow my vacation!";
if((fd=open("test.cpp",O_RDWR|O_CREAT|O_TRUNC,S_IRWXU)) ==-1) {//可写可读打开
my_err("open",__LINE__);
}else{
printf("creat file success!");
}
if(write(fd,write_buf,strlen(write_buf)) != strlen(write_buf)){//写入
my_err("write",__LINE__);
}
my_read(fd);//读文件
printf("_____________________________\n");
if(lseek(fd,20,SEEK_END) == -1){ //中间隔了20字节的数据
my_err("lseek",__LINE__);
}
if(write(fd,write_buf,strlen(write_buf)) != strlen(write_buf)){//写入数据
my_err("write",__LINE__);
}
my_read(fd);
close(fd); //关闭文件
return 0;
}
6.3.4 dup dup2 fcntl ioctl
dup:找到一个最小的描述符,最小的描述符和其参数描述符指向同一个文件
dup2:把旧的描述符赋值给新的描述符
fcntl:其可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的 各种属性。
1.得到标志值和设置标志值,设置打开方式,获得打开方式。
/*************************************************************************
> File Name: file_read.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 09时24分34秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
typedef long long ll;
void my_err(const char * err_string,int line)
{
fprintf(stderr,"line %d:\t",line);//错误输出,输出到终端
perror(err_string); //错误函数提示
exit(1);
}
int main(int argc,char *argv[])
{
int ret;
int access_mode;
int fd;
if((fd = open("test1.cpp",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU)) == -1){
my_err("open",__LINE__);
}
if((ret = fcntl(fd, F_SETFL, O_APPEND))<0){//设置最加
my_err("fcntl",__LINE__);
}
printf("O_APPEND=%d\n",O_APPEND );
printf("只读O_RDONLY=%d\n",O_RDONLY );
printf("只写O_WRONLY=%d\n",O_WRONLY );
printf("读写O_RDWR=%d\n",O_RDWR );
if((ret = fcntl(fd,F_GETFL,0))<0){//得到其打开状态
my_err("fcntl",__LINE__);
}
printf("ret=%d\n",ret);
access_mode=ret & 3;//得到其文件打开方式,取其后两位
printf("%d\n", access_mode);
if(access_mode == O_RDONLY){
printf("example_1 access mode:read only\n");
}else if(access_mode == O_WRONLY){
printf("example_1 access mode:write only\n");
}else if(access_mode == O_RDWR){
printf("example_1 access mode:read + write\n");
}
printf("ret=%d\n",ret);
if(ret & O_APPEND){//判断其是否是追加
printf(" ,append\n");
}
printf("O_APPEND=%d\n",O_APPEND);
if(ret & O_NONBLOCK){//判断是否锁定
printf(" ,nonblock\n");
}
printf("O_NONBLOCK=%d\n", O_NONBLOCK);
if(ret & O_SYNC){
printf(", sync\n");
}
printf("O_SYNC=%d\n", O_SYNC);
printf("\n");
return 0;
}
2.文件锁:文件锁分为共享锁(读锁)和互斥锁(写锁),锁具有不兼容性(其是指的在不同进程上,如果在一个进程上,新锁会取代旧锁)
- 建议性锁是这样规定的:每个使用上锁文件的进程都要检查是否有锁存在,当然还得尊重已有的锁。内核和系统总体上都坚持不使用建议性锁,它们依靠程序员遵守这个规定。(Linux默认是采用建议性锁),其对于合作的进程有效,但对于其他不合作的,建议锁并不能阻止。
- 强制性锁是由内核执行的。当文件被上锁来进行写入操作时,在锁定该文件的进程释放该锁之前,内核会阻止任何对该文件的读或写访问,每次读或写访问都得检查锁是否存在。
来看代码:
/*************************************************************************
> File Name: file_read.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 09时24分34秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
typedef long long ll;
void my_err(const char * err_string,int line)
{
fprintf(stderr,"line %d:\t",line);//错误输出,输出到终端
perror(err_string); //错误函数提示
exit(1);
}
/*struct flock
{
short_l_type;//锁的类型
short_l_whence;//偏移量的起始位置
short_l_start;//偏移量
short_l_len; //要锁的字节数
short_l_pid; //锁的属主进程ID
};
*/
int lock_set(int fd,struct flock* lock)
{
if(fcntl(fd,F_SETLK,lock) == 0)
{
if(lock->l_type == F_RDLCK){
printf("set read lock ,pid:%d\n", getpid());
}else if(lock->l_type == F_WRLCK){
printf("set write lock,pid:%d\n", getpid());
}else if(lock->l_type == F_UNLCK){
printf("release lock,pid:%d\n", getpid());
}
}
else
{
perror("lock operation fail\n");
return -1;
}
}
int lock_test(int fd,struct flock * lock)
{
if(fcntl(fd,F_GETLK,lock) == 0)
{
if(lock->l_type == F_UNLCK)//可以上锁
{
printf("lock can be set in fd\n");
return 0;
}
else //不能上锁
{
if(lock->l_type == F_RDLCK)//输出其已锁的类型
printf("can`t set lock, read lock has been set by:%d\n", lock->l_pid);
else if(lock->l_type == F_WRLCK)
printf("can`t set lock, write lock has been set by:%d\n", lock->l_pid);
return -2;
}
}
else
{
perror("get incompatible locks fail");
return -1;
}
}
int main(int argc,char *argv[])
{
int fd;
int ret;
struct flock lock;
char read_buf[32];
/**************************打开文件,并写文件******************************/
if((fd = open("test2.cpp",O_CREAT|O_TRUNC|O_RDWR,S_IRWXU)) == -1){
my_err("open",__LINE__);
}
if(write(fd,"test lock",10) != 10){
my_err("write",__LINE__);
}
/***********************初始化结构体******************************/
memset(&lock,0,sizeof(struct flock));
lock.l_start = SEEK_SET;
lock.l_whence = 0;
lock.l_len = 0;
/*******************上读锁****************************************/
lock.l_type = F_RDLCK;
if(lock_test(fd,&lock) == 0){
lock.l_type = F_RDLCK;
lock_set(fd,&lock);
}
lseek(fd,0,SEEK_SET);
if((ret = read(fd,read_buf,10))<0){
my_err("read",__LINE__);
}
read_buf[ret]='\0';
printf("%s\n", read_buf);
getchar();
/*****************上写锁*****************************/
lock.l_type = F_WRLCK;
if(lock_test(fd,&lock) == 0){
lock.l_type = F_WRLCK;
lock_set(fd,&lock);
}
/**********************取消锁****************************/
lock.l_type = F_UNLCK;
lock_set(fd,&lock);
close(fd);
return 0;
}
3.ioctl :用来控制特殊设备文件的属性
/*************************************************************************
> File Name: file_read.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 09时24分34秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <net/if.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
typedef long long ll;
unsigned char g_eth_name[16];
unsigned char g_macaddr[6];
unsigned char g_subnetmask;
unsigned char g_ipaddr;
unsigned char g_broadcast_ipaddr;
void init_net(void)
{
int i;
int sock;
struct sockddr_in sin;
struct ifreq ifr;
sock=socket(AF_INET,SOCK_DGRAM,0);
if(sock == -1){
perror("socket");
}
strcpy(g_eth_name,"eth0");
strcpy(ifr.ifr_name,g_eth_name);
printf("eth name\t%s\n", g_eth_name);
if(ioctl(sock,SIOCGIFHWADDR,&ifr)<0){
perror("ioctl");
}
memcpy(g_macaddr,ifr.ifr_hwaddr.sa_data,6);
printf("local mac:\t");
for(int i=0;i<5;i++){
printf("%.2x:", g_macaddr[i]);
}
printf("%.2x\n", g_macaddr[i]);
if(ioctl(sock,SIOCGIFADDR,&ifr)<0){
perror("ioctl");
}
memcpy(&sin,&ifr.ifr_addr, sizeof(sin));
g_ipaddr = sin.sin_addr.s_addr;
printf("local eth0:\t%s\n", inet_ntoa(sin,sin_addr));
if(ioctl(sock,SIOCGIFBRDADDR,&ifr)<0){
perror("ioctl");
}
memcpy(&sin,&ifr.ifr_addr,sizeof(sin));
g_subnetmask = sin.sin_addr.s_addr;
printf("subnetmask:\t%s\n", inet_ntoa(sin.sin_addr));
close(sock);
}
int main(int argc,char *argv[])
{
init_net();
return 0;
}
6.4 文件属性操作
6.4.1获取文件属性:
利用stat() fstat() lstat()
/*************************************************************************
> File Name: file_read.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 09时24分34秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
typedef long long ll;
int main(int argc,char *argv[])
{
struct stat buf;
if(argc != 2){
printf("Usage: my_stat <filename>\n");
exit(0);
}
if(stat(argv[1],& buf) == -1){
perror("stat:");
exit(1);
}
printf("mode:%o\n", buf.st_mode);
printf("user ID of owner is:%d\n", buf.st_uid);
printf("group ID of owner is:%d\n", buf.st_gid);
return 0;
}
6.4.2设置文件属性
1.chmod/fchmod
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
2.chown/fchown/lchown 更变所属组
3.truncate/ftruncate 用于改变文件大小
4.utime 更改文件的st_time和st_ctime域
int utime(const char *filename, const struct utimbuf *times);
5.umask 屏蔽字,权限权限
/*************************************************************************
> File Name: set_file.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 19时54分28秒
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
using namespace std;
typedef long long ll;
int main(int argc,char *argv[])
{
umask(0);
if(creat("test3.cpp",S_IRWXU|S_IRWXG|S_IRWXO) < 0){
perror("creat");
exit(1);
}
getchar();
getchar();
umask(S_IRWXO);//屏蔽其他用户权限
if(creat("test4.cpp",S_IRWXU|S_IRWXG|S_IRWXO) < 0){
perror("creat");
exit(1);
}
truncate("test4.cpp",100);//修改大小
if(chown("test3.cpp",123,123) < 0){//更改组
perror("chown");
}
return 0;
}
6.5文件的移动和删除
6.5.1 文件的移动 rename()
/*************************************************************************
> File Name: rm.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 20时33分26秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
int main(int argc,char *argv[])
{
if(argc<3){
printf("my_mv <old file> <new file>\n");
exit(0);
}
if(rename(argv[1],argv[2]) < 0){
perror("rename");
exit(1);
}
return 0;
}
6.5.2文件的删除
unlink()删除文件
rmdir() 删除目录
remove()封装两者
/*************************************************************************
> File Name: unlink.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月18日 星期一 21时08分37秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
typedef long long ll;
void my_err(const char * err_string,int line)
{
fprintf(stderr,"line %d:\t",line);//错误输出,输出到终端
perror(err_string); //错误函数提示
exit(1);
}
int main(int argc,char *argv[])
{
int fd;
char buf[32];
if((fd = open("temp5.cpp",O_RDWR|O_CREAT|O_TRUNC,S_IRWXU)) < 0){
my_err("open",__LINE__);
}
if(unlink("temp5.cpp") < 0){
my_err("unlink",__LINE__);
}
printf("file unlinked\n");
if(write(fd,"temp5.cpp",5) < 0){
my_err("write",__LINE__);
}
getchar();
return 0;
}
6.6目录操作
6.6.1 目录创建和删除:
mkdir()
rmdir()
6.6.2 获取当前目录
getcwd();
6.6.3 设置工作目录
chdir()并不能真正改变,因为其只能改变本进程的,而无法改变其他进程的。
/*************************************************************************
> File Name: my_cdvc.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月19日 星期二 08时08分11秒
************************************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/limits.h>
typedef long long ll;
void my_err(const char * err_string,int line)
{
fprintf(stderr,"line %d:\t",line);//错误输出,输出到终端
perror(err_string); //错误函数提示
exit(1);
}
int main(int argc,char *argv[])
{
char buf[PATH_MAX+1];
if(argc < 2){
printf("my_cd <target path>\n");
exit(1);
}
if(chdir(argv[1]) < 0){
my_err("chdir",__LINE__);
}
if(getcwd(buf,512) < 0){
my_err("getcwd",__LINE__);
}
printf("%s\n", buf);
return 0;
}
6.6.4 获取目录信息
opendir();打开
readdir();读取
closedir();关闭
DIR *形态的目录流
struct dirent {}结构体
/*************************************************************************
> File Name: show_file.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年07月19日 星期二 08时23分04秒
************************************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
using namespace std;
typedef long long ll;
int my_readir(const char *path)
{
DIR *dir;
struct dirent *ptr;
if((dir = opendir(path)) == NULL){
perror("opendir");
return -1;
}
while((ptr = readdir(dir)) != NULL){
printf("file name :%s\n",ptr->d_name);
}
closedir(dir);
return 0;
}
int main(int argc,char *argv[])
{
if(argc < 2){
printf("listfile <target path>\n");
exit(1);
}
if(my_readir(argv[1]) < 0){
exit(1);
}
return 0;
}