【项目一】IO库对比、文件操作

news2024/9/23 7:20:17

文章目录

    • 1.17 标准C库IO函数和Linux系统IO函数对比
    • 1.18 虚拟地址空间
    • 1.19 文件描述符
    • 1.20 open打开文件
    • 1.20 open创建文件
    • 1.22 read、write函数
    • 1.23 lseek函数
    • 1.24 stat、lstat函数
    • 1.25 使用stat函数模拟实现 ls -l 命令
    • 1.26 文件属性操作函数
    • 1.27 目录操作函数
    • 1.28 目录遍历函数
    • 1.29 dup、dup2函数
    • 1.30 fcntl函数


1.17 标准C库IO函数和Linux系统IO函数对比

注意!站在内存的角度去考虑输入和输出。
输入:从文件到内存
输出:从内存到文件

在这里插入图片描述标准C库是跨平台的,不同的操作系统会调用不同系统的API
缓冲区,默认8KB
磁盘读写,使用标准C库IO函数,提高系统效率;网络通信使用Linux系统IO,无缓冲。
man 3 fopen,查看文件函数具体明细
File是一个结构体,主要包含三大块内容,如图所示

在这里插入图片描述

1.18 虚拟地址空间

在这里插入图片描述虚拟地址空间不存在,解决程序加载内存出现的问题,以及解释程序中的堆栈模型等。进程与程序的区别:进程是运行的程序,加载到内存中;程序是磁盘上的代码。32位机器,虚拟地址2^32;64位机器,虚拟地址2的48次方。虚拟地址空间被MMU映射到物理内存。
使用系统调用,通过系统API对内核数据进行操作。

图上是32位的,4G

1.19 文件描述符

程序:是文件,不占用内存,只占用磁盘(test、test.c)
进程:把可执行程序启动起来,操作系统会为其分配资源,也就是进程。占用内存空间,具有虚拟地址空间

文件描述符:在进程内核区,内核其实就是一个程序,其中有一个结构体叫做PCB(Process Control Block),其中就管理着文件描述符表
在这里插入图片描述文件描述符表,是一个数组,可存储n个文件描述符。每个文件描述符都可定位到一个文件。一个进程可同时打开多个文件。默认大小是1024。Linux系统,一切皆文件。一个文件可被打开多次,每次打开的文件描述符不一样,如第一次打开文件进行读操作,第二次打开文件进行写操作。系统调用close可以释放掉文件描述符,使用open获取文件描述符。

1.20 open打开文件

在这里插入图片描述fopen会调用linux底层的open函数

使用man指令查看函数手册,linux系统API都在第二章,第三章是标准C库的API

man 2 open

注:
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
这里不是函数重载,因为是C语言,这里其实是可变参数

/*
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>//函数声明

    //打开一个已经存在的文件
    //flags定义为宏,放在前两个头文件中,方便在其他地方也能用
    int open(const char *pathname, int flags);
        1)参数:
            1、pathname:要打开的文件路径
            2、-flags:对文件的操作权限及其他设置
            原文: The  argument  flags must include one of the 
            following access modes: O_RDONLY, O_WRONLY, or 
            O_RDWR.  These request  opening the file
             read-only, write-only, or read/write, 
             respectively.(参数标志必须包括以下访问模式之
             一:O_RDONLY、O_WRONLY或O_RDWR。这些请求分别以只读、
             只读或读/写方式打开文件,三者互斥) 
             O_RDONLY,  O_WRONLY,   O_RDWR这三个设置是互斥的
        2)返回值:返回一个新的文件描述符,如果调用失败,
        返回-1(在man 2 open 进入说明文档后,可以使用
        /return value来查看这个函数的返回值说明)

    errno:属于Linux系统函数库,库里面的一个全局变量,
    记录的是最近的错误号。但是最终还是想看具体错误信息
    所以使用perror(C库函数),打印具体信息

    #include <stdio.h>

    void perror(const char *s);
        s参数:用户描述,比如hello,最终输出的内容是 hello:***(实际的错误报告)
    作用:打印error对应的错误描述

    //创建一个新的文件
    int open(const char *pathname, int flags, mode_t mode);//并非重载,而是可变参数实现


*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//linux open
#include <stdio.h>  //C perror
#include <unistd.h> //linux close


int main(){
    int fd=open("a.txt",O_RDONLY);

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

    //关闭
    close(fd);

    return 0;
}

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

1.20 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,文件不存在就创建(还有很多,man 2 open就能看到,文件的追加写入等等)
            -mode:八进制的数,表示用户创建出的新的文件的操作权限,比如:0777
            但最终的权限是:mode & ~umask(0002)
            (0002转换成2进制:000 000 000 010)
            (取反:111 111 111 101  0775)
            (0777转成2进制: 000 111 111 111)
            (二者相与:000 111 111 101 也就是0775)
            0777 rwxrwxrwx ->111 111 111
        &   0775           ->111 111 101
        --------------------------------
                             111 111 101
       (其他组不能对文件进行写操作)->用户、本组、其他组
        按位与:0与任何数都为0
        umask的作用就是抹去某些权限!
        可通过umask 0022将掩码设置成0022,但仅对此终端有效


*/
#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);
    //O_RDWR|O_CREAT
    //按位或:flags是一个int类型的数据,占4个字节,32位
    //flags 32个位,每一位就是一个标志位  

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

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

