04_Linux文件IO和Linux系统函数

代码: https://github.com/WHaoL/study/tree/master/00_06_Linux_SystemCode_and_SocketCode

代码: https://gitee.com/liangwenhao/study/tree/master/00_06_Linux_SystemCode_and_SocketCode

1. 文件IO

1.1 Linux系统IO和C标准库IO

1.标准C库IO函数

在这里插入图片描述

fopen -> open
// linux c FILE结构体定义: /usr/include/libio.h

struct _IO_FILE 
{
    
    
  int _flags;		/* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags
 
  /* The following pointers correspond to the C++ streambuf protocol. */
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */
  char* _IO_read_ptr;	/* Current read pointer */
  char* _IO_read_end;	/* End of get area. */
  char* _IO_read_base;	/* Start of putback+get area. */
  char* _IO_write_base;	/* Start of put area. */
  char* _IO_write_ptr;	/* Current put pointer. */
  char* _IO_write_end;	/* End of put area. */
  char* _IO_buf_base;	/* Start of reserve area. */
  char* _IO_buf_end;	/* End of reserve area. */
  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */
 
  struct _IO_marker *_markers;
 
  struct _IO_FILE *_chain;
 
  int _fileno;
#if 0
  int _blksize;
#else
  int _flags2;
#endif
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */
 
#define __HAVE_COLUMN /* temporary */
  /* 1+column number of pbase(); 0 is unknown. */
  unsigned short _cur_column;
  signed char _vtable_offset;
  char _shortbuf[1];
 
  /*  char* _save_gptr;  char* _save_egptr; */
 
  _IO_lock_t *_lock;
#ifdef _IO_USE_OLD_IO_FILE
};

// 在文件: /usr/include/stdio.h
typedef struct _IO_FILE FILE;

2.标准C 库IO和 Linux系统IO的关系

在这里插入图片描述

/*
1. 使用标准c函数往磁盘写数据:
	- 数据进入到函数的缓冲区
		- 缓冲区满了, 写入磁盘
        - 强制这些数据写入磁盘, flush/fclose, 等
    - 标准c函数会调用Linux系统函数
    - 数据由linux系统函数写入到磁盘
1. 读磁盘
  	- 标准C函数调用系统函数读磁盘数据
  	- Linux系统函数将读到的数据写入标准C函数的缓冲区
  	- 标准C函数去缓冲区中读数据
*/

1.2 虚拟地址空间

在这里插入图片描述

// 每启动一个磁盘应用程序(进程), 这个程序就对应一个自己的虚拟地址空间
// 虚拟地址空间: 
// 	1. 用户区	
// 		.text -> 代码区
//		.data .bss -> 全局数据区
//		黄色区域 	-> 堆区(比栈大得多)
//		橙色		  -> 栈区(比较小)
1. 内核区
 - 只有内核可以操作其中的数据, 用户是不能读写的
 - 1G
 - 内核区用于进程管理的部分 -> 进程控制块(结构体)

在这里插入图片描述

在进程控制块中有一个组成部分 -> 文件描述符表(整形数组)
	- 这个数组默认大小: 1024, 代表在当前应用程序中(进程), 能够同时打开的文件个数
	
/*
	标准c中的三个FILE*, 对应的是终端
		标准输入: stdin   -> 	文件描述符: STDIN_FILENO(0)		-> 当前终端对应的文件
		标准输出: stdout  -> 	文件描述符: STDOUT_FILENO(1)		-> 当前终端对应的文件
		标准错误: stderr  -> 	文件描述符: STDERR_FILENO(2)		-> 当前终端对应的文件
*/

int buf[1024];
如果打开对应的某个磁盘文件, 这个文件对应一个文件描述符, 这个文件描述符被存储到文件描述符表中
	- 默认文件描述表数组所有的元素都是空的, 然后前三个被终端占用了, 分别赋值为0, 1, 2
	- 未占用的元素应该是空的, 没有存储数据的元素
	- 找到之后如何赋值: 是第几个元素就被赋值为几

1.3 Linux系统IO函数

perror

// 查看错误号: 
//   /usr/include/asm-generic/errno-base.h
//   /usr/include/asm-generic/errno.h
// errno: Linux系统函数库中的全局变量, 记录函数调用失败之后的错误号, 每个错误号都有对应的错误描述

