5、标准C库IO和Linux系统IO函数、虚拟地址空间、系统IO文件/目录操作

1. 标准C库IO函数

在这里插入图片描述

  • 对进行磁盘读写时,标准C库IO函数得到的缓冲区可以提高效率,降低写磁盘的次数;
  • 当使用网络通信时,直接调用Linux系统的IO函数,提高响应速度,及时性

2. 标准C库IO和Linux系统IO的关系(调用和被调用)

  • 标准C库IO设立缓冲区,当缓冲区满或者人为刷新时再调用内核 IO
  • 内核IO直接对磁盘进行操作。
    在这里插入图片描述

3. 虚拟地址空间

早期的计算机运行方式是直接将程序运行在物理内存上。这就存在三个问题:

问题1. 地址空间不隔离
– 所有进程都直接访问物理地址,程序使用的物理空间不是隔离的
问题2. 运行效率低
– 监控程序直接将整个程序装入内存进行执行
问题3. 运行地址不确定
– 因为每次执行装入的物理地址是不确定的

为了解决以上三个问题,增加了一个中间层,用来解决上面的问题。
那就是给出一个虚拟地址,然后通过一种映射关系,将其与物理地址进行映射,将虚拟地址转换成物理地址
这样就能解决存在的问题1和问题3,问题2需要页映射来解决。

虚拟内存,与进程相关。

使用虚拟内存不用将进程全部内容加载到主存上(局部性立了大功——进程在某段时间只需要用到部分代码及其数据),虚拟内存与主存通过不断的数据交换,只需要占用满足程序运行最小空间的内存即可完成程序运行。

不光如此,虚拟内存为程序分配同样的空间(大小取决与CPU位数)便于管理(意味着进程自认为占用了整个内存)。

同时,虚拟内存为每个进程增添地址空间的安全性,保护其地址空间不被其他进程所破坏。

  • 解决内存加载时产生的一些问题
  • 解释 栈、堆 等模型
  • 被内存管理单元(MMU)映射到真实物理地址上(虚拟内存管理)

在这里插入图片描述

内核空间:内核是操作系统总是驻留在存储器中的部分
	应用程序不允许读写这个区域的内容或者直接调用内核代码定义的函数
	
栈:临时变量,返回值等,向低地址扩展
	用户栈在程序执行期间可以动态地扩展和收缩
	调用函数时,栈就会增长每次我们从函数返回时,栈就会收缩
	
共享库:存放像C标准库和数学库这样共享库的代码和数据的区域

堆:动态分配的内存,向高地址扩展
	调用像malc和free这样的C标准库函数的结果,堆可以在运态地扩展和收缩
	
bss段数据:即未初始化全局变量的内存映射

数据段(只读数据段):即可执行文件的已初始化全局变量的内存映射

代码段:即可执行文件二进制代码的内存映射(可执行文件映像)

保留区域

4. 文件描述符

Linux中设备都被虚拟成文件进行管理

文件描述符用于指向操作文件,多个文件描述符可以指向同一个文件

在这里插入图片描述

5. Linux系统IO函数

Linux中 inode 的理解
什么是软链接、硬链接
硬链接 - 百度百科
基于 C 语言 LINUX 的文件操作

int open(const char *pathname, int flags);int open(const char *pathname, int flags, mode_t mode);int close(int fd);ssize_t read(int fd, void *buf, size_t count);ssize_t write(int fd, const void *buf, size_t count);off_t lseek(int fd, off_t offset, int whence);int stat(const char *pathname, struct stat *statbuf);int lstat(const char *pathname, struct stat *statbuf);

open() 函数中的 flags 参数(文件打开模式)

打开模式含义
O_CREAT必要时创建文件
O_TRUNC删除全部现有数据
O_APPEND维持现有数据,保存到其后面
O_RDONLY只读打开
O_WRONLY只写打开
O_RDWR读写打开

5.1 open() 打开文件

