• 【Linux】系统api和指令的模拟实现


    1.虚拟地址空间

    image-20220630020528641

    1.1一个进程打开的最大文件数量

    #include <unistd.h>
    #include<stdio.h>
    #inlcude<sys/stat.h>
    #include<sys/types.h>
    #include<fcntl.h>
    
    //int open(char*name,mode) 
    //如果返回值小于0,则表示打开文件或者创建文件失败
    
    
    int maim(){
        int num=3;  //系统默认打开stdin,stdout,stderr;所以打开的文件的下标从3开始
        char filename[128]={0}//定义文件名
        while(1){
            sprinft(filename,"temp_%04d",num++);
            int fd=open(filename,O_RDNOLY|O_CREAT,0666);
            if(fd<0){
                perror("open err");  //打印警告
                break;
            }
        }
        printf("num==%d\n",num);
        return 0;
    }
    //输出: num==1023
    //需要在程序结束后再关闭所有的文件,否则程序会出现死循环
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    2.stat文件信息函数

    获取文件信息的函数,文化信息存放在参数buf中

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    int stat(const char *path, struct stat *buf);
    int fstat(int fd, struct stat *buf);
    int lstat(const char *path, struct stat *buf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    image-20220630023911206

    hello.hard是hello的硬链接,两者指向同一个块。

    vim ../ #查看目录文件
    
    • 1

    image-20220630024301427

    目录项存放了该目录下的文件名,inode,读条大小

    2.1stat参数和返回值

    参数:

    pathname :文件名

    struct stat *buf(传入参数):struct stat sb;&sb

    传入类型参数:需要我们传入参数,由函数完成参数的内容填充

    返回值:

    成功返回0,失败返回-1;

    #teststat.c
    #include <unistd.h>
    #include<stdio.h>
    #inlcude<sys/stat.h>
    #include<sys/types.h>
    #include<fcntl.h>
    int main(int agrc,char*argv[]){
        struct stat sb;
        stat(argv[1],&sb);//sb的值由函数填充
        return 0;
    }
    int main(int argc,char*argv[]){
        struct stat sb;
        stat(argv[1],&sb);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    gcc teststat -o teststat.out
    ./teststat.out 文件名  #获得该文件的信息(主要信息存放在struct stat*sb中);
    
    • 1
    • 2

    2.实现简单版ls -l(对单文件操作)

    int stat(const char *path, struct stat *buf);
    
    • 1

    stat结构体的内容image-20220630131348328

    st_mode中包含了文件的主要信息,st_ino包含了文件连接数

    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
      	//获取文件数据
        struct stat sb;  //struct stat*sb;
        //传入型数据类型
        stat(argv[1],&sb); //文件信息都存储在sb中。
        //输出文件信息,解析属性信st_mode,st_uid,st_gid,st_atime,st_ino
        char stmode[11]={0};   //文件类型--用户权限---用户组权限---其他人权限,最后一个位置为\n
    	memset(stmode,'-',sizeof(stmmode));  //初始化
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.1获取文件类型

    文件类型相关的宏(st_mode)

    image-20220630113446637

    文件权限宏image-20220630125626016

    if(S_ISREG(sb.st_mode))  stmode[0]='-';	//is it a regular file?
         if(S_ISDIR(sb.st_mode))  stmode[0]='d';	//directory?
         if(S_ISCHR(sb.st_mode))  stmode[0]='c';	//character device?
         if(S_ISBLK(sb.st_mode))  stdmode[0]='b';		//block device?
         if(S_ISFIFO(sb.st_mode)) stdmode[0]='p';		//FIFO (named pipe)?
         if(S_ISLNK(sb.st_mode))  stdmode[0]='l';//symbolic link?  (Not in POSIX.1-1996.)
         if(S_ISSOCK(sb.st_mode)) stdmode[0]='s';//socket?  (Not in POSIX.1-1996.)
        //解析文件权限
        //解析文件拥有者权限位
        if(sb.st_mode&S_IRUSR) stdmode[1]='r';
        if(sb.st_mode&S_IWUSR) stdmode[2]='w';
        if(sb.st_mode&S_IXUSR) stdmode[3]='x';
        //解析文件所有组权限位
        if(sb.st_mode&S_IRGRP) stdmode[4]='r';
        if(sb.st_mode&S_IWGRP) stdmode[5]='w';
        if(sb.st_mode&S_IXGRP) stdmode[6]='x';
        if(sb.st_mode&S_IROTH) stdmode[7]='r';
        if(sb.st_mode&S_IWOTH) stdmode[8]='w';
        if(sb.st_mode&S_IXOTH) stdmode[9]='x';
    	stdmode[10]='\0';
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2.2获取连接数

    int num=sb.st_nlink;
    
    • 1

    2.3获取用户名和所有组

    //使用struct passwd* getpwuid(uid_t uid) 获得用户名,需要传入uid
    //使用struct group* getgrgid(gid_t gid) 获得组名,需要传入gid
    //stat结构体已经包含了两者
    struct passwd*sbwd=(struct passwd*)getpwuid(sb.st_uid);
    struct group* sbgrp=(struct group*)getgrgid(sb.st_git);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    image-20220630133256264

    image-20220630133331674

    2.4获取时间

    //获取时间:struct tm *localtime(const time_t *timep);对应stat结构体中的st_atime
    struct tm*sttime=localtime(&sb.st_atim.tv_sec);
    
    • 1
    • 2

    image-20220630133605731

    2.5实现ls-l

    #include<stdio.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<string.h>
    #include<pwd.h>
    #include<time.h>
    #include<string.h>
    int main(int argc,char*argv[]){
        if(argc!=2){
            printf("./a.out filename\n");
            return -1;
        }
        //获取文件数据
        struct stat sb;
        //传入型数据类型
        stat(argv[1],&sb); //文件信息都存储在sb中。
        //输出文件信息,解析属性信息stat_mode,uid,gid,tim
        //获取文件类型
        char stmode[11]={0};   //文件类型--用户权限---用户组权限---其他人权限---连接数
        //通过宏判断文件的类型
        memset(stmode,'-',sizeof(stmode));
         if(S_ISREG(sb.st_mode))  stmode[0]='-';	//is it a regular file?
         if(S_ISDIR(sb.st_mode))  stmode[0]='d';	//directory?
         if(S_ISCHR(sb.st_mode))  stmode[0]='c';	//character device?
         if(S_ISBLK(sb.st_mode))  stmode[0]='b';		//block device?
         if(S_ISFIFO(sb.st_mode)) stmode[0]='p';		//FIFO (named pipe)?
         if(S_ISLNK(sb.st_mode))  stmode[0]='l';//symbolic link?  (Not in POSIX.1-1996.)
         if(S_ISSOCK(sb.st_mode)) stmode[0]='s';//socket?  (Not in POSIX.1-1996.)
        //解析文件权限
        //解析文件拥有者权限位
        if(sb.st_mode&S_IRUSR) stmode[1]='r';
        if(sb.st_mode&S_IWUSR) stmode[2]='w';
        if(sb.st_mode&S_IXUSR) stmode[3]='x';
        //解析文件所有组权限位
        if(sb.st_mode&S_IRGRP) stmode[4]='r';
        if(sb.st_mode&S_IWGRP) stmode[5]='w';
        if(sb.st_mode&S_IXGRP) stmode[6]='x';
        if(sb.st_mode&S_IROTH) stmode[7]='r';
        if(sb.st_mode&S_IWOTH) stmode[8]='w';
        if(sb.st_mode&S_IXOTH) stmode[9]='x';
        stmode[10]='\0';
        //解析连接数
        //使用struct passwd* getpwuid(uid_t uid) 获得用户名,需要传入uid
        //使用struct group* getgrgid(gid_t gid) 获得组名,需要传入gid
        //获取时间:struct tm *localtime(const time_t *timep);对应stat结构体中的st_atime
        struct tm*sttime=localtime(&sb.st_atim.tv_sec);
        char timebuf[20]={0};
        sprintf(timebuf,"%d月  %d %02d:%02d",sttime->tm_mon,sttime->tm_yday,sttime->tm_hour,sttime->tm_min);
        //结构体体类型:指针使用->访问成员;非指针使用.访问成员
        printf("%s %2ld %s %s %4ld %s %s\n ",stmode,sb.st_nlink,getpwuid(sb.st_uid)->pw_name,getgrgid(sb.st_gid)->gr_name,sb.st_size,timebuf,argv[1]);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    3.stat和lstat的区别

    stat可以穿透链接,找到链接对应的文件;而lstat无法穿透。

    //通过stat获取文件大小     也可以使用fseek获取文件大小
    struct stat* sb;
    stat(filename,sb);
    printf("文件大小是:%d",sb->st_size);
    
    • 1
    • 2
    • 3
    • 4

    4.access判断权限函数

    作用:判读用户是否具有r,w,x权限;

    #include <unistd.h>
    int access(const char *pathname, int mode);
    /*
    参数:
    	pathname:文件名
    	mode:R_OK读权限, W_OK写权限,X_OK:执行权限,F_OK:是否存在
    返回值:
    	成功:有权限或者文件存在返回0
    	失败:返回-1
    */
    EXAMPLE:
    int main(int argc,char*argv[]){
        if(!access(argv[1],W_OK)) printf("有可写权限");
        if(!access(argv[1],R_OK)) printf("有可读权限");
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    5.truncate文件截断

    文件截断函数

    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char *path, off_t length);
    /*
    参数:
    	path:文件名
    	length:文件大小  长度大于原文件就扩展y
    返回值:
    	成功:返回0
    	失败:返回-1
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6.chown和rename函数

    6.1chown函数

     #include <unistd.h>
     int chown(const char *path, uid_t owner, gid_t group);
    /*
    参数:
    	path:文件名
    	owner:用户ID  在文件/etc/passwd可以查询
    	group:组ID	  在文件/etc/group可以查询
    返回值:
    	成功:返回0
    	失败:返回-1
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6.2rename函数

    #include <stdio.h>
    int rename(const char *oldpath, const char *newpath);
    /*
    参数:
    	oldpath:旧文件名
    	newpath:新文件名
    返回值:
    	成功:0
    	失败:-1
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    7.chdir目录切换和getcwd目录查询函数

    7.1getcwd获取当前目录

    #include<unistd.h>
    char* getcwd(char*buf,size_t size);
    /*
    	buf:传处参数,缓冲区中存放的就是路径
    	size:缓冲区大小
    返回值:
    	成功:返回目录指针
    	失败:返回NULL
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    模拟实现pwd指令
    mypwd.c
    #include<stdio.h>
    #include<unistd.h>
    int main(int argc,char*argv[]){
        char buf[128];
        getcwd(buf,sizeof(buf));
        printf("%s",buf);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    gcc mypwd.c
    ./a.out
    /home/west/dr2
    
    • 1
    • 2
    • 3

    7.2chdir函数

    #include<unistd.h>
    int chdir(const char*path);
    /*
    	path:进入目录路径指针
    返回值:
    	成功:0
    	失败:-1
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    8.mkdir目录创建函数

    #include <sys/stat.h>
    #include <sys/types.h>
    int mkdir(const char *pathname, mode_t mode);
    /*
    参数:
    	pathname:创建的目录名
    	mode:权限  0777|umask   或者 0777&~umask
    	这里的0777是目录创建的默认初始权限,umask表示权限掩码,最后创建的默认权限位0777|umask
    返回值:
    	成功:0
    	失败:-1
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    模拟实现mkdir指令

    #include<sys/stat.h>
    #include<sys/types.h>
    #include<stdio.h>
    int main(int argc,char*argv[]){
        if(argc!=2){
            printf("a.out filename\n");
        }
        //umask权限掩码
        //int mkdir(const char *pathname, mode_t mode)的mode参数是权限
        //而目录的默认权限参数是0777,而umask一般是0002
        //所以目录的默认参数是0777|umask=0775(八进制数)
        mkdir(argv[1],0775);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    gcc mydir.c
    ./a.out mydir
    ls -l
    drwxrwxr-x -------------- mydir
    
    • 1
    • 2
    • 3
    • 4

    9.rmdir目录删除函数

    #include <unistd.h>
    int rmdir(const char *pathname);
    /*
    	pathname:目录名
    返回值:
    	成功:返回0;
    	失败:返回-1;
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    模拟实现rmdir指令

    //删除空目录
    #include<stdio.h>
    #include<unistd.h>
    int main(int argc,char*argv[]){
        if(argc!=2){
            printf("a.out filename\n");
            return -1;
        }
        rmdir(argv[1]);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    gcc myrmdir.c
    ./a.out mydir
    #结果为成功删除mydic目录
    
    • 1
    • 2
    • 3

    10.统计指定目录下普通文件的个数,子目录递归统计

    10.1opendir目录打开函数

    #include <sys/types.h>
    #include <dirent.h>
    DIR *opendir(const char *name);
    /*
    	name:目录名
    返回值:The  opendir() and fdopendir() functions return a pointer to the directory stream.
    	意思是返回一个目录项信息的的指针,指向目录项信息,主要作为readdir和closedir的参数传入
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    10.2readdir目录读取函数

    调用readdir函数时,目录指针自动向下移动

    #include <dirent.h>
    struct dirent *readdir(DIR *dirp);
    //如果读到EOF或者发生错误就返回NULL
    
    • 1
    • 2
    • 3

    image-20220701020235782

    文件类型d_type的宏定义

    image-20220701021654656

    10.3closedir目录关闭函数

    #include <sys/types.h>
    #include <dirent.h>
    int closedir(DIR *dirp);
    
    • 1
    • 2
    • 3

    10.4目录位置调整函数

    void rewinddir(DIR* dirp); 	//把目录指针恢复到起始位置
    long telldir(DIR*dirp);  	//获取目录读写位置   返回值为返回当前在目录中的位置
    void seekdir(DIR*dirp,long loc);	//修改目录读写位置
    
    • 1
    • 2
    • 3
    10.5统计目录文件个数程序
    coutdir.c
    #include<stdio.h>
    #include<unistd.h>
    #include <sys/types.h>
    #include <dirent.h>
    #include<string.h>
    int num=0;
    void Dircount(char*dirname){
        //打开目录
        DIR* dirp=opendir(dirname);
        //如果为空
        if(dirp==NULL){
            printf("opendir err\n");
            return ;
        }
        //循环读目录,如果是普通文件++,如果是目录,递归统计个数
        struct dirent*dentp=NULL;
        while((dentp=readdir(dirp))!=NULL){  //调用依次readdir函数,目录指针就移动一次。
            //如果是目录文件
            if(dentp->d_type==DT_DIR){
                //如果是.或者是..目录就跳过这两个目录
                if(strcmp(".",dentp->d_name)==0||strcmp("..",dentp->d_name)==0){
                    continue;
                }
                //如果是其他目录,就递归调用
                //进程的工作路径无法直接打开
                //使用dirname拼接下一级目录
                char tmpname[256]={0};
                sprintf(tmpname,"%s/%s",dirname,dentp->d_name);
                Dircount(tmpaname);
            }
    		if(dentp->d_type==DT_REG){
                num++;
            }
        }
        closedir(dirp);
    } 
    int main(int argc,char*argv[]){
        if(argc!=2){
            printf("a.out filename\n");
            return -1;
        }
        Dircount(argv[1]);
        printf("文件数为:%d",num);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    gcc coutdir.c
    ./a.out mk
    文件数为 4
    
    • 1
    • 2
    • 3

    11.dup和dup2输出重定向函数

    复制文件描述符,用于备份

    #include <unistd.h>
    int dup(int oldfd);  //返回当前最小可用的文件描述符,并指向oldfd指向的文件
    int dup2(int oldfd, int newfd); //输出重定向函数
    
    • 1
    • 2
    • 3

    dup(oldfd):

    image-20220701033415347

    dup2(oldfd,newfd):

    image-20220701033821601

    案例

    //在代码中执行2次printf("hello world\n");第一次输出到hello文件中,后一次输出到屏幕上
    #include<stdio.h>
    #include <unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    int main(){
        //备份标准输出stdout的描述符
        int outfd=dup(1);
        //printf默认打开stdin,stdout,stderr
        //打开文件hello,返回open文件的文件描述符
        int fd=open("hello",O_WRONLY|O_CREAT,0666);
        //重定向
        dup2(fd,1);//这里的1指的是stdout,把标准输出重定向为hello文件
        //这样hello world就输出到hello文件中
        printf("hello world\n");
        fflush(stdout);
        //恢复标准输出指向
        dup2(outfd,1);
        //输出到屏幕上
        printf("hello world\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    windows域的搭建
    LVS+Keepalived 高可用群集
    知识关联视角下金融证券知识图谱构建与相关股票发现
    牛客网零碎小东西
    超快的 Python 包管理工具「GitHub 热点速览」
    JDBC版本简介
    【Linux】:环境变量
    GOM登录器配置免费版生成图文教程
    阿里云认证是什么?报考要什么条件?
    一文彻底搞懂性能测试
  • 原文地址:https://blog.csdn.net/qq_53893431/article/details/125551288