// 通过错误号得到错误描述
// 参数: 用户对错误信息的描述, 调用这个函数错误信息会被打印到终端
// 用户描述+系统对错误编号的描述
#include <stdio.h>
void perror(const char *s);

open/close

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

// 打开一个已经存在的文件
int open(const char *pathname, int flags);
// 创建新文件
int open(const char *pathname, int flags, mode_t mode);
参数:
- pathname: 要操作的文件的文件名
- flags: 对文件的操作权限
	必选项: O_RDONLY,  O_WRONLY,  or  O_RDWR, 不能同时用, 只能用一个
	可选项:
		- O_APPEND: 追加, 指定文件文件有写权限的时候可追加
		- O_CREAT: 文件存在不创建, 不存在就创建
		- O_EXCL: 检测文件是否存在, 必须要和O_CREAT一起使用
			O_CREAT | O_EXCL
- mode: 8进制整形数, 0777, 创建出的新文件的权限
	- 实际权限的算法:  (mode & ~umask)
	- 假设: 权限: 0664, umask: 0002, ~umask: 775
           110 110 100
           111 111 101
           &
           110 110 100          
返回值:
	- 成功: 可用文件描述符
    - 失败: -1
        
#include <unistd.h>
int close(int fd);			
参数:
	fd: open成功得到的文件描述符
	
// 示例程序
// 检测文件是否存在
// 不存在, 创建新文件, 存在返回-1
int fd2 = open("./world.txt", O_RDWR|O_CREAT|O_EXCL, 0777);
printf("fd2 = %d\n", fd2);
if(fd2 == -1) 
{
    
       
    perror("open-world.txt");
}

read

// 标准C库, 有缓冲区, 默认8M
#include <stdio.h>
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

// Linux系统函数没有缓冲区
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
	- fd: open文件之后得到的文件描述符, 通过该fd可用找到磁盘文件
	- buf: 存储数据的缓冲区地址(数组地址)
    - count: 缓冲区大小(数组最大容量)
返回值:
	- 成功: 读到的字节数
	- 文件读完了: 返回0
	- 失败: -1

write

// 标准C库, 有缓冲区, 默认8k
#include <stdio.h>
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

// Linux系统函数没有缓冲区
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
	- fd: open文件之后得到的文件描述符, 通过该fd可用找到磁盘文件
	- buf: 存储了要写的数据
	- count: buf中存储的有效数据的长度
返回值: 
	- 成功: 写入的字节数
	- 什么也没写: 0
    - 失败: -1
        
        
// 准备一个比较大的磁盘文件, open两个文件, 一个读文件, 一个读到的数据写入另一个文件中

lseek

// fseek
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

// lseek
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
	- fd: open文件之后得到的文件描述符, 通过该fd可用找到磁盘文件
	- offset: 一个整形的偏移量
	- whence: 
		SEEK_SET: 设置文件指针偏移量, 偏移量就是通过第二个参数指定的//从文件头开始偏移
		SEEK_CUR: 当前文件指针偏移量 + 第二个参数指定的偏移//从当前指针位置开始偏移
		EEK_END: 当前文件大小 + 第二个参数指定的偏移//从文件尾开始偏移
返回值: 文件指针最后的偏移值

// 1. 文件指针移动到文件头(重置文件指针)
lseek(fd, 0, SEEK_SET);

// 1. 获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);

// 2. 获取文件总大小
lseek(fd, 0, SEEK_END);

// 4. 可以拓展文件大小, 原文件大小100字节, 拓展大小为110, 多出的字节是10
// 使用lseek拓展文件大小, 最后必须进行一次写操作, 才能成功
lseek(fd, 10, SEEK_END);
write(fd, " ", 1);

2. Linux其他系统函数

1.stat/lstat函数

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *pathname, struct stat *buf);
	参数: 
		- pathname: 操作的文件的路径
		- buf: 结构体变量, 传出

在这里插入图片描述

// man fstat