终端输入 man 2 open 查看 open() 函数手册;(2代表 Linux 系统函数API,3 代表标准C库函数API文档)

在这里插入图片描述

// 打开一个已经存在的文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
    参数:
        - pathname:要打开的文件路径
        - flags:对文件的操作权限设置还有其他的设置
          O_RDONLY,  O_WRONLY,  O_RDWR  这三个设置是互斥的
    返回值:返回一个新的文件描述符,如果调用失败,返回-1

// 错误号
errno:属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号。

// 打印errno对应的错误描述
#include <stdio.h>
void perror(const char *s); 
    s参数:用户描述,比如hello,最终输出的内容是  hello:xxx(实际的错误描述)

// 关闭文件
#include <unistd.h>
int close(int fd);
#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);

    // 打开失败,错误号赋值给errno,用perror()查看具体错误
    if(fd == -1) {			// 文件描述符 (fd, file descriptor)
        perror("open");    	// 参数由用户进行描述
    }
    
    // 读写操作...

    // 关闭
    close(fd);

    return 0;
}

不存在 a.txt 文件,打开失败,提示如下:

open: No such file or directory

5.2 open()创建新文件

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags, mode_t mode);
    参数:
        - pathname:要创建的文件的路径
        - flags:【程序对文件的操作权限】和其他的设置
            - 必选项:O_RDONLY,  O_WRONLY, O_RDWR  这三个之间是互斥的
            - 可选项:O_CREAT 文件不存在,创建新文件
			flags参数是一个int类型的数据,占4个字节,32位。
    		flags 32个位,每一位就是一个标志位,不同标记间用 或| 结合。
        - mode:八进制的数,表示创建出的新的【文件本身的操作权限】,比如:0775(0开头表示8进制)
		
			三个对象   	当前用户 |  同组用户  |	其他组	|
			权限			r w x 	|	r w x 	|	r w x 	|	读-写-执行
			二进制		1 1 1	|	1 1 1	|	1 1 1	|	0 或 1
						-----	|	-----	|	-----	|
			八进制	  	  7		|	  7		|	  7		|	
			
            最终的权限是:mode & ~umask(掩码值,不同用户umask不一样,也可以更改)
	            0777   ->   111111111		
	        &   0775   ->   111111101  (~ 002)
	        ----------------------------
	                        111111101	( rwxrwxr-x 其他组用户没有写权限)
	        按位与:0和任何数都为0
	        umask的作用就是抹去某些权限。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main() {

    // 创建一个新的文件
    int fd = open("create.txt", O_RDWR | O_CREAT, 0777);

    if(fd == -1) {
        perror("open");
    }

    // 关闭
    close(fd);

    return 0;
}
cyf@cyf-virtual-machine:~/Linux/test09$ umask
0002
cyf@cyf-virtual-machine:~/Linux/test09$ ll create.txt
-rwxrwxr-x 1 cyf cyf 0 527 12:11 create.txt*

5.3 read() / write() 读写文件

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
    参数:
        - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
        - buf:需要读取数据存放的地方,数组的地址(传出参数)
        - count:指定的数组的大小
    返回值:
        - 成功:(偏移量移动已读大小)
            >0: 返回实际的读取到的字节数
            =0:文件已经读取完了
        - 失败:-1 ,并且设置errno,使用 perror("  "); 查看错误原因

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
    参数:
        - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
        - buf:要往磁盘写入的数据,数据
        - count:要写的数据的实际的大小
    返回值:
        成功:实际写入的字节数
        失败:返回-1,并设置errno,使用 perror("  "); 查看错误原因
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {

    // 1.通过open打开english.txt文件
    int srcfd = open("english.txt", O_RDONLY);
    if(srcfd == -1) {
        perror("open");
        return -1;
    }

    // 2.创建一个新的文件(拷贝文件)
    int newfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
    if(newfd == -1) {
        perror("open");
        return -1;
    }

    // 3.频繁的读写操作
    char buf[1024] = {0};
    int len = 0;
    while((len = read(srcfd, buf, sizeof(buf))) > 0) {
       //  write(newfd, buf, len);
        if(write(newfd, buf, len) == -1) {
            perror("write");
            return -1;
        }
    }

    // 4.关闭文件
    close(newfd);
    close(srcfd);
    return 0;
}