在这里插入图片描述

//O_RDWR|O_CREAT
//按位或:flags是一个int类型的数据,占4个字节,32位
//flags 32个位,每一位就是一个标志位
在这里插入图片描述

1.22 read、write函数

解决xftp打开文件中文名乱码:
https://jingyan.baidu.com/article/0bc808fc03296a1bd485b9ed.html

read:从文件中读取数据到内存中
write:把内存中的数据写到文件当中
结合read和write可实现文件的拷贝功能

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

    #include <unistd.h>
    ssize_t write(int fd, const void *buf, size_t count);
        参数:
            - fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
            - buf:要往磁盘写入的数据,数组 
            - count:要写的数据的实际的大小
        返回值:
            成功:实际写入的字节数
            失败:返回-1,并设置errno
*/
#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 destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664); 
    //-rwxrwxrwx  -rw_rw_r__ &(~umask)
    //-rw_rw_r__  & (0775 就是去掉了最后一个w)
    //最后还是-rw_rw_r__ 
    if(destfd == -1) {
        perror("open");
        return -1;
    }

    // 3.频繁的读写操作
    char buf[1024] = {0};
    int len = 0;
    while((len = read(srcfd, buf, sizeof(buf))) > 0) {
        write(destfd, buf, len);
    }

    // 4.关闭文件
    close(destfd);
    close(srcfd);


    return 0;
}

1.23 lseek函数

获取文件从不同位置开始偏移一定量后,文件指针的位置。

作用:
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 <unistd.h>//Unix标准头文件
        off_t lseek(int fd, off_t offset, int whence);//Linux系统,对文件指针进行操作
            参数:
                -fd:文件描述符,通过open得到的,通过这个fd操作某个文件
                -offset:偏移量
                -whence:
                    SEEK_SET
                        设置从文件开始指针的偏移量

                    SEEK_CUR
                        设置偏移量:当前位置+第二个参数offset的值.

                    SEEK_END
                        设置偏移量:文件大小+第二个参数offset的值
            
            返回值:返回文件指针的位置

            作用:
                1、移动文件指针到文件头
                lseek(fd,0,SEEK_SET);
                返回从文件开始偏移0个单位后,指针的位置

                2、获取当前文件指针的位置
                lseek(fd,0,SEEK_CUR)
                返回从文件当前位置0个单位后,指针的位置

                3、获取文件长度
                lseek(fd,0,SEEK_END);
                返回从文件末尾偏移0个单位后,指针的位置

                4、拓展文件的长度,当前文件10b,拓展成110b,增加了100个字节
                lseek(fd,100,SEEK_END);
                注意:需写一次数据,扩展才生效
                应用:下载文件时,先扩展文件,预留文件大小后再下载数据。(比如有个5G的数据要下载到电脑,就会先在电脑创建一个5G的文件,然后一点点填充空字符。否则如果电脑没有5G空间,就算前4.8G下载了也没有用)
        //标准C库
        #include <stdio.h>
        int fseek(FILE *stream, long offset, int whence);
            
*/

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

int main(){
    int fd=open("hello.txt",O_RDWR);

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

    //扩展文件的长度
    int ret=lseek(fd,100,SEEK_END);
    if(ret==-1){
        perror("lseek");
        return -1;
    }

    //写入一个空数据
    write(fd," ",1);

    //关闭文件
    close(fd);
    
    return 0;
}
//最终其实是112

1.24 stat、lstat函数

int stat(const char *pathname, struct stat *statbuf);作用:获取文件的相关信息