// 摘自man手册 fstat()
struct stat
{
    
    
    dev_t st_dev;         /* ID of device containing file */
    ino_t st_ino;         /* inode number */
    mode_t st_mode;       /* file type and mode */
    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 filesystem I/O */
    blkcnt_t st_blocks;   /* number of 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
                  precision for the following timestamp fields.
                  For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim; /* time of last access */
    struct timespec st_mtim; /* time of last modification */
    struct timespec st_ctim; /* time of last status change */

#define st_atime st_atim.tv_sec /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};


struct stat 
{
    
    
    dev_t          st_dev;        	//文件的设备编号
    ino_t           st_ino;        	//节点
    mode_t      st_mode;      		//文件的类型和存取的权限, 16位整形数
    nlink_t        st_nlink;     	//连到该文件的硬连接数目,刚建立的文件值为1
    uid_t           st_uid;       	//用户ID
    gid_t           st_gid;       	//组ID
    dev_t          st_rdev;      	//(设备类型)若此文件为设备文件,则为其设备编号
    off_t            st_size;      	//文件字节数(文件大小)
    blksize_t     st_blksize;   	//块大小(文件系统的I/O 缓冲区大小)
    blkcnt_t      st_blocks;    	//块数
    time_t         st_atime;     	//最后一次访问时间
    time_t         st_mtime;     	//最后一次修改时间
    time_t         st_ctime;     	//最后一次改变时间(指属性)
};
// 摘自man手册 fstat()
// st_mode的各个标志位

The following mask values are defined for the file mode component of the st_mode field:

           S_ISUID     04000   set-user-ID bit
           S_ISGID     02000   set-group-ID bit (see below)
           S_ISVTX     01000   sticky bit (see below)

           S_IRWXU     00700   owner has read, write, and execute permission
           S_IRUSR     00400   owner has read permission
           S_IWUSR     00200   owner has write permission
           S_IXUSR     00100   owner has execute permission

           S_IRWXG     00070   group has read, write, and execute permission
           S_IRGRP     00040   group has read permission
           S_IWGRP     00020   group has write permission
           S_IXGRP     00010   group has execute permission

           S_IRWXO     00007   others  (not  in group) have read, write, and
                               execute permission
           S_IROTH     00004   others have read permission
           S_IWOTH     00002   others have write permission
           S_IXOTH     00001   others have execute permission

关于变量 st_mode: 
	- st_mode -- 16位整数
		○ 0-2 bit -- 其他人权限
			- S_IROTH    00004  读权限
			- S_IWOTH    00002  写权限
			- S_IXOTH    00001  执行权限
			- S_IRWXO    00007  掩码, 过滤 st_mode中除其他人权限以外的信息
		○ 3-5 bit -- 所属组权限
			- S_IRGRP    00040  读权限
			- S_IWGRP    00020  写权限
			- S_IXGRP    00010  执行权限
			- S_IRWXG    00070  掩码, 过滤 st_mode中除所属组权限以外的信息
		○ 6-8 bit -- 文件所有者权限
			- S_IRUSR    00400    读权限
			- S_IWUSR    00200    写权限
			- S_IXUSR    00100    执行权限
			- S_IRWXU    00700    掩码, 过滤 st_mode中除文件所有者权限以外的信息
		○ 12-15 bit -- 文件类型
			- S_IFSOCK   0140000 套接字
			- S_IFLNK    0120000 符号链接(软链接)
			- S_IFREG    0100000 普通文件
			- S_IFBLK    0060000 块设备
			- S_IFDIR    0040000 目录
			- S_IFCHR    0020000 字符设备
			- S_IFIFO    0010000 管道
			- S_IFMT     0170000 掩码,过滤 st_mode中除文件类型以外的信息
          
        
           S_IFMT     0170000   bit mask for the file type bit field