5.4 lseek() 偏移量(光标)操作

标准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);	// whence + offset
    参数:
        - fd:文件描述符,通过open得到的,通过这个fd操作某个文件
        - offset:偏移量
        - whence:
            SEEK_SET
                设置文件指针的偏移量
            SEEK_CUR
                设置偏移量:当前位置 + 第二个参数offset的值
            SEEK_END
                设置偏移量:文件大小 + 第二个参数offset的值
    返回值:返回文件指针的位置


作用:
    1.移动文件指针到文件头
    lseek(fd, 0, SEEK_SET);

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

    3.获取文件长度
    lseek(fd, 0, SEEK_END);

    4.拓展文件的长度,当前文件10b, 110b, 增加了100个字节
    lseek(fd, 100, SEEK_END)
    注意:需要写一次数据
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> // malloc

int main() {

    int fd = open("hello.txt", O_RDWR | O_CREAT | O_TRUNC, 0777);      // 读写 | 必要时创建 | 清除数据   
    if(fd == -1) {
        perror("open");
        return -1;
    }
    write(fd, "0123456789", 10);            // 0123456789
    

    // 设置光标位置
	int pos = lseek(fd, 4, SEEK_SET);       // 0123
	printf("zeroPos: %d\n", pos);           // 4
    // 获取光标位置
    int currPos = lseek(fd, 0, SEEK_CUR);
    printf("currPos: %d\n", currPos);

    // 读取文件数据(光标随之移动)
	char buff[4] = {0};
	read(fd, buff, sizeof(buff));           // 01234567
    printf("read: %s\n", buff);             // 4567
    currPos = lseek(fd, 0, SEEK_CUR);       // 4 + 4
    printf("currPos: %d\n", currPos);

    // 从当前光标出写数据,并覆盖原有数据
    write(fd, "abcde", 5);                  // 01234567abcde
    currPos = lseek(fd, 0, SEEK_CUR);       // 8 + 5
    printf("currPos: %d\n", currPos);

    // 扩展文件的长度,空字符填充
    int ret = lseek(fd, 9, SEEK_END);      // 01234567abcde---------
    if(ret == -1) {
        perror("lseek");
        return -1;
    }

    // 需要在扩展的长度中或者之后写数据才算扩展完成
    lseek(fd, -9, SEEK_CUR);
    write(fd, "123456789", 9);            // 01234567abcde123456789

    // 写入一个数据
    write(fd, "o", 1);                    // 01234567abcde123456789o

    // 移动到起始位置,读取整个文件
    int len = lseek(fd, 0, SEEK_END);  
    printf("len: %d\n", len); 
    lseek(fd, 0, SEEK_SET);              
    char* buf = (char*)malloc(len); 
    read(fd, buf, len);
    printf("read: %s\n", buf);
    free(buf);	// 释放内存

    // 关闭文件
    close(fd);
    return 0;
}

5.5 stat() / lstat() 获取文件信息

返回文件的相关信息(stat 结构体)

在这里插入图片描述
在这里插入图片描述

#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);  
    参数:
        - pathname:操作的文件的路径
        - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
    返回值:
        成功:返回0
        失败:返回-1 设置errno
  • 软链接:

建立新文件,类同与windos的快捷方式,给文件创建一个快速的访问路径,它依赖于原文件,与普通文件没什么不同,iode都指向同一个文件在硬盘中的区块。当原文件出现问题后,该链接不可用。

  • 硬链接

硬链接一旦建立,源文件和链接文件的任意一方修改则会同步修改。本质上是同一个文件,不建立新的文件。