直接用stat命令获取文件信息
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
st_mode类型一共有16位,每一组权限占3位,一共4组。共计12位。
权限例如:
00004、00040,此时后四位与二进制的换算可以直接看成8进制的,第一个0看成4位二进制。
也就是 0000 000 000 000 100、0000 000 000 100 000
文件类型例如:
0120000 后四个0代表八进制,012此时是一个八进制整体 012不能直接转成000 001 010,而是0+8+2 = 10,转换成4位二进制是:1010,所以这一串的整体是:1010 000 000 000 000

Linux有七种文件类型。st_mode通过一个或多个标志位表示不同的文件类型。
判断文件权限&与操作:
比如与0001,如果有这个权限,最后一位的结果就是1,如果没有那就是0。并且除了这一位全都置零,方便比较
判断文件类型:
将st_mode与掩码与操作后,并与具体文件类型进行比较:
掩码:170000,其实就是1111 000 000 000 000
前四位是1或者0都会被保留下来,后面12位直接置零,方便跟具体类型比较

/*
        #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;
}

关于lstat函数
作用:查看软连接的文件信息
使用ln -s 给a.txt创建软链接 b.txt
在这里插入图片描述
可以看到b的大小其实只有5,但是如果但是如果用stat函数读b.txt得到的会是a的文件信息,直接vim b.txt其实打开的也是a文件
如果想知道b的文件信息,就要使用lstat函数

1.25 使用stat函数模拟实现 ls -l 命令

ls -l命令功能演示
在这里插入图片描述

//模拟实现ls-l 指令
//-rw-rw-r-- 1 xiaohong xiaohong 13 5月  25 22:34 a.txt

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.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';
            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;

    //文件所有者
    //st.st_uid只能返回所有者的id,但我们想知道用户名
    //直接看结构体password的成员pw_name
    char * fileUser=getpwuid(st.st_uid)->pw_name;
    //extern struct passwd *getpwuid (__uid_t __uid);
    // struct passwd
    // {
    //     char *pw_name;		/* Username.  */
    //     char *pw_passwd;		/* Password.  */
    //     __uid_t pw_uid;		/* User ID.  */
    //     __gid_t pw_gid;		/* Group ID.  */
    //     char *pw_gecos;		/* Real name.  */
    //     char *pw_dir;			/* Home directory.  */
    //     char *pw_shell;		/* Shell program.  */
    // };

    //文件所在组
    char * fileGrp=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); //拷贝字符串 去掉time自带的回车换行

    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; 

}

1.26 文件属性操作函数

在这里插入图片描述
1、acess 判断文件是否存在以及文件的权限

/*
    #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("acess");
        return -1;
    }
    
    printf("文件存在!!!\n");
    

    return 0;
}

2、chmod (changemod) 修改权限

/*
    #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",0777);

    if(ret==-1){
        perror("chmod");
        return -1;
    }
    
    return 0;
}

3、chown (change owner)改变文件所有者或者所在组

int chown(const char* path,uid_t owner,gid_t group);

1、vim /etc/passwd 显示所有用户的id和所在组id
依次是:用户名、用户id、所在组id在这里插入图片描述
2、vim /etc/group 显示所有组的名称和对应的id
root组的id就是0在这里插入图片描述
3、使用sudo创建新用户和分组
uid = userid
gid = groupid在这里插入图片描述

4、truncate 缩减或者扩展文件的大小

/*
    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char* pathname,off_t length);
        作用:缩减或者扩展文件的尺寸至指定的大小
        参数:
            -pathname:需要修改的文件路径
            -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;
}

1.27 目录操作函数

在这里插入图片描述1、mkdir 创建目录
在创建的时候要注意:想要进入目录,就必须要给可执行权限


/*
    #include <sys/stat.h>
    #include <sys/types.h>
    int mkdir(const char* pathname,mode_t mode);
        作用:创建一个目录
        参数:
            pathname:创建的目录路径
            mode:权限,八进制的数,与掩码进行与操作获得最后的权限
            最终是:mode & ~umask & 0777
        返回值:
            成功返回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;
}


2、rmdir
只能删除空目录(目录下不能有东西)

int rmdir(const char* pathname);

3、rename 修改目录名称

/*
    #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;
}

4、chdir 修改进程的工作目录
5、getcwd 获取当前工作目录

/*
    #include <unistd.h>
    int chdir(const char* path);
        作用:修改进程的工作目录
            比如在/home/nowcoder启动了一个可执行程序a.out,进程的工作目录就是/home/nowcoder
        参数:
            path:需要修改的工作目录
        返回值:
            成功返回0,失败返回-1

    #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);

    //修改工作目录
    int ret=chdir("/home/xiaohong/Linux/lesson13");
    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);

    return 0;
}

1.28 目录遍历函数

在这里插入图片描述
这三个函数在第三章查看 man 3 opendir

在这里插入图片描述

/*
    //打开一个目录
    #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* drip);
*/