           S_IFSOCK   0140000   socket
           S_IFLNK    0120000   symbolic link
           S_IFREG    0100000   regular file
           S_IFBLK    0060000   block device
           S_IFDIR    0040000   directory
           S_IFCHR    0020000   character device
           S_IFIFO    0010000   FIFO
// 判断文件所有者对文件有没有执行权限
if(S_IXUSR & stat.st_mode)
{
    
    
    
}

if((stat.st_mode & S_IFMT) ==  S_IFREG)
{
    
    
   
}
// 调用
struct stat st;
stat(filePathName, &st);
if (S_ISREG(st.st_mode)) 
{
    
    
	/* Handle regular file */
}

// 判断文件类型的宏 m == 结构体中的变量st_mode
S_ISREG(st.st_mode)  is it a regular file?
S_ISDIR(st.st_mode)  directory?
S_ISCHR(st.st_mode)  character device?
S_ISBLK(st.st_mode)  block device?
S_ISFIFO(st.st_mode) FIFO (named pipe)?
S_ISLNK(st.st_mode)  symbolic link?  (Not in POSIX.1-1996.)
S_ISSOCK(st.st_mode) socket?  (Not in POSIX.1-1996.)

man文档的stat()示例1

 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>

 struct stat buffer;
 int         status;
 ...
 status = stat("/home/cnd/mod1", &buffer);
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <langinfo.h>
#include <stdio.h>
#include <stdint.h>

struct dirent *dp;
struct stat statbuf;
struct passwd *pwd;
struct group *grp;
struct tm *tm;
char datestring[256];
//...

/* Loop through directory entries. */
while ((dp = readdir(dir)) != NULL)
{
    
    

    /* Get entry's information. */
    if (stat(dp->d_name, &statbuf) == -1)
        continue;

    /* Print out type, permissions, and number of links. */
    printf("%10.10s", sperm(statbuf.st_mode));
    printf("%4d", statbuf.st_nlink);

    /* Print out owner's name if it is found using getpwuid(). */
    if ((pwd = getpwuid(statbuf.st_uid)) != NULL)
        printf(" %-8.8s", pwd->pw_name);
    else
        printf(" %-8d", statbuf.st_uid);

    /* Print out group name if it is found using getgrgid(). */
    if ((grp = getgrgid(statbuf.st_gid)) != NULL)
        printf(" %-8.8s", grp->gr_name);
    else
        printf(" %-8d", statbuf.st_gid);

    /* Print size of file. */
    printf(" %9jd", (intmax_t)statbuf.st_size);

    tm = localtime(&statbuf.st_mtime);

    /* Get localized date string. */
    strftime(datestring, sizeof(datestring), nl_langinfo(D_T_FMT), tm);

    printf(" %s %s\n", datestring, dp->d_name);
}

man文档的fstat()示例2

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysmacros.h>

int main(int argc, char *argv[])
{
    
    
    struct stat sb;

    if (argc != 2)
    {
    
    
        fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (stat(argv[1], &sb) == -1)
    {
    
    
        perror("stat");
        exit(EXIT_FAILURE);
    }
    
    // st_dev This field describes the device on which this file resides.  
    // (The major(3) and minor(3) macros may be useful to decompose the device ID in this field.)
    printf("ID of containing device:  [%lx,%lx]\n",
           (long)major(sb.st_dev), (long)minor(sb.st_dev));
    
    printf("File type:                ");

    switch (sb.st_mode & S_IFMT)
    {
    
    
    case S_IFBLK:
        printf("block device\n");
        break;
    case S_IFCHR:
        printf("character device\n");
        break;
    case S_IFDIR:
        printf("directory\n");
        break;
    case S_IFIFO:
        printf("FIFO/pipe\n");
        break;
    case S_IFLNK:
        printf("symlink\n");
        break;
    case S_IFREG:
        printf("regular file\n");
        break;
    case S_IFSOCK:
        printf("socket\n");
        break;
    default:
        printf("unknown?\n");
        break;
    }

    printf("I-node number:            %ld\n", (long)sb.st_ino);

    printf("Mode:                     %lo (octal)\n",
           (unsigned long)sb.st_mode);

    printf("Link count:               %ld\n", (long)sb.st_nlink);
    printf("Ownership:                UID=%ld   GID=%ld\n",
           (long)sb.st_uid, (long)sb.st_gid);

    printf("Preferred I/O block size: %ld bytes\n",
           (long)sb.st_blksize);
    printf("File size:                %lld bytes\n",
           (long long)sb.st_size);
    printf("Blocks allocated:         %lld\n",
           (long long)sb.st_blocks);
    printf("Last status change:       %s", ctime(&sb.st_ctime));
    printf("Last file access:         %s", ctime(&sb.st_atime));
    printf("Last file modification:   %s", ctime(&sb.st_mtime));

    exit(EXIT_SUCCESS);
}


//  [root@lwh test]# ./execute  test2.cpp 
//  ID of containing device:  [fd,0]
//  File type:                regular file
//  I-node number:            53108702
//  Mode:                     100644 (octal)
//  Link count:               1
//  Ownership:                UID=0   GID=0
//  Preferred I/O block size: 4096 bytes
//  File size:                1931 bytes
//  Blocks allocated:         8
//  Last status change:       Sun Jul 19 12:20:06 2020
//  Last file access:         Sun Jul 19 12:20:16 2020
//  Last file modification:   Sun Jul 19 12:20:06 2020
//  [root@lwh test]# 

stat() Demo

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


int main(int argc, char* argv[])// a.out fileName
{
    
    
    if(argc < 2)
    {
    
    
        printf("a.out filename\n");
        exit(1);
    }
    
    struct stat buf_st;
    int ret = lstat(argv[1], &buf_st);
    if(ret == -1)
    {
    
    
        perror("stat");
        exit(1);
    }

    printf("file size = %d\n", (int)buf_st.st_size);

    return 0;
}

2.文件属性函数

1.access()

int access(const char *pathname, int mode);

// 判断文件权限, 或者文件是否存在
int access(const char *pathname, int mode);
	参数:
		- pathname: 文件名
		- mode: 
			R_OK: 判断文件是不是有读权限
			W_OK: 判断文件是不是有写权限
			X_OK: 判断文件是不是有执行权限
			F_OK: 判断当前文件是否存在
	返回值:
		判断成功: 0, 
		失败: -1
        文件有判断的权限:0
        文件没有判断的权限:-1

access() Demo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    
    
    if (argc < 2)
    {
    
    
        printf("%s argv err \n", argv[0]);
        exit(1);
    }
    int ret = 0;
    ret = access(argv[1], W_OK);
    if (ret == -1)
    {
    
    
        perror("access");
        exit(1);
    }
    if (ret == 0)
    {
    
    
        printf("you can write this file.\n");
    }
    return 0;
}

2.chmod() chown()

int chmod(const char *filename, int mode);
// 修改文件权限
// chmod 0777 a.txt
int chmod(const char *filename, int mode);
	参数:
		- filename: 要修改文件权限的文件的名字
		- mod: 八进制数
int chown(const char *path, uid_t owner, gid_t group);
// 修改文件所有者
int chown(const char *path, uid_t owner, gid_t group);
	参数:
		- path: 要修改的文件名
		- owner: 用户ID, stat 	/etc/passwd
		- group: 组ID         	/etc/group

chmod() chown() Demo

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char *argv[]) // a.out filename mod
{
    
    
    if (argc < 3)
    {
    
    
        printf("%s argv err \n", argv[0]);
        exit(1);
    }
    int mode = strtol(argv[2], NULL, 8);
    int ret = chmod(argv[1], mode);
    if (ret == -1)
    {
    
    
        perror("chmod");
        exit(1);
    }

    ret = chown(argv[1], 1001, 1002);
    if (ret == -1)
    {
    
    
        perror("chown");
        exit(1);
    }
    return 0;
}

