牛客网C++项目-Linux高并发服务器开发之第一章:Linux系统编程入门 学习笔记

news2024/11/16 4:40:46

1.1 Linux 开发环境搭建

由于仅是开发环境的搭建,所以只简单记述一下步骤

必备软件:

  • Ubuntu 18.04

  • XShell-用于远程登录,使用SSH协议,TCP连接,端口号22

  • XFtp,本次实验中尚未用到

  • Visual studio code,安装扩展包:

    • Remote Development,用于建立远程连接管理

      同样使用SSH建立连接

      为了便于后期操作,windows端与ubuntu端使用相同的公钥

      ssh-keygen -t rsa

      此处也可以记录一下原本的过程:

      (1)远程主机收到用户的登录请求,把自己的公钥发给用户。

      (2)用户使用这个公钥,将登录密码加密后,发送回来。

      (3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

    • 中文软件包

大体本节实验就结束了


1.2 && 1.3 GCC

GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器。GNU还有其他语言,例如Java、Go的编译器套件,也包括了这些语言的库

GCC不仅支持C,还可以区分不同的C语言标准,支持用命令行选项来控制编译器在编译源代码时应该遵循哪个C标准。

查看标准可以使用如下命令

gcc/g++ -v/–version

编译文件的命令格式如下:

gcc 文件名.扩展名 -o 输出文件名

本节第二个内容有关于编程语言的发展:

image-20221230212448758


第三个内容有关 GCC的工作流程:

image-20221230212722069

预处理:指令-E,举例如下:

gcc test.c -E -o test.i

编译:指令-S

gcc test.i -S -o test.s

汇编:指令-c

gcc test.s -s -o test.o

.o文件已是可执行文件


第四个内容有关gcc和g++的区别
首先声明gcc和g++都是GNU(组织)的一个编译器

误区1:gcc只能用于编译c代码,g++只能编译c++代码。但实际上两者都可以

  • 后缀为.c的gcc把它当做c程序,而g++当做是c++程序

  • 后缀为.cpp的,两者都会认为是c++程序,c++的语法规则更加严谨一些

  • 编译阶段,g++会调用gcc,对于c++代码,两者等价,但因为gcc命令不能自动和c++程序使用的库链接,所以通常用g++完成链接。

    为了统一,索性直接用g++完成编译、链接,但实际上,gcc也可以完成.cpp程序的编译

误区2:gcc不会定义__cplusplus宏,而g++会

  • 实际上,该宏只是标志着编译器会把代码按照c还是c++语法来解释
  • 如果后缀为.c,并且采用gcc编译器,那么宏就是未定义的;使用g++就是已定义

误区3:编译只能用gcc,链接只能用g++

  • 严格来说,这句话不错,但是混淆了概念,应该说编译二者都可以用,而链接则可以用g++或者gcc -lstdc++

  • gcc命令不能自动和C++程序使用的库链接,所以通常用g++来完成链接,但在编译阶段,二者等价


内容5,GCC常用参数选项

image-20221231110141204

image-20221231110219346


1.4 静态库的制作

此前也稍微了解过如何制作静态库

私密性好,方便部署与分发

image-20221231111053796 image-20221231111138141

简而言之需要先生成各文件的可执行文件,再将这些可执行文件封装入库


1.5 静态库的使用


1.6 动态库的制作与使用

image-20221231115842193 image-20221231120040305

1.7 动态库加载失败的原因

首先介绍一下动态库和静态库的工作原理:

  • 静态库:

  • 动态库:

  • 程序启动之后,动态库会被动态加载到内存中,通过ldd(list dynamic dependencies)命令检查动态库依赖关系

  • 如何定位共享库文件呢?

    当系统加载可执行代码时,能够直到其所依赖的库的名字,但是还需要知道绝对路径。此时就需要系统的动态载入器来获取该绝对路径。

    先后搜索DT_RPATH段→环境变量LD_LIBRARY_PATHetc/ld.so.cache文件列表→/lib/,/usr/lib目录(不推荐,因为文件众多)

    找到库文件后将其载入内存


1.8 解决动态库加载失败问题


1.9 静态库和动态库的对比

image-20221231165401870

静态库制作过程:

image-20221231165917833

动态库制作过程:

image-20221231170447657

静态库的优缺点:

优点:

  • 被打包到应用程序之中,加载速度快
  • 发布程序无需提供静态库,移植方便

缺点:

  • 消耗系统资源,浪费内存(不管用不用,都得占内存)
  • 更新、部署、发布麻烦(每次修改都需要重新编译库文件)

image-20221231170926699

动态库的优缺点:

优点:

  • 可以实现进程间资源共享(共享库)
  • 更新、部署、发布简单
  • 可以控制何时加载动态库

缺点:

  • 加载速度比静态库慢
  • 发布程序时需要提供依赖的动态库

image-20221231171402933


1.10/11/12 Makefile

什么是Makefile?

一个工程中源文件不计其数,按照类型、功能、模块分别放在若干个目录中。

Makefile文件定义了一系列的规则来指定那些文件需要先编译,哪些文件需要后编译,那些文件需要重新编译,甚至更复杂的功能操作。Makefile就像是一个shell脚本一样,也可以执行操作系统的命令。

文件一定要命名为makefile或者Makefile

Makefile规则:

  • 一个Makefile文件可以有一个或者多个规则:

    目标 ...: 依赖 ...
    	命令(Shell命令)
    	...
    
    • 目标:最终要生成的文件(伪目标除外)
    • 依赖:生成目标所需要的文件或是目标
    • 命令:通过执行命令对依赖操作生成目标(命令行前必须是Tab缩进)
  • Makefile中其他规则一般都是为了第一条规则服务的

Makefile工作原理:

  • 命令在执行前,需要检查规则中的依赖是否存在
    • 如果存在,执行命令
    • 如果不存在,向下检查其它规则,检查有没有一个规则是用来生成这个依赖的,如果找到了,则执行该规则中的命令
  • 检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间
    • 如果依赖的时间比目标的时间晚,需要重新生成目标
    • 如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被执行
      规定定义的条数多一些,可以令模块更新更加自由,效率有可能会更高

Makefile中的变量:

  • 自定义变量

    变量名 = 变量值,例如 var=hello

  • 预定义变量

    AR:归档维护程序的名称,默认值为ar

    CC:C编译器的名称,默认值为cc

    CXX:C++编译器的名称,默认值为g++

    $@:目标的完整名称

    $<:第一个依赖文件的名称

    $^:所有的依赖文件

  • 获取变量的值

    $(变量名)

image-20230101132330451

Makefile中的模式匹配:

image-20230101141401054

Makefile中的函数:

  • $(wildcard PATTERN ...)

    • 功能:获取指定目录下指定类型的文件列表

    • 参数:PATTERN 指的是某个或多个目录下的对应的某种类型的文件,如果有多个目录,一般使用空格间隔

    • 返回:得到的若干个文件的文件列表,文件名之间使用空格间隔

    • 示例:

      $(wildcard *.c ./sub/*.c)

      返回值格式:a.c b.c c.c d.c e.c f.c

  • $(patsubst <pattern>,<repalcement>,<text>)

    • 功能:查找<text>中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符合模式<pattern>,如果匹配的话,则以<replacement>替换。

    • 参数:<pattern>可以包括通配符%,表示任意长度的字符串。如果<replacement>中也包含%,那么,<replacement>中的这个%将是<pattern>中的那个%所代表的字符串。(可以用\来转义,以\%来表示%字符串)。

    • 返回:函数返回被替换过后的字符串

    • 示例:

      $(patsubst %.c, %.o, x.c bar.c)

      返回值格式:x.o bar.o

  • clean属于目标中的一种,可以用于清除指定类型的文件


1.13~1.16GDB调试

GDB是GNU软件系统社区提供的调试工具,同GCC配套组成了一套完整的开发环境,GDB是Linux和许多类UNIX系统中的标准开发环境。

一般来说,GDB主要完成以下四个方面的功能:

  • 启动程序,按照自定义的要求随心所欲的运行程序
  • 可让被调试的程序在所指定的调试断点处停住(断点可以为条件表达式)
  • 当程序被停住时,可以检查此时程序中所发生的事
  • 可以改变程序,将一个BUG产生的影响修整从而测试其他BUG

GDB的官方吉祥物:射水鱼

image-20230101145102128


准备工作:

  • 通常,为调试而编译时,我们会关掉编译器的优化选项-O,并打开调试选项-g。另外,-Wall在尽量不影响程序行为的情况下选项打开所有的warning,也可以发现一些能避免bug的问题
  • gcc -g -Wall program.c -o program
  • -g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行(但并不是把整个源文件嵌入到可执行文件中),所以在调试时必须保证gdb能找到源文件。

GDB中的常见命令

基础命令——

  • 启动和退出

    gdb 可执行程序

    quit或者q

  • 设置参数(获取程序参数)

    set args 10 20

    show args

  • GDB 使用帮助

    help

行号相关——

  • 查看当前文件代码

    list/l 从默认位置显示

    list/l 行号 从指定的行显示

    list/l 函数名 从指定的函数显示

  • 查看非当前文件代码

    list/l 文件名:行号

    list/l 文件名:函数名

  • 设置显示的行数

    show list/listsize

    set list/listsize 行数

断点相关——

  • 设置断点

    b/break 行号

    b/break 函数名

    b/break 文件名:行号

    b/break 文件名:函数

  • 查看断点

    i/info b/break

  • 删除断点

    d/del/delete 断点编号

  • 设置断点无效

    dis/disable 断点编号

  • 设置断点生效

    ena/enable 断点编号

  • 设置条件断点(一般用于循环的位置)

    b/break 10 if i==5

正式调试相关——

  • 运行GDB程序

    start 程序停在第一行

    run 遇到断点才停

  • 继续运行,到下一个断点停止

    c/continue

  • 向下执行一行代码(不会进入函数体)

    n/next

  • 变量操作

    p/print 变量名 打印变量值

    ptype 变量名 打印变量类型

  • 向下单步调试(遇到函数进入函数体)

    s/step

    finish 跳出函数体

  • 自动变量操作

    display num 自动打印指定变量的值

    i/info display

    undisplay 编号

  • 其他操作

    set var 变量名=变量值

    until 跳出循环


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

标准c库IO函数——可以跨平台

image-20230101170443533

image-20230101172134454

主要区别在于调用与被调用:在Linux环境中,标准C库会调用Linux的系统函数


1.18 虚拟地址空间

image-20230101173435001

当进程运行后,就相当于上图所示的一个系统模型

虚拟内存由CPU进行管理

用户区:

内核区:普通用户无权限操作


1.19 文件描述符

image-20230101174531002

PCB属于内核区的内存管理模块,本质是一个复杂的结构体

每个进程都有一个文件描述符表,其是一个数组,大小为1024,前三位是默认被占用的


1.20 open打开文件

标准c库的api man 3 xxx

Linux的IO api man 2 xxx


1.21 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, or O_RDWR,三个选项相互互斥
        - 可选项:O_CREAT 文件不存在,就创建新文件
        - mode:八进制,表示创建出文件的操作权限,比如:0775
        但最终的权限是:mode & ~umask(和用户权限有关)
#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;
}

1.22 read、write函数

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
    参数:
        - fd:文件描述符,通过open函数返回
        - buf:读入数据的存放地址,通常为数组地址
        - count:指定缓冲区的大小
        返回值:
        	- 成功:
        >0:返回实际的读取到的字节数
            =0:文件已经读取完了
            - 失败:-1,并且设置errno(error number)

            #include <unistd.h>
            ssize_t write(int fd, const void *buf, size_t count);
    参数:
        - fd:同上
        - buf:要往磁盘写入的数据
        - count:要写的数据的实际大小
        返回值:
        	- 成功:实际写入的字节数
       		- 失败:返回-1,设置errno
int main() {

    int srcfd = open("english.txt", O_RDONLY);

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

    int destfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
    if (destfd == -1) {
        perror("open");
        return -1;
    }

    char buf[1024] = {0};

    int len = 0;
    while ((len = read(srcfd, buf, sizeof(buf))) > 0) {
        write(destfd, buf, len);
    }

    close(destfd);
    close(srcfd);

}

1.23 Iseek函数

标准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得到的,通过这个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>

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

1.24 stat、Istat函数

两个函数都有读取文件的信息有关

image-20230102211539717

image-20230102211631967

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

1.25 模拟实现ls -l命令

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

// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
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;

    // 文件所有者
    char * fileUser = getpwuid(st.st_uid)->pw_name;

    // 文件所在组
    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);

    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 文件属性操作函数

image-20230102221039435


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

    printf("文件存在!!!\n");

    return 0;
}

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

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

1.27 目录操作函数

image-20230102223214408


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

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

/*

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

    // 修改工作目录
    int ret = chdir("/home/nowcoder/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 目录遍历函数

image-20230102224157091

image-20230102224804763


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

1.29 dup、dup2函数

image-20230102225600499
/*
    #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);
    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函数

image-20230102225629827


/*
    #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/136891.html

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

相关文章

什么是蒙特卡洛学习,时序差分算法

在学习的过程中经常会看到蒙卡特洛和时序差分算法&#xff0c;到底这两个是指什么&#xff0c;今天稍微整理下&#xff0c;开始吧。蒙卡特洛1.1 蒙卡特洛方法蒙特卡罗方法又叫做统计模拟方法&#xff0c;它使用随机数(或伪随机数)来解决计算问题。比如上图&#xff0c;矩形的面…

Python全栈开发(二)——python基础语法(二)

我们昨天说了python的数据类型&#xff0c;今天说说python的缩进规则和函数、python的顺序语句结构&#xff0c;条件和分支语句以及循环语句。缩进不规范会报错&#xff08;IndentationError: unexpected indent&#xff09;&#xff0c;python的函数使用&#xff0c;从定义到实…

操作系统——进程之处理机调度

操作系统——进程之处理机调度一、处理机调度的本概念和层次1、高级调度&#xff08;作业调度&#xff09;2、中级调度&#xff08;内存调度&#xff09;3、低级调度&#xff08;进程调度&#xff09;二、进程调度的时机、切换与过程、方式1、进程调度的时机2、进程调度的方式3…

ATAC-seq分析:比对(3)

1. 质控 在比对之前&#xff0c;我们建议花一些时间查看 FASTQ 文件。一些基本的 QC 检查可以帮助我们了解您的测序是否存在任何偏差&#xff0c;例如读取质量的意外下降或非随机 GC 内容。 2. Greenleaf 在本节中&#xff0c;我们将稍微处理一下 Greenleaf 数据集。 我们将处理…

新一代OPC UA解决方案,快速实现IT与OT融合

一、OPC数据采集难题 OPC技术在现今的工业自动化中应用越来越广泛&#xff0c;为现场工业控制设备与控制软件之间的数据交换提供了统一的数据存储规范。但随着工业的不断发展&#xff0c;OPC数据采集出现了一些难题。例如&#xff0c;在传统OPC在远程连接时候一定会面临的DCOM…

Qt扫盲-Qt Designer配置QSS交互使用

Qt Designer配置QSS交互记录一、概述二、用法1. 选择2. 修改1. 菜单区2. 编辑区3. 在底部功能区4. 查询一、概述 Qt Designer {Qt Designer }是一个很好的工具来预览样式表、设置样式的效果&#xff0c;而且是所见即所得&#xff0c;用界面这种开发更快些。 我一般是在Qt Des…

【编译基础】new delete详解及内存泄漏

内存的使用&#xff0c;一文不太够 文章目录C语言1.new关键字2.delete关键字C语言1.malloc关键字2.free关键字区别内存泄漏参考博客&#x1f60a;点此到文末惊喜↩︎ C语言 1.new关键字 作用&#xff1a;C通过new关键字动态分配内存三种用法 plain new&#xff1a;最朴素的n…

JdbcUtils工具类的优化升级——通过配置文件连接mysql8.0

我之前的博文JDBC重构——JdbcUtils工具类的封装写了一个JdbcUtils的工具类&#xff0c;但是这个类也会有一个问题&#xff1a;如下图所示&#xff1a;连接数据库的代码在java中是写死的&#xff0c;如果我们想要换一个数据库进行连接&#xff0c;就会很麻烦&#xff0c;这时我…

嵌入式HLS 案例开发手册——基于Zynq-7010/20工业开发板(2)

目 录 2 led_flash 案例 19 2.1 HLS 工程说明 19 2.2 编译与仿真 20 2.3 IP 核测试 23 3 key_led_demo 案例 23 3.1 HLS 工程说明 23 3.2 编译与仿真 25 3.3 IP 核测试 27 前 言 本文主要介绍 HLS 案例的使用说明,适用开发环境: Windows 7/10 64bit、Xilinx Vivado…

从零搭建的前后端完整的直播网页方案

前言&#xff1a;由于前段时间刚租了台服务器打算自己玩玩&#xff0c;随想首页或者哪哪个页面挂个我个人的直播间应该还挺有趣的。遂探索如何在我的网站上弄一个直播。三下五除二&#xff0c;清清爽爽&#xff0c;看完此文5分钟即可直播。 整体思路 最简单直观的图解。 由上图…

VB2019创建、使用静态库(同样的使用动态库dll)

库&#xff1a; 二进制可执行文件&#xff0c;操作系统载入内存执行&#xff0c;将不怎么更改的底层打包成库后可以使整体编译更改&#xff0c;并且实现对底层的保密&#xff08;不对外或员工开放&#xff09;。库有两种&#xff1a;静态库&#xff08;.a、.lib&#xff09;和动…

【国科大模式识别】第二次作业(阉割版)

【题目一】最大似然估计也可以用来估计先验概率。假设样本是连续独立地从自然状态 ωi\omega_iωi​ 中抽取的, 每一个自然状态的概率为 P(ωi)P\left(\omega_i\right)P(ωi​) 。如果第 kkk 个样本的自然状态为 ωi\omega_iωi​, 那么就记 zik1z_{i k}1zik​1, 否则 zik0z_{i…

【无标题】测试新发文章

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

Spring之后处理器

目录 一&#xff1a;概述 二&#xff1a;案例演示 三&#xff1a;Bean的后处理器----BeanPostProcessor 案例&#xff1a;对Bean方法进行执行时间日志增强 四&#xff1a;Spring ioc整体流程总结 一&#xff1a;概述 Spring的后处理器是Spring对外开发的重要扩展点、允许我…

量子计算机是什么?量子计算机和传统计算机之间有什么区别?

1.突破1000量子比特大关&#xff01; 2022年11月9日的IBM年度量子峰会上&#xff0c;IBM宣布了Osprey在量子硬件和软件方面取得的突破性进展&#xff0c;同时推出了“鱼鹰”&#xff08;Osprey&#xff09;芯片。“鱼鹰”是全球迄今为止量子比特最多的量子计算机&#xff0c;而…

软考初级信息处理

软考初级信息处理技术员还是比较简单的&#xff0c;只要多刷题&#xff0c;实操也很重要的&#xff01;备考时间讲究高效哦&#xff01; 1、考试安排&#xff1a; 2、关于信处的考点分析&#xff1a; 上午考试&#xff1a; 下午考试&#xff1a; 3、信处如何备考&#xff1a;…

从spark WordCount demo中学习算子:map、flatMap、reduceByKey

文章目录spark map和flatMap应用&#xff1a;Word CountreduceByKey的用法spark map和flatMap val rdd sc.parallelize(List("coffee panda","happy panda","happiest panda party"))&#xff08;1&#xff09;map rdd.map(_.split(" &q…

windows terminal 还是 cmder ?

前景提要 windows terminal自带的没有tab命令自动补全, cmd的自动补全垃圾; cmder虽然有自动补全, 但是界面管理不太行; 而且比较复杂&#xff1b;只想要其UI和路径换行显示; windows terminal 应用商城或https://github.com/microsoft/terminal 下载页 https://github.com/mic…

【算法刷题】哈希表题型及方法归纳

哈希表特点 常见的三种哈希结构&#xff1a; 1、数组&#xff1a;操作简单&#xff0c;方便快捷&#xff0c;但不适于进行一些更复杂的操作。 注&#xff1a;适用于用set或map的情景&#xff1a;&#xff08;1&#xff09;当数组大小受限&#xff1b;&#xff08;2&#xff0…

powerquery 连接 postgresql

1下载安装postgresql的驱动器 https://pan.baidu.com/s/1ii9PudUs9WL_clP7Ub647Q 提取码&#xff1a;hm6g 2 安装配置odbc 2.1打开控制面板 – 选择管理工具 2.2选择ODBC数据源(64位) 2.3控制面板搜索数据源-单击添加 选择postgresql unicode 2.4配置数据源信息 3.通过e…