在这里插入图片描述

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

int main() {
    struct stat statbuf;
    struct stat lstatbuf;
    int ret = stat("b.txt", &statbuf);      // 被指向文件
    int lret = lstat("b.txt", &lstatbuf);   // 软链接的文件本身
    if(ret == -1) {
        perror("stat");
        return -1;
    }

    printf("size: %ld\n", statbuf.st_size);
    printf("lsize: %ld\n", lstatbuf.st_size);
    return 0;
}

size: 10
lsize: 5

利用 stat 模仿 ls -l file 功能

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>    // getpwuid()
#include <grp.h>    // getgrgid()
#include <time.h>   // ctime()
#include <string.h> // strncpy(), strlen()

// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
int main(int argc, char * argv[]) {

    // 判断输入的参数是否正确
    if(argc < 2) {      // argc 形参个数
        printf("%s filename\n", argv[0]);   // argv[0]为当前程序名称
        return -1;
    }

    // 通过stat函数获取用户传入的文件的信息
    struct stat st;
    int ret = stat(argv[1], &st);       // argv[1] 为真正传递的参数
    if(ret == -1) {
        perror("stat");
        return -1;
    }

    
    char perms[11] = {0};       // -rw-rw-r-- 10个字符 + 1个空字符 表字符串, 用于保存文件类型和文件权限的字符

    // 获取文件类型 st_mode 二进制前四位
    switch(st.st_mode & S_IFMT) {   // st.st_mode & 1111 000 000 000 000
        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;
    }

    // 判断文件的访问权限   st_mode 二进制后九位
    // 文件所有者
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';  // 按位与,若某一位为1 则为非0
    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; // ID -> 结构体 -> 用户名

    // 文件所在组
    char * fileGrp = getgrgid(st.st_gid)->gr_name;  // ID -> 结构体 -> 组名

    // 文件大小
    long int fileSize = st.st_size;

    // 获取修改的时间
    char * time = ctime(&st.st_mtime);      // st_mtime 为1970.1.1 0时到目前的秒数

    // 以上ctime转换后 time 自带 回车换行,此处减去末尾 回车换行符
    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, fileGrp, fileSize, mtime, argv[1]);

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

    return 0;
}

5.6 文件属性操作函数

◼ int access(const char *pathname, int mode);		// 判断文件权限 或 是否存在
◼ int chmod(const char *filename, int mode);		// 修改文件权限 change mode
◼ int chown(const char *path, uid_t owner, gid_t group); // 修改文件所有者和所在组 change owner
◼ int truncate(const char *path, off_t length);		// 缩减 或者 扩展文件大小
  • access.c
/*
    #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");
    }

    ret = access("a.txt", W_OK);
    if(ret == -1) {
        perror("access");
    }else{
        printf("文件可写!!!\n");
    }

    ret = access("a.txt", X_OK);
    if(ret == -1) {
        perror("access");
    }else{
        printf("可执行!!!\n");
    }
    return 0;
}
  • chmod.c
/*
    #include <sys/stat.h>
    int chmod(const char *pathname, mode_t mode);
        修改文件的权限
        参数:
            - pathname: 需要修改的文件的路径
            - mode:需要修改的权限值,八进制的数 或 宏值
        返回值:成功返回0,失败返回-1
*/
#include <sys/stat.h>
#include <stdio.h>
int main() {
    int ret = chmod("a.txt", 0775);
    if(ret == -1) {
        perror("chmod");
        return -1;
    }
    return 0;
}
  • truncate.c
/*
    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char *path, off_t length);
        作用:缩减或者扩展文件的尺寸至指定的大小
        参数:
            - path: 需要修改的文件的路径
            - length: 需要最终文件变成的大小
        返回值:
            成功返回0, 失败返回-1
*/

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main() {
    int ret = truncate("b.txt", 5);
    if(ret == -1) {
        perror("truncate");
        return -1;
    }
    return 0;
}