3.truncate()

int truncate(const char *path, off_t length);

// 修改文件大小
int truncate(const char *path, off_t length);
	参数: 
		- path: 要操作的文件
		- length: 最终的文件大小
			- length > 源文件大小   ==> 文件拓展, 添加字符0
			- length < 源文件大小   ==> 文件被裁剪, 尾部的被删除

truncate() Demo

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

int main(int argc, char *argv[]) // a.out truncateFileName length
{
    
    
    if (argc < 3)
    {
    
    
        printf("a.out filename size\n");
        exit(1);
    }

    long int len = strtol(argv[2], NULL, 10);
    int aa = truncate(argv[1], len);
    if (aa == -1)
    {
    
    
        perror("truncate");
        exit(1);
    }
    return 0;
}

strtol()

long int strtol(const char *str, char **endptr, int base)
//参数
//	str -- 要转换为长整数的字符串。
//	endptr -- 对类型为 char* 的对象的引用,其值由函数设置为 str 中数值后的下一个字符。
//	base -- 基数,必须介于 2 和 36(包含)之间,或者是特殊值 0。

//功能说明
//	把str中的数字提取到返回值中
//	把str中数字之后的字符串提取到endptr中
//	base代表了进制
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    
    
    if (argc < 2)
    {
    
    
        printf("./a.out number: ");
        exit(1);
    }

    char *pt;
    int number = strtol(argv[1], &pt, 16);
    printf("number = %x\n", number);
    printf("pt = %s\n", pt);
    return 0;
}