#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;
}


1.29 dup、dup2函数

在这里插入图片描述

/*
    #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;
}

/*
    #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);
    if(fd==-1){
        perror("open");
        return -1;
    }

    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);

    int fd2=dup2(fd,fd1);//fd2与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;
}

1.30 fcntl函数

fcontrol
在这里插入图片描述

/*

    #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_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;   // flag = flag | O_APPEND

    // 修改文件描述符状态的flag,给flag加入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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/739626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux 乱码

在 Linux 中遇到乱码问题通常是由于字符编码不正确导致的。以下是几种常见的乱码情况以及相应的解决方法&#xff1a; 1、终端乱码&#xff1a;如果在终端中出现了乱码&#xff0c;可以尝试以下方法来解决&#xff1a; 检查终端的字符编码设置&#xff1a;确保终端的字符编码…

18 MFC TCP和UDP 网络通信

文章目录 TCP服务器TCP客户端UDP 服务器UDP客户端MFC TCP通信TCP服务器TCP 客户端 MFC UDP通信 TCP服务器 #include <stdio.h> #include <WinSock2.h> #pragma comment(lib,"ws2_32.lib") //包含静态库/* .obj .lib 文件 组合打包成 .exe */int main()…

申请和注销设备号的方法

一、Linux内核对设备的分类 linux的文件种类&#xff1a; -&#xff1a;普通文件 d&#xff1a;目录文件 p&#xff1a;管道文件 s&#xff1a;本地socket文件 l&#xff1a;链接文件 c&#xff1a;字符设备 b&#xff1a;块设备 Linux内核按驱动程序实现模型框架的不…

【C/C++练习】经典的排列组合问题(回溯算法)——电话号码的字母组合

&#x1f4d6;题目描述 题目出处&#xff1a;电话号码的字母组合 示例&#xff1a; &#x1f4d6;题解  这是一道典型的排列组合问题&#xff0c;根据输入&#xff0c;我们需要找到所有的组合。下面以输入字符串digits "23"为例来讲解这道题目。 图解&#xff1…

Java入门到入土(集合篇)

前言 初出茅庐 Collection集合特点 Map集合特点 牛刀小试 List集合用法 迭代器原理 Set集合用法 Map集合用法 追根溯源 List集合解析 Set集合解析 Map集合解析 结束语 前言 Java中用来批量存储数据的方式有…

mac 怎么批量修改文件后缀?

mac 批量修改文件后缀的方法教程~平时在电脑上使用文件的时候&#xff0c;经常需要对文件的后缀名进行修改&#xff0c;文件后缀名也就是文件扩展名&#xff0c;如果仅是单纯的修改文件后缀名&#xff0c;并不涉及格式转换的情况下&#xff0c;其实方法很简单&#xff0c;只需要…

ZBrush 3D游戏建模教程:创建女武士模型

推荐&#xff1a;将NSDT场景编辑器加入你的3D工具链 3D工具集&#xff1a;NSDT简石数字孪生 介绍 在本教程中&#xff0c;演示创建实时武士角色的流程&#xff0c;该流程基于 Kati Sarin S 的原始概念。我将使用各种各样的软件&#xff0c;如ZBrush&#xff0c;Maya&#xff…

微信小程序外卖管理的设计与实现(论文+源码)_kaic

摘要 随着互联网技术的不断更新和发展&#xff0c;人们的生活水平也在不断的提高&#xff0c;人们对互联网的依赖越来越紧密&#xff0c;尤其是网上外卖也越来越习惯了。它的功能包括在主页上列出出售的外卖。您也可以直接在首页顶部搜索想要的产品&#xff0c;如果余额不足&am…

信号与系统课程实验报告: 连续信号及其傅里叶变换的编程

一、实验目的 认识并熟悉Matlab软件的使用&#xff0c;并能利用其进行编程利用Matlab实现连续信号的表示掌握数值法和符号法进行编程实现连续周期信号的傅里叶级数求解编程算法实现连续信号的傅里叶变换求解编程算法理解Matlab代码的具体意义并熟练使用利用Matlab对LTI系统的频…

Maven依赖管理(核心)