5.7 目录操作函数

◼ int mkdir(const char *pathname, mode_t mode);		// 创建目录
◼ int rmdir(const char *pathname);					// 删除【空目录】
◼ int rename(const char *oldpath, const char *newpath);	// 重命名
◼ int chdir(const char *path);						// 更改进程的工作目录
◼ char *getcwd(char *buf, size_t size);				// 获取当前工作目录
  • mkdir.c
/*
    #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 <sys/types.h>
#include <stdio.h>
int main() {
    int ret = mkdir("aaa", 0777);
    if(ret == -1) {
        perror("mkdir");
        return -1;
    }
    return 0;
}
  • rename.c
/*
    #include <stdio.h>
    int rename(const char *oldpath, const char *newpath);

*/
#include <stdio.h>
int main() {
    int ret = rename("aaa", "bbb");
    if(ret == -1) {
        perror("rename");
        return -1;
    }
    return 0;
}
  • chdir(), getcwd()
/*
    #include <unistd.h>
    int chdir(const char *path);
        作用:修改进程的工作目录
            比如在/home/nowcoder 启动了一个可执行程序a.out, 进程的工作目录 /home/nowcoder
        参数:
            path : 需要修改的工作目录

    #include <unistd.h>
    char *getcwd(char *buf, size_t size);
        作用:获取当前工作目录
        参数:
            - buf : 存储的路径,指向的是一个数组(传出参数)
            - size: 数组的大小
        返回值:
            返回的指向的一块内存,这个数据就是第一个参数
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main() {

    // 获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf);  // /home/cyf/Linux/test14

    // 修改工作目录
    int ret = chdir("/home/cyf/Linux/test14/bbb");
    if(ret == -1) {
        perror("chdir");
        return -1;
    } 

    // 创建一个新的文件
    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
    if(fd == -1) {
        perror("open");
        return -1;
    }

    close(fd);

    // 获取当前的工作目录
    char buf1[128];
    getcwd(buf1, sizeof(buf1));
    printf("当前的工作目录是:%s\n", buf1); // /home/cyf/Linux/test14/bbb
    
    return 0;
}

5.8 目录遍历函数

◼ DIR *opendir(const char *name);		// 打开一个目录,返回目录流
◼ struct dirent *readdir(DIR *dirp);	// 读取目录数据,目录流往后移
◼ int closedir(DIR *dirp);				// 关闭目录

在这里插入图片描述

  • 获取某个文件夹下所有普通文件的个数(递归子文件夹)
/*
    // 打开一个目录
    #include <sys/types.h>
    #include <dirent.h>
    DIR *opendir(const char *name);
        参数:
            - name: 需要打开的目录的名称
        返回值:
            DIR * 类型,理解为目录流
            错误返回NULL

    // 读取目录中的数据,目录流对应往后移
    #include <dirent.h>
    struct dirent *readdir(DIR *dirp);
        - 参数:dirp是opendir返回的结果
        - 返回值:
            struct dirent,代表读取到的文件的信息
            读取到了末尾或者失败了,返回NULL

    // 关闭目录
    #include <sys/types.h>
    #include <dirent.h>
    int closedir(DIR *dirp);
*/
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getFileNum(const char * path);

// 读取某个目录下所有的普通文件的个数
int main(int argc, char * argv[]) {
    if(argc < 2) {
        printf("%s path\n", argv[0]);
        return -1;
    }
    int num = getFileNum(argv[1]);
    printf("普通文件的个数为:%d\n", num);
    return 0;
}

// 用于获取目录下所有普通文件的个数
int getFileNum(const char * path) {

    // 1.打开目录
    DIR * dir = opendir(path);
    if(dir == NULL) {
        perror("opendir");
        exit(0);
    }

    struct dirent *ptr;
    // 记录普通文件的个数
    int total = 0;
    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 += getFileNum(newpath);       // 递归调用,获取子文件夹文件个数
        }

        if(ptr->d_type == DT_REG) {
            // 普通文件
            total++;
        }
    }

    // 关闭目录
    closedir(dir);
    return total;
}