//  [root@lwh test]# ./execute 16834Helloworld
//  number = 16834
//  pt = Helloworld
//  [root@lwh test]# 

ls -l fileName 的C实现

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

int main(int argc, char *argv[])
{
    
    
    if (argc < 2)
    {
    
    
        printf("./a.out filename\n");
        exit(1);
    }

    struct stat st;
    int ret = stat(argv[1], &st);
    if (ret == -1)
    {
    
    
        perror("stat");
        exit(1);
    }

    // 存储文件类型和访问权限
    char perms[11] = {
    
    0};
    // 判断文件类型
    switch (st.st_mode & S_IFMT)
    {
    
    
    case S_IFLNK:
        perms[0] = 'l';
        break;
    case S_IFDIR:
        perms[0] = 'd';
        break;
    case S_IFREG:
        perms[0] = '-';
        break;
    case S_IFBLK:
        perms[0] = 'b';
        break;
    case S_IFCHR:
        perms[0] = 'c';
        break;
    case S_IFSOCK:
        perms[0] = 's';
        break;
    case S_IFIFO:
        perms[0] = 'p';
        break;
    default:
        perms[0] = '?';
        break;
    }
    // 判断文件的访问权限
    // 文件所有者
    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 *fileGrp = getgrgid(st.st_gid)->gr_name;
    // 文件大小
    int fileSize = (int)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  %d  %s  %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);

    printf("%s\n", buf);

    return 0;
}

3.目录操作函数

1.rename()

int rename(const char *oldpath, const char *newpath);
// 文件重命名
int rename(const char *oldpath, const char *newpath);
	参数: 
		- oldpath: 旧的文件名
		- newpath: 新的文件名

rename() Demo

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) // a.out  fileName NewFileName
{
    
    
    if (argc < 3)
    {
    
    
        printf("a.out oldName newName\n");
        exit(1);
    }

    int ret = rename(argv[1], argv[2]);
    if (ret == -1)
    {
    
    
        perror("rename");
        exit(1);
    }
    return 0;
}

2.chdir() getcwd()

int chdir(const char *path);
// 修改进程的工作目录
// 在 /home/itcat启动a.out, a.out的工作目录 /home/itcast
// chdir("/") -> 进程切换到了根目录
int chdir(const char *path);
char *getcwd(char *buf, size_t size);
// == pwd命令
char *getcwd(char *buf, size_t size);
//	参数: 
//		- buf: 存储路径, 指向一个由内存大小的数组
//		- size: 修饰buf大小
//	返回值:
//		指针指向一块内存, 这个内存就是第一个参数

chdir() getcwd() Demo

#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
    
    
    if (argc < 2)
    {
    
    
        printf("a.out dir: \n");
        exit(1);
    }

    int ret = chdir(argv[1]);
    if (ret == -1)
    {
    
    
        perror("chdir");
        exit(1);
    }

    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0777);
    if (fd == -1)
    {
    
    
        perror("open");
        exit(1);
    }
    close(fd);

    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("current dir: %s\n", buf);

    return 0;
}
[root@lwh test]# ./execute  /home/lwh/Desktop/study/test/testshell/
current dir: /home/lwh/Desktop/study/test/testshell
[root@lwh test]# 

3.mkdir()

int mkdir(const char *pathname, mode_t mode);
// 创建目录
// 如果创建目录, 这个目录必须要有执行权限
int mkdir(const char *pathname, mode_t mode);
	参数:
		- pathname: 目录名
		- mode: 目录的权限 mode & ~umask