依赖配置 依赖&#xff1a;指当前项目运行所需要的jar包&#xff0c;一个项目可以引入多个依赖配置&#xff1a; 在pom.xml中编写<dependencies>标签在<dependencies>标签中使用<dependency>引入坐标定义坐标的groupId、artifacId、version点击刷新按钮&…

二叉树层序遍历

目录 一、什么是层序遍历 二、层序遍历的实现 三、判断一棵树是否为完全二叉树 总结&#xff1a; 学习二叉树结构&#xff0c;最简单的就是遍历。 所谓二叉树遍历就是按照某种规则对二叉树中的节点进行相应操作&#xff0c;每个节点值操作一次。 遍历是二叉树的重要运算之…

深入篇【C++】手搓模拟实现string类(详细剖析常见的各接口):【400行代码实现】

深入篇【C】手搓模拟实现string类(包含常见的各接口&#xff09;&#xff1a;【400行代码实现】 【string类模拟实现完整代码】Ⅰ.构造/析构1.string()2.operator3.~string() Ⅱ.访问遍历1.operator[]2.iterator3.范围for Ⅲ.增操作1.push_back()2.append()3.operator4.insert(…

Harbor未授权创建管理员

人处在幸福与不幸交织的矛盾之中&#xff0c;反而使内心有一种更为深刻的痛苦&#xff0c;看来近在眼前的幸福而实际上又远得相当渺茫&#xff0c;海市蜃楼。放不得抓不住。 漏洞描述 近日&#xff0c;镜像仓库Harbor爆出任意管理员注册漏洞&#xff0c;攻击者在请求中构造特…

eNSP-VLAN多端口成员模式+DHCP

VLAN多端口成员模式DHCP 文章目录 VLAN多端口成员模式DHCP一、题目要求二、题目分析三、拓扑结构四、基本配置五、测试验证1.网段测试2.访问测试 一、题目要求 1、PC1和pc3所在接口为Access接口&#xff0c;PC2/4/5/6处于同一网段&#xff0c;其中PC2可以访问PC4/5/6&#xff…

Webkit内核探究——Webkit CSS实现

文章目录 前言1、CSS是什么2、CSS实现模型3、CSS默认样式表4、CSS解析5、CSS如何作用于Render Tree 前言 CSS在Webkit中的实现属于相对独立的一个模块&#xff0c;注意这里说的是相对。 CSS在Webkit中的作用自然是不言而喻的&#xff0c;在Web早期&#xff0c;文档的结构和样…

【运维工程师学习五】数据库

【运维工程师学习五】数据库 1、常用的关系型数据库2、C/S结构3、MariaDB图形客户端4、安装MariaDB5、启动MariaDB及验证启动是否成功6、验证启动——端口7、验证启动——进程8、MariaDB配置文件路径主配置文件解读&#xff1a; 9、MariaDB的配置选项10、MariaDB客户端连接1、在…

Windows下 Oracle 12c 安装保姆级图文详解

Windows下 Oracle 12c 安装步骤如下&#xff1a; 1、将压缩包“winx64_12c_database_1of2.zip“和“winx64_12c_database_2of2.zip”解压到同一目录“database”目录。 2、双击“database”目录下的“setup.exe"&#xff0c;软件会加载并初步校验系统是否可以达到了数据…

华为云出品《深入理解高并发编程:Java线程池核心技术》电子书发布

系统拆解线程池核心源码的开源小册 透过源码看清线程池背后的设计和思路 详细解析AQS并发工具类 点击下方链接进入官网&#xff0c;右上角搜索框搜索“《深入理解高并发编程&#xff1a;Java线程池核心技术》” 即可获取下载。 https://auth.huaweicloud.com/authui/login…

01-线性表 (数据结构和算法)

要点&#xff1a; 程序 数据结构 算法 一、数据结构的概述 程序 数据结构 算法 数据结构&#xff1a;计算机存储、组织数据的方式 算法&#xff1a;处理数据的方式 1.1 基本概念和术语 1、数据 数据&#xff08;data&#xff09;&#xff1a;所有能够输入到计算机中…

【Method】稀疏与压缩感知 | 图像稀疏性及压缩感知方法白话讲解

【Method】稀疏与压缩感知 | 图像稀疏性及压缩感知方法白话讲解 文章目录 【Method】稀疏与压缩感知 | 图像稀疏性及压缩感知方法白话讲解1. 为什么图像是可压缩的&#xff1a;图像空间的广阔2. 什么是Sparsity&#xff1f;3.压缩感知&#xff1a;简介4.压缩感知&#xff1a;数…