一、标准C库与Linux系统库函数区别
1.库示意图和内存示意图
2.文件描述符
文件描述符是内核为了高效管理已被打开的文件所创建的索引,用于指向被打开的文件,所有执行I/O操作的系统调用都通过文件描述符;文件描述符是一个简单的非负整数,用以表明每个被进程打开的文件。程序刚刚启动时,第一个打开的文件是0,第二个是1,以此类推。也可以理解为文件的身份ID。
3.系统I/O函数
3.1 int open(const char *pathname, int flags);
/*
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> //打开一个已经存在的文件 int open(const char *pathname, int flags); 参数: -pathname:文件路径 -flags :文件权限设置(O_RDONLY, O_WRONLY, or O_RDWR)只读只写和读写 返回值: -文件描述符 errono: -属于Linux系统函数库,属于全局变量,记录的是最近的错误号 #include<stdio.h> void perror (const char * s);作用:打印error对应的错误描述 //创建一个新的文件 int open(const char *pathname, int flags, mode_t mode); */ #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; }
3.2 int open(const char *pathname, int flags, mode_t mode);
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<stdio.h> #include<unistd.h> /* //创建一个新的文件 int open(const char *pathname, int flags, mode_t mode); 参数: -pathname:文件路径 -flags :文件权限设置和其他参数 -必选项(O_RDONLY, O_WRONLY, or O_RDWR) -可选项(O_APPEND追加,O_CREATE文件不存在则进行创建等) -mode:八进制的数,表示创建出的新的文件的操作权限,比如0775 r=4,w=2,x=1 最终权限是(mode & ~umask) umask作用是抹去某些权限 返回值: -文件描述符 errono: -属于Linux系统函数库,属于全局变量,记录的是最近的错误号 */ int main(){ int fd = open("create.txt",O_RDWR | O_CREAT, 0777); if(fd==-1){ perror("open"); } //关闭 close(fd); return 0; }
3.3 文件读写命令ssize_t read(int fd, void *buf, size_t count); && ssize_t write(int fd, const void *buf, size_t count);
/* #include <unistd.h> ssize_t read(int fd, void *buf, size_t count); 参数: -fd:文件描述符 -buf:读取数据存放的地方,(数组地址) -count:指定的数组的大小 返回值: -成功: >0:返回实际的读取到的字节数 ==0: 文件已经读取完毕 -失败: -1: errno #include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); 参数: -fd:文件描述符 -buf:写入数据存放的地方,(数组地址) -count:需要写的数据的大小 返回值: -成功:实际写入的字节数 -失败:返回-1,设置errno */ #include <unistd.h> #include <stdio.h> #include<sys/types.h> #include <sys/stat.h> #include<fcntl.h> int main(){ //通过open打开english.txt int srcfd = open("english.txt",O_RDONLY); if(srcfd==-1){ perror("open"); return -1; } //创建一个新的文件 int desfd = open("cpy.txt",O_WRONLY|O_CREAT,0664); if(desfd==-1){ perror("open"); return -1; } //频繁的读写操作 char buf[1024] = {0}; int len = 0; do { len = read(srcfd, buf, sizeof(buf)); write(desfd, buf, len); }while(len>0); //关闭文件 close(desfd); close(srcfd); return 0; }
3.4 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得到,可以用来操作某个文件 -offset:偏移量 -whence: SEEK_SET:设置文件指针偏移量 SEEK_CUR:设置偏移量:当前位置+第二个参数offset值 SEEK_END:设置偏移量:文件大小+第二个参数offset值 lseek作用 1.移动文件指针到头文件 lseek(fd, 0, SEEK_SET) 2.获取当前文件指针位置 lseek(fd, 0, SEEK_CUR) 3.获取文件长度 lseek(fd, 0, SEEK_END) 4.拓展文件长度 lseek(fd, 100, SEEK_END ) */ #include <unistd.h> #include <stdio.h> #include<sys/types.h> #include <sys/stat.h> #include<fcntl.h> int main(){ //获取文件描述符 int srcfd = open("hello.txt",O_RDWR); if(srcfd==-1){ perror("open"); return -1; } //扩展文件长度 int ret = lseek(srcfd,100,SEEK_END); if(ret==-1){ perror("lseek"); return -1; } //写入空数据 write(srcfd," ",1); //关闭文件 close(srcfd); return 0; }
3.5 文件信息命令int stat(const char *pathname, struct stat *statbuf);
/* #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int stat(const char *pathname, struct stat *statbuf); 作用: 获取文件相关的信息 参数: -pathname:要操作的文件路径 -statbuf:结构体变量,传出参数,用于保存获取到的文件信息 返回值: -成功:返回0 -失败:返回-1,设置errno int lstat(const char *pathname, struct stat *statbuf); 作用:获取软连接文件的信息,而不是软连接指向的文件的信息 */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include<stdio.h> int main(){ struct stat statbuf; int ret = stat("a.txt", &statbuf); if(ret == -1){ perror("stat"); return -1; } printf("size:%ld\n", statbuf.st_size); return 0; }
3.6 通过stat函数返回struct stat.st_mode成员模拟实现Linux ls -l 指令
首先是stat结构体成员
struct stat { dev_t st_dev ;//文件的设备编号 ino_t st_ino; //节点 mode_t st_mode ; //文件的类型和存取的权限 nlink_t st_nlink; //连至该文件的硬连接数目 uid_t st_uid; //用户ID gid_t st_gid; //组ID dev_t st_rdev ; //设备文件的设备编号 off_t st_size; //文件字节数(文件大小 blksize_t st_blksize; //块大小 blkcnt_t st_blocks ; //块数 time_t st_atime; //最后—次访问时间 time_t st_mtime; //最后一次修改时间 time_t st_ctime; //最后一次改变时间(指属性) } ;
st_mode变量组成(权限)
模拟实现ls -l
//模拟实现ls -l //-rw-rw-r-- 1 coco coco 12 12月 2 14:12 a.txt #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include<stdio.h> #include<pwd.h> #include <grp.h> #include<time.h> #include<string.h> int main(int argc, char * argv[]){ //判断输入的参数是否正确 if(argc < 2){ printf("%s filename\n", argv[0]); return -1; } //通过stat函数获取文件信息 struct stat st; int ret = stat(argv[1], &st); if(ret==-1){ perror("stat"); return -1; } //获取文件权限 char perms[11] = {0};//用于保存文件类型和权限的字符串 switch (st.st_mode& S_IFMT) { case S_IFLNK: perms[0]='l'; case S_IFDIR: perms[0]='d'; case S_IFREG: perms[0]='-'; case S_IFCHR: perms[0]='c'; case S_IFBLK: perms[0]='b'; case S_IFSOCK: perms[0]='s'; case S_IFIFO: perms[0]='p'; break; default: perms[0]='?'; } //判断文件的访问权限 //对用户自身 perms[1]=st.st_mode & S_IRUSR ? 'r' : '-'; perms[2]=st.st_mode & S_IWUSR ? 'w' : '-'; perms[3]=st.st_mode & S_IXUSR ? 'x' : '-'; //对同组 perms[4]=st.st_mode & S_IRGRP ? 'r' : '-'; perms[5]=st.st_mode & S_IWGRP ? 'w' : '-'; perms[6]=st.st_mode & S_IXGRP ? 'x' : '-'; //对其他组 perms[7]=st.st_mode & S_IROTH ? 'r' : '-'; perms[8]=st.st_mode & S_IWOTH ? 'w' : '-'; perms[9]=st.st_mode & S_IXOTH ? 'x' : '-'; //获取硬链接数 int linknum = st.st_nlink; //获取文件所有者 char * fileuser = getpwuid(st.st_uid)->pw_name; //文件所在组 char * filegroup = getgrgid(st.st_gid)->gr_name; //获取文件大小 long int filesize = st.st_size; //获取修改时间 char * time = ctime(&st.st_mtime); char mtime[512] = {0}; strncpy(mtime ,time, strlen(time)-1); //输出所有信息 char buf[1024]; sprintf(buf, "%s %d %s %s %ld %s %s",perms, linknum, fileuser, filegroup, filesize, mtime, argv[1]); printf("%s\n", buf); return 0; }
3.7 判断权限命令 int access(const char *pathname, int mode);
/* #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",F_OK); if(ret == -1){ perror("access"); } else{ printf("文件存在\n"); } return 0; }
3.8 文件权限设置命令 int chmod(const char *pathname, mode_t mode);
/* #include <sys/stat.h> int chmod(const char *pathname, mode_t mode); 作用:修改权限 参数: -pathname:文件路径 -mode:需要修改的权限,八进制数 返回: -成功:0 -失败:-1 */ #include <unistd.h> #include <sys/stat.h> #include<stdio.h> int main(){ int ret = chmod("a.txt",0775); if(ret == -1){ perror("chmod"); return -1; } else{ printf("修改成功"); } return 0; }
3.9文件大小更改命令int truncate(const char *path, off_t length);
/* #include <unistd.h> #include <sys/types.h> int truncate(const char *path, off_t length); 作用:缩减或者扩展文件的尺寸至指定的大小 参数: -path:文件路径 -length:需要最终文件变成的大小,扩展会使用空格填充 */ #include <unistd.h> #include <sys/types.h> #include<stdio.h> int main(){ int ret = truncate("a.txt",20); if(ret == -1){ perror("truncate"); } else{ printf("成功\n"); } return 0; }
3.10 更改进程的工作目录命令int chdir(const char *path);
/* #include <unistd.h> int chdir(const char *path); 作用:修改进程的工作目录 比如在/home/coco目录启动了可执行程序a.out,进程的工作目录为/home/coco 参数: -path:需要修改的目录路径 返回值: -成功:0 -失败:-1 #include <unistd.h> char *getcwd(char *buf, size_t size); 作用:获取当前的工作目录 比如在/home/coco目录启动了可执行程序a.out,进程的工作目录为/home/coco 参数: -buf:存储的路径,指向的是一个数组(传出参数) -size:数组的大小 返回值: 返回的是指向的一块内存,为第一个参数(buf)的地址 char *getwd(char *buf); */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<stdio.h> #include<unistd.h> int main(){ //获取当前的工作目录 char buf[128]; getcwd(buf, sizeof(buf)); printf("当前的工作目录是:%s", buf); //修改工作目录 int ret = chdir("/home/coco/LinuxTest/lession13"); if(ret == -1){ perror("chdir"); } else{ printf("目录更改成功\n"); int fd = open("chdir.txt", O_CREAT|O_RDWR,0664); if(fd == -1){ perror("open"); } close(fd); char buf1[128]; getcwd(buf1,sizeof(buf1)); printf("当前的工作目录是:%s\n", buf1); } return 0; }
3.11 目录新建命令int mkdir(const char *pathname, mode_t mode);
/* #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 <unistd.h> #include <sys/types.h> #include<stdio.h> int main(){ int ret = mkdir("aaa",0777); if(ret == -1){ perror("mkdir"); } else{ printf("成功\n"); } return 0; }
3.12 目录遍历命令
struct dirent{ //此目录进入点的inode ino_t d_ino; //目录文件开头至此目录进入点的位移 off_t d off; // d_name 的长度,不包含NULL字符 unsigned short int d_reclen; //d_name所指的文件类型 unsigned char d_type; //文件名 char d name [ 256]; } ;
/* #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *name); 作用:打开一个目录 参数: -path:需要打开目录的路径 返回值: -DIR*目录流信息] -失败返回NULL #include <dirent.h> struct dirent *readdir(DIR *dirp); 作用:读取目录中的数据,指向下一个目录流的实体 参数: -dirp:通过opendir返回的DIR指针 返回值: -结构体,代表读取到的文件的信息 -读取到了末尾或者失败,返回NULL #include <sys/types.h> #include <dirent.h> int closedir(DIR *dirp); 作用:关闭目录 参数: -dirp:通过opendir返回的DIR指针 返回值: */ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #include<unistd.h> #include<dirent.h> #include<string.h> #include<stdlib.h> int getFilenm(const char * path); //读取某个目录下所有文件个数 int main(int argc, char * argv[]){ if(argc<2){ printf("%s path\n",argv[0]); return -1; } int num = getFilenm(argv[1]); printf("普通文件的个数为:%d\n", num); return 0; } //用于递归获取目录下所有普通文件的个数 int getFilenm(const char * path){ DIR *dir = opendir(path); if(dir==NULL){ perror("opendir:"); exit(0); } //记录文件个数 int total=0; struct dirent * ptr; 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 += getFilenm(newpath); } else if(ptr->d_type== DT_REG){ total++; } } //关闭目录 closedir(dir); return total; }
3.13 复制文件描述符和重定向文件描述符
/* #include <unistd.h> int dup(int oldfd); 作用:用文件描述符表中,最小的文件描述符复制原文件描述符 参数: -旧文件描述符 返回值: -成功:新的文件描述符 -失败:-1 int dup2(int oldfd, int newfd); 作用:重定向文件描述符 说明:oldfd指向a.txt,newfd指向b.txt,调用函数成功后: newfd和 b.txt做close,newfd指向了a.txt 参数:用指定的new去复制old,新的指向old */ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #include<unistd.h> #include<dirent.h> #include<string.h> #include<stdlib.h> int main(){ //指向1.txt int fd = open("1.txt", O_RDWR|O_CREAT,0664); if(fd == -1){ perror("open"); return -1; } //指向2.txt int fd1 = open("2.txt",O_RDWR|O_CREAT,0664); if(fd1 == -1){ perror("open"); return -1; } printf("fd : %d, fd1 : %d\n", fd, fd1); //重定向fd1 int fd2 = dup2(fd , fd1); if(fd2 == -1){ perror("dup2:"); return -1; } //通过fd1去写数据,操作的是1.txt而不是2.txt char * str = "hello, dup2"; int len = write(fd1, str, strlen(str)); if(len==-1){ perror("write"); return -1; } printf("fd : %d, fd1 : %d, fd2: %d\n", fd, fd1, fd2); close(fd); close(fd1); return 0; }
3.14 文件描述符操作命令
/* #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ); 作用:操作文件描述符 参数: -fd:需要操作的文件描述符 -cmd:表示对文件描述符进行什么操作 1.复制文件描述符:-F_DUPFD,复制的是第一个参数fd,返回新的文件描述符 2.获取指定的文件描述符状态flag:-F_GETFL 获取的包含不限于O_CREAT,O_RWONLY 3.设置文件描述符文件状态flag,必选项:O_RDONLY,O_WRONLY,O_RDWD,不可以被修改 可选项:O_APPEND,NONBLOCK,可以被修改 NONBLOCK:设置成非阻塞 阻塞和非阻塞:描述的是函数调用的行为。阻塞等待输入 */ #include<sys/types.h> #include<sys/stat.h> #include<fcntl.h> #include<stdio.h> #include<unistd.h> #include<dirent.h> #include<string.h> #include<stdlib.h> int main(){ //复制文件描述符 // int fd = open("1.txt",O_RDONLY); // int ret =fcntl(fd, F_DUPFD); //2.修改或者获取文件状态flag,修改 int fd = open("1.txt",O_RDWR); if(fd == -1){ perror("open"); return -1; } //首先获取文件描述符状态flag int flag = fcntl(fd, F_GETFL); if(flag == -1){ perror("fcntl"); return -1; } flag |= O_APPEND; //修改状态描述符,加入O_APPEND指令 int ret = fcntl(fd, F_SETFL, flag); if(ret == -1){ perror("fcntl"); return -1; } char * str = "nihao"; write(fd, str, strlen(str)); close(fd); return 0; }