mkdir() Demo

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    
    
    if (argc < 3)
    {
    
    
        printf("a.out newDir mode\n");
        exit(1);
    }

    int mode = strtol(argv[2], NULL, 8);
    int ret = mkdir(argv[1], mode);
    if (ret == -1)
    {
    
    
        perror("mkdir");
        exit(1);
    }
    return 0;
}

4.rmdir()

int rmdir(const char *pathname);
// == rmdir命令, 只能删除空目录 rm -r
int rmdir(const char *pathname);

5.自动清除缓存文件

// 删除磁盘文件
// 应用场景举例:自动清除缓存
#include <unistd.h>
int unlink(const char *pathname);
int fd = open("hello.txt", o_rdwr);//举例:假如: 有一个文件hello.txt
unlink("hello.txt");// 这个时候无法删除;当文件被close的时候, 文件才被删除
......;
.......;
close(fd);// 文件才被删除

6.目录遍历函数

1.opendir()

// 打开目录
DIR *opendir(const char *name);
	参数: 
		- name: 要打开的目录
	返回值: 
		成功: 有效指针, 失败: NULL
opendir() Demo
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>

int main(int argc, char *argv[])
{
    
    
    if (argc < 2)
    {
    
    
        printf("a.out path\n");
        exit(1);
    }

    DIR *dir = opendir(argv[1]);
    if (dir == NULL)
    {
    
    
        perror("opendir");
        exit(1);
    }
    char buf[512];
    getcwd(buf, sizeof(buf));
    printf("current dir: %s\n", buf);

    closedir(dir);

    return 0;
}

2.readdir()

// 读目录, 参数opendir的返回值
// 因为一个目录中还有目录, 读目录是递归操作
// 读当前目录: 文件是有若干个的, 调用一次readdir只能读一个文件信息, 读所有while()
struct dirent *readdir(DIR *dirp);
// 返回一个结构体, 这个对应一个文件
struct dirent
{
    
    
    ino_t d_ino;               // 此目录进入点的inode
    ff_t d_off;                // 目录文件开头至此目录进入点的位移
    signed short int d_reclen; // d_name 的长度, 不包含NULL 字符
    unsigned char d_type;      // d_name 所指的文件类型 
    har d_name[256];	       // 文件名
};
d_type
	DT_BLK 	- 块设备
	DT_CHR 	- 字符设备
	DT_DIR 	- 目录
	DT_LNK 	- 软连接
	DT_FIFO - 管道
	DT_REG 	- 普通文件
	DT_SOCK - 套接字
	DT_UNKNOWN - 未知

3.closedir()

// 关闭目录, 参数就是opendir的返回值
int closedir(DIR *dirp);

读某个目录下普通文件的个数

// 读某个目录下普通文件的个数
// 需要用到递归, 想明白递归结束的条件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>

int get_file_num(char *curPath)
{
    
    
  int total = 0;
  DIR *dir = NULL;

  // 打开目录
  // 循环从目录中读文件
  dir = opendir(curPath);
  if (dir == NULL)
  {
    
    
      perror("opendir");
      return 0;
  }

  // 存储下一级目录(绝对路径)
  char path[1024] = {
    
    0}; //

  // 定义readdir()返回的结构体指针
  // 通过该指针,可得到文件的文件名
  struct dirent *ptr = NULL;

  while ((ptr = readdir(dir)) != NULL)
  {
    
    
      // 跳过目录. 和 目录..
      if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)
      {
    
    
          continue;
      }

      // 是目录的话,递归读取
      if (ptr->d_type == DT_DIR)
      {
    
    
          // 拼接下一级目录(绝对路径)
          sprintf(path, "%s/%s", curPath, ptr->d_name);
          // 递归读目录
          total += get_file_num(path);
      }

      // 如果是普通文件
      if (ptr->d_type == DT_REG)
      {
    
    
          total++;
      }
  }
  closedir(dir);
  return total;
}

int main(int argc, char *argv[])
{
    
    
  if (argc < 2)
  {
    
    
      printf("./a.out path");
      exit(1);
  }

  int total = get_file_num(argv[1]);
  printf("%s has regfile number: %d\n", argv[1], total);
  return 0;
}

思考:组合readdir和ls -l

猜你喜欢

转载自blog.csdn.net/liangwenhao1108/article/details/107445422
今日推荐