5.9 复制、重定向文件描述符

◼ int dup(int oldfd);   			 复制文件描述符
◼ int dup2(int oldfd, int newfd);    重定向文件描述符
/*
    #include <unistd.h>
    int dup(int oldfd);
        作用:复制一个新的文件描述符
        fd=3, int fd1 = dup(fd),
        fd指向的是a.txt, fd1也是指向a.txt
        从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符
*/

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

int main() {
    int fd = open("a.txt", O_RDWR | O_CREAT, 0664);

    int fd1 = dup(fd);      // 复制描述符 指向同一个文件
    if(fd1 == -1) {
        perror("dup");
        return -1;
    }

    printf("fd : %d , fd1 : %d\n", fd, fd1);

    close(fd);              // 关闭原描述符

    char * str = "hello,world";
    int ret = write(fd1, str, strlen(str)); // 在复制的描述符上操作文件
    if(ret == -1) {
        perror("write");
        return -1;
    }
    close(fd1);
    return 0;
}
fd : 3 , fd1 : 4
/*
    #include <unistd.h>
    int dup2(int oldfd, int newfd);
        作用:重定向文件描述符
        oldfd 指向 a.txt, newfd 指向 b.txt
        调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
        oldfd 必须是一个有效的文件描述符
        oldfd和newfd值相同,相当于什么都没有做
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main() {

    int fd = open("1.txt", O_RDWR | O_CREAT, 0664);     // oldfd
    if(fd == -1) {
        perror("open");
        return -1;
    }

    int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);    // newfd
    if(fd1 == -1) {
        perror("open");
        return -1;
    }

    printf("fd : %d, fd1 : %d\n", fd, fd1);

    int fd2 = dup2(fd, fd1);    // fd1, fd2 都指向fd指向的同一个文件1.txt
    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;
}
fd : 3, fd1 : 4
fd : 3, fd1 : 4, fd2 : 4

5.10 fcntl()函数

◼ int fcntl(int fd, int cmd, ... /* arg */ );
	复制文件描述符
	设置/获取文件的状态标志

    #include <unistd.h>
    #include <fcntl.h>
    int fcntl(int fd, int cmd, ...);
	参数:
    fd : 表示需要操作的文件描述符
    cmd: 表示对文件描述符进行如何操作
        - F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
            int ret = fcntl(fd, F_DUPFD);

        - F_GETFL : 获取指定的文件描述符文件状态flag
          获取的flag和我们通过open函数传递的flag是一个东西。

        - F_SETFL : 设置文件描述符文件状态flag
          必选项:O_RDONLY, O_WRONLY, O_RDWR 【**不可以被修改**】
          可选性:O_APPEND, O_NONBLOCK
            O_APPEND 表示追加数据
            NONBLOK 设置成非阻塞(阻塞和非阻塞:描述的是函数调用的行为。)         
        ...   
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

int main() {

    // 1.复制文件描述符
    // int fd = open("1.txt", O_RDONLY);
    // int ret = fcntl(fd, F_DUPFD);

    // 2.修改或者获取文件状态flag
    int fd = open("1.txt", O_WRONLY);
    if(fd == -1) {
        perror("open");
        return -1;
    }

    // 获取文件描述符状态flag
    int flag = fcntl(fd, F_GETFL);
    if(flag == -1) {
        perror("fcntl");
        return -1;
    }
    flag |= O_APPEND;   // flag = flag | O_APPEND

    // 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
    int ret = fcntl(fd, F_SETFL, flag);
    if(ret == -1) {
        perror("fcntl");
        return -1;
    }

    char * str = "\nnihao";
    write(fd, str, strlen(str));

    close(fd);

    return 0;
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值