简介1:标准I/0,和文件I/O
标准I/O:是ansic建立的一个标准i/o模型,就是包含stdio.h头文件的库函数,标准I/O又称为有缓存的I/O,即他对文件I/O的操作做了进一步的处理,包括缓存的分配等。
文件I/O:又叫做无缓存的I/O,例如read/write的操作,是直接调用系统中的系统调用。
首先你要明确linux操作系统函数之间的调用关系,首先是“应用程序”调用“库函数”,“库函数”调用“系统调用”,“系统调用”通过一个函数指针去驱动“设备”
不同点:
1.标准I/O提供了缓冲机制,比如fopen函数,打开一个文件的同时,创建了一个缓冲区,还创建了一个包含文件和缓冲区数据的数据结构,直白讲就是一个结构体
2.操作的对象有差别,文件I/O错做的是文件,读写硬盘,是对文件的描述符操作,而标准I/O操作的是控制台,操作的是字符流,这里就可以解释为什么open的返回值叫文件描述符,而fopen的返回值叫文件流。
需要认识的是:
1.系统调用(sysytem call)是用户进程访问内核的接口,把用户从底层编程解放出来,提高系统的安全性。
2.库函数(lib)是提供统一的编程接口,便于程序移植,为了实现某个功能而封装到应用程序接口的集合,库函数是使用系统调用实现的。
(一) 库函数中针对文件操作的实现,他们是
fopen() :打开
fclose() :关闭
fread() :读
fwrite() :写
fscanf() :输入
fprintf() :输出
fgets(); fputs();gets() puts():针对字符串的读写操作
fgetc();fputc(); :针对字符的读写操作
------------------------------------------------------------------------------------------------
fseek ftell1.int fseek(FILE *stream, long offset, int whence); 改变文件的读写位置/文件位置标识符
stream :文件流
offset :偏移量whence :基准
SEEK_SET :文件开头
SEEK_CUR :当前位置
SEEK_END :文件结尾
2.long ftell(FILE *stream);返回当前文件的读写位置3.空洞文件:文件中有空字符。因为向后进行文件读写位置的移动。
#include <stdio.h>
int main(int argc,char *argv[]){
FILE *fp;
if(argc != 2){
fprintf(stderr,"usage:%s filename\n",argv[0]);
}
if((fp = fopen(argv[1], "r+")) == NULL){
perror("fopen");
return -1;
}
printf("%ld\n", ftell(fp)); //查看当前位置
fseek(fp, 10, SEEK_END); //改变位置,在末尾添加10个空字符
printf("%ld\n", ftell(fp)); //查看当前位置
fputc('a', fp); //向文件内添加字符'a'
fclose(fp);
return 0;
}
------------------------------------------------------------------------------------------------
(二) 系统调用中对文件操作的实现
open();close();write();read();.....等,在linux下man 2中,可以看出系统调用与库函数中的函数名比较,少了一个f,其实库函数就是在系统调用的基础上进行了一个包装,这个做个例子:如系统调用中有一个函数为:int set(); 那么库函数中的函数为:int fset(){ return set;},即对系统调用做了一个包装,中间可以进行简单的数据处理和一些其他的操作等,但是不改变原理。
在man 2 中查看某一个系统调用函数时,要按照以下几步来查看和分析
1 头文件
2 函数名和函数的功能(函数名<-->函数功能)
3 参数列表4 返回值和错误处理
-------------------------------------------------------------------------------------------------
1.int open(const char *pathname, int flags, mode_t mode);:打开文件,返回值是文件描述符
pathname:文件的路径
flags:O_RDONLY: O_WRONLY:O_RDWR 三选一
可选:O_CREAT :文件不存在就创建
:O_TRUNC :文件已经存在就截短
:O_APPEND :文件追加
mode:创建文件的时候,文件的模式/权限。普通文件0666 其他的文件0777
2.int close(int fd);:关闭文件
3.ssize_t //有符号整形,就是一个整型数
ssize_t read(int fd, void *buf, size_t count);:读文件
fd :文件描述符
buf :缓冲区的首地址
count :期望/想要读的字节的个数
返回值 :实际读到的字节的个数
4.ssize_t write(int fd, const void *buf, size_t count);:写文件
fd :文件描述符
buf :缓冲区的首地址
count :期望/想要写的字节的个数
返回值 :实际写入的字节的个数
-------------------------------------------------------------------------------------------------
时间相关的系统调用:time_t time(time_t *t);
返回从1970年开始到现在的秒数。
char *ctime(const time_t *timep);
将时间转换成字符串,字符串的格式"Wed Jun 30 21:49:08 1993\n"
struct tm *localtime(const time_t *timep);
将时间转换成结构体 struct tm
程序运行的流程:
进程:一个应用程序的运行实例。
1.创建一个新的进程
2.加载应用程序
3.运行启动例程
创建空间,初始化。打开3个文件,标准输入/标准输出/标准错误
4.调用main函数
5.退出操作
文件流 文件描述符
标准输入stdin 0
标准输出stdout 1
标准错误stderr 2
文件描述符分配原则:
从当前未被使用的文件描述符中找到最小的那一个。
文件描述符 对应 一个已经打开的文件。
eg1:获取当前日期,并打印日志
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[]){
if(argc != 2){
fprintf(stderr,"usage:%s logfile\n",argv[0]);
return -1;
}
if(do_log(argv[1])){
printf("write failed\n");
}
return 0;
}
int do_log(char *filename){
int count=0;
int fd;
time_t t;
char *p;
fd = open(filename,O_CREAT|O_WRONLY|O_APPEND, 0666);
if(fd == -1){
perror("open");
}
while(count < 5){
time(&t);
p = ctime(&t);
write(fd,p,strlen(p));
count++;
sleep(1);
}
close(fd);
return 0;
}
eg2:实现cat查看指令
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#define N 64
int main(int argc, char *argv[]){
int fd;
ssize_t n;
char buf[N];
if(argc != 2){
fprintf(stderr, "usage: %s filename\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
if(fd == -1){
perror("open");
return 1;
}
while(1){
bzero(buf, N);
if((n =read(fd, buf, N-1)) <= 0) break;
printf("%s", buf);
}
printf("\n");
close(fd);
return 0;
}
eg3:实现cp拷贝命令
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#define N 64
int main(int argc, char *argv[]){
if(argc != 3){
fprintf(stderr,"usage:%s oldfilename newfilename\n",argv[0]);
return -1;
}
do_cp(argv[1], argv[2]);
return 0;
}
int do_cp(char *oldfile, char *newfile){
char buf[N];
int fod,fed;
ssize_t n;
fod = open(oldfile,O_RDONLY);
if(fod == -1){
perror("oldfile");
return 1;
}
fed = open(newfile, O_CREAT|O_WRONLY|O_TRUNC, 0666);
if(fed == -1){
perror("newfile");
return 2;
}
while(1){
bzero(buf,N);
if(n = read(fod,buf,1) <= 0){
break;
}
write(fed,buf,1);
}
close(fod);
close(fed);
return 0;
}
目录文件操作(库函数)
opendir();
DIR *opendir(const char *name);
打开一个目录文件,返回目录流
readdir();
struct dirent *readdir(DIR *dirp);
读目录流,获取一个目录条目。每个目录条目代表目录中包含的一个文件的相关信息。
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported by all file system types */
char d_name[256]; /* filename */
};
closedir
int closedir(DIR *dirp);
关闭目录流
-------------------------------------------------------------------------------------------------
stat/lstat
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
获取文件的状态信息
path 文件的路径
buf 出参,结构体地址
struct stat {
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
};
-------------------------------------------------------------------------------------------------
eg4:实现ls -a命令
/*
* opendir
* while(readdir)
* printf(d_name)
* closedir
* */
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
void *do_ls(char *);
int main(int argc, char *argv[]){ // lsa /home/uplooking/
if(argc == 1){
do_ls(".");
}else{
while(--argc){
printf("%s:\n", *++argv);
do_ls(*argv);
}
}
return 0;
}
void *do_ls(char *dirname){
DIR *dir_ptr;
struct dirent *direntp;
dir_ptr = opendir(dirname);
if(dir_ptr ==NULL){
perror("opendir");
return ;
}
while((direntp = readdir(dir_ptr)) != NULL ){
printf("%s ",direntp->d_name);
}
closedir(dir_ptr);
return ;
}
eg5:实现ls -l命令
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#define N 64
void display_time();
void display_file(char *filename, char *showname);
void display_dir(char *dirname);
int main(int argc, char *argv[]){
struct stat buf;
if(argc != 2){
fprintf(stderr,"usage:%s filename",argv[0]);
return -1;
}
lstat(argv[1], &buf);
if(S_ISDIR(buf.st_mode)){ //如果是目录
display_dir(argv[1]);
}else{ //如果是文件
display_file(argv[1],argv[1]);
}
return 0;
}
void display_time(){
time_t t;
struct tm *loc_tm = NULL;
char arr[12][4] = {"Jan","Feb","Mar","Apr",
"May","Jun","Jul","Aug",
"Sep","Oct","Nov","Dec"};
time(&t);
loc_tm = localtime(&t);
printf(" %-4s %d %d:%d ",arr[loc_tm->tm_mon]
,loc_tm->tm_mday
,loc_tm->tm_hour
,loc_tm->tm_min);
return ;
}
void display_file(char *filename, char *showname){
struct stat buf;
int i=8;
struct passwd *pas;
struct group *gid;
lstat(filename, &buf);
switch(buf.st_mode & S_IFMT){
case S_IFSOCK: printf("s"); break;
case S_IFLNK : printf("l"); break;
case S_IFREG : printf("-"); break;
case S_IFBLK : printf("b"); break;
case S_IFDIR : printf("d"); break;
case S_IFCHR : printf("c"); break;
case S_IFIFO : printf("p"); break;
}
// S_IRUSR 00400 owner has read permission
// 100 000 000
// 876 543 210
while(i >= 0){
if(buf.st_mode & (1<<i)){
switch(i%3){
case 2: printf("r"); break;
case 1: printf("w"); break;
case 0: printf("x"); break;
}
}else{
printf("-");
}
i--;
}
printf(" %d",(int)buf.st_nlink); // number of hard links
pas = getpwuid(buf.st_uid);
printf(" %s",pas->pw_name); // user ID of owner
gid = getgrgid(buf.st_gid);
printf(" %s",gid->gr_name); // user ID of owner
printf(" %d",(int)buf.st_size); // total size, in bytes
display_time(); // time
printf(" %s\n",showname); // filename
return ;
}
void display_dir(char *dirname){
DIR *dir;
struct dirent *dir_ent;
char str[N] = {0};
dir = opendir(dirname);
while((dir_ent = readdir(dir)) != NULL){
sprintf(str,"%s/%s",dirname,dir_ent->d_name);
display_file(str, dir_ent->d_name);
}
closedir(dir);
return ;
}
eg6:实现pwd查看路径命令
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define SIZE 1024
void printpathto(ino_t this_inode);
void inum_to_name(ino_t inode_to_find, char *namebuf, int buflen);
ino_t get_inode(char *fname);
int main(int argc, char *argv[]){
printpathto(get_inode("."));
putchar('\n');
return 0;
}
/* 递归打印路径:a,打印父目录的路径 b,打印当前目录名
* 递归:1.终止条件:/ 父目录..和当前目录.的inode结点号一样的
* 2.步长:移动到上一层目录
* 3.收敛:向根目录/进行收敛
* */
void printpathto(ino_t this_inode){
ino_t my_inode;
char its_name[SIZE];
if(get_inode("..") != this_inode){
// 移动当前路径
chdir(".."); // cd ..
inum_to_name(this_inode, its_name, SIZE);
my_inode = get_inode(".");
printpathto(my_inode); //先打印上层目录名,先调用函数
printf("/%s", its_name); //在打印当前目录名
}
}
void inum_to_name(ino_t inode_to_find, char *namebuf, int buflen){
struct dirent *dir_ent;
DIR *dir;
dir = opendir(".");
if(dir == NULL){
perror("opendir");
return ;
}
while((dir_ent = readdir(dir)) != NULL){
if(dir_ent->d_ino == inode_to_find){ //注释掉的为容错的处理,两个buffer大小问题
// if(strlen(dir_ent->d_name) < buflen){
strcpy(namebuf,dir_ent->d_name);
// }else{
// strcpy(namebuf,dir_ent->d_name);
// }
closedir(dir);
return ;
}
}
return ;
}
ino_t get_inode(char *fname){
struct stat info;
if(stat(fname, &info) == -1){
printf("errpr\n");
return -1;
}
return info.st_ino;
}