linux多进程与多线程总结

news2024/11/23 3:25:47

这里写自定义目录标题

    • 2 linux多进程与多线程
      • 2.1 进程间通信
        • 2.1.1 管道
        • 2.1.2 信号
        • 2.1.3 消息队列
        • 2.1.4 共享内存
    • 3 线程
    • 4 IO多路复用
      • 4.1 非阻塞IO
      • 4.2 IO多路复用

2 linux多进程与多线程

学习并发程序。

linux系统中,使用树型管理进程。因此进程之间有父子关系。通过getpid()getppid()函数,获取进程ID。

1 进程的地址空间
一旦进程建立之后, 系统则要为这个进程分配相应的资源。32位的系统,一般系统会分配4G的地址空间。这个地址空间结构:

  • 4G: 内核空间
  • 0-3G: 用户空间。用户空间有具体分为:
    • stack: 存放非静态的局部变量
    • heap: 动态申请的内存
    • .bss: 未初始化过的全局变量(包括初始化为 0 的, 未初始化过的静态变量 (包括初始化为 0)
    • .data: 初始化过并且值不为 0 的全局变量, 初始化过的不为 0 静态变量
    • .rodata: 只读变量(字符串之类)
    • .text: 程序文本段(包括函数,符号常量)
  • 当用户进程需要通过内核获取资源时, 会切换到内核态运行, 这时当前进程会使用内核空间的资源

在这里插入图片描述

注意,这里的4G地址空间,是虚拟空间,不是占有实际的内存地址。虚拟地址空间中的每个地址都是一个虚拟地址,虚拟地址只是一个用于寻址的编号。

  • 虚拟地址空间,好比一张地图。地图本身是一张纸,而不是真实的地点;地图上标注的每一个地点只是一个映射关系。
  • 物理地址:是指内存设备中真实存在的存储空间的编号

虚拟地址通过映射的方式建立与物理地址的关联, 从而达到访问虚拟地址就可以访问到对应的物理地址。

  • 在 cpu 中有一个硬件 MMU(内存管理单元) , 负责虚拟地址与物理地址的映射管理以及虚拟地址访问
  • 操作系统可以设置 MMU 中的映射内存段
    在这里插入图片描述

在操作系统中使用虚拟地址空间主要是基于以下原因

  • 直接访问物理地址, 会导致地址空间没有隔离, 很容易导致数据被修改
  • 通过虚拟地址空间可以实现每个进程地址空间都是独立的,操作系统会映射到不用的物理地址区间,在访问时互不干扰.

2 进程的状态管理:状态机
在这里插入图片描述

进程的状态一般分为如下:

  • 运行态 (TASK_RUNNING)。就绪或者正在进行都属于运行态
  • 睡眠态 () : 此时进程在等待一个事件的发生或某种系统资源
    • 可中断的睡眠 (TASK_INTERRUPT) : 可以被信号唤醒或者等待事件或者资源就绪
    • 不可中断的睡眠 (TASK_UNTERRUPT) : 只能等待特定的事件或者资源就绪
  • 停止态 (TASK_STOPPED) : 进程暂停接受某种处理。例如:gdb 调试断点信息处理。
  • 僵尸态(TASK_ZOMBIE):进程已经结束但是还没有释放进程资源

3 并发与并行

  • 并行 : 在 cpu 多核的支持下,实现物理上的同时执行
  • 并发 : 在有限的 cpu 核心的情况下(如只有一个 cpu 核心) , 利用快速交替 (时间片轮转) 执行来达到宏观上的同时执行。软件开发中,提高任务执行效率是通过并发

4 程序的并发执行:同一个时间段,有多个任务在同时执行,由操作系统调度算法来实现,比较典型的是时间片轮转

  • 假定一个时间片为1s,由操作系统来分配每个任务的时间片数量
  • 一旦一个任务的时间片消耗完,则操作系统会切换下一个任务到cpu中执行
  • 如果没有执行完成,则等待下一次分配
    在这里插入图片描述

并行是基于硬件完成,而并发则可以使用软件算法来完成, 在完成任务时,可以创建多个进程并发执行
通过 fork() 函数创建子进程之后,有如下特点:

  • 父子进程并发执行, 子进程从 fork() 之后开始执行
  • 父子进程的执行顺序由操作系统算法决定的,不是由程序本身决定
  • 子进程会拷贝父进程地址空间的内容, 包括缓冲区、文件描述符等:当子进程拷贝了父进程文件描述符后,则会共享文件状态标志与文件偏移量等信息
    在这里插入图片描述

5 进程多任务
使用 fork() 函数之后,会创建子进程,fork() 之后的代码会在父、子进程中都执行一遍

  • 如果父子进程执行相同的任务,则正常执行
  • 如果父子进程执行不同的任务,则需要利用 fork() 函数返回值

在创建多个进程时, 最主要的原则为 由父进程统一创建,统一管理, 不要进行递归创建

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

int g_x = 0;

int main(int argc, char **argv)
{
    printf("pid=%d;ppid=%d\n", getpid(), getppid());
    write(STDOUT_FILENO, "hello", 5); // Linux文件IO,不带缓存区
    fputs("hello", stdout); // 标准IO,自带缓冲区

    pid_t pid = fork(); // 子进程会复制父进程的缓冲区、文件描述符。因此运行结果会打印三个hello。
    if (pid == -1) {
        perror("fork error");
        return pid;
    }

    for (int x=0;x<5;x++) {
        g_x++;
        sleep(0.1);
    }
    printf("g_x=%d\n", g_x);


    return 0;
}

6 进程的退出
在进程结束时,需要释放进程地址空间 以及内核中产生的各种数据结构。
资源的释放需要通过调用 exit 函数或者 _exit 函数来完成

exit属于标准库函数,_exit为底层系统函数。exit是基于_exit的,即exit最终也会调用_exit,区别在于,exit还会刷新缓冲区,而_exit不会

在程序结束时,会自动调用 exit 函数。exit 函数让当前进程退出, 并刷新缓冲区。所以如无特殊需求,不用显示调用。

并发编程时,父进程一定要等待子进程退出,避免内存里面残留一些孤儿进程

7 进程的等待
在子进程运行结束后,进入僵死状态, 并释放资源, 子进程在内核中的 数据结构 依然保留。
父进程 调用 wait()waitpid() 函数等待子进程退出后,释放子进程遗留的资源。

示例:

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

int main(void)
{
    pid_t pid1 = fork();
    if (pid1 < 0) {
        perror("fork");
        return pid1;
    }

    if (pid1 > 0) {
        pid_t pid2 = fork();
        if (pid2 < 0) {
            perror("fork");
            return pid2;
        }

        if (pid2 > 0) {
            printf("this is father, pid=%d\n", getpid());
            int wstatus = 0;
            pid_t c_pid;
            // c_pid = wait(&wstatus); // 阻塞
            //c_pid = waitpid(-1, &wstatus, WNOHANG);
            c_pid = waitpid(pid1, &wstatus, WUNTRACED);
            if (c_pid < 0) {
                perror("wait error");
                return -1;
            }

            while ((c_pid = waitpid(-1, &wstatus, WNOHANG)) == 0) {}  // 非阻塞,但是主进程又要等待子进程退出写法。并发编程时,父进程一定要等待子进程退出,避免内存里面残留一些孤儿进程

            printf("wstatus=%d, child c_pid=%d\n", WEXITSTATUS(wstatus), c_pid);
        } else if (pid2 == 0) {
            sleep(5);
            printf("this is child2, pid=%d\n", getpid());
            exit(EXIT_SUCCESS);      
        }

    }
    else if (pid1 == 0) {
        sleep(2);
        printf("this is child1, pid=%d\n", getpid());
        exit(EXIT_SUCCESS);
    }
    return 0;
}

8 进程的替换:
使用execl函数族,为进程重载0-3G的用户空间,可与fork函数搭配使用。
语法:int execl("绝对路径", “标识符”, “需要的参数”(需要多少传入多少) ,NULL);
博客链接:https://blog.csdn.net/m0_53133879/article/details/125092300

其作用是通过一个进程启动另外一个进程。比如通过c调用执行一个python脚本:

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

int main()
{
    int ret;
    char *p_path = "/usr/bin/python3";
    ret = execl(p_path, "python3", "hello.py", NULL);
    // ret = execlp("ls", "my_ls", "-l", NULL);
    if (ret == -1) {  // 必须有异常处理,否则异常情况,不会做进程替换,导致出现两个执行主进程的程序
        perror("execl error");
	return ret;
    }
    printf("this is main process\n");
    return 0;
}

2.1 进程间通信

四种方式:管道、信号、消息队列、共享内存。

2.1.1 管道

无名管道pipe,适用于父子关系的进程;有名管道mkfifo,适用于独立的进程之间。

2.1.2 信号

信号是在软件层次上 是一种通知机制, 对中断机制的一种模拟,是一种异步通信方式

信号来源:

  • 其他程序发送
  • 控制终端发送,如ctrl+c,其实也是其他程序发送的信号
  • 程序中设定的定时器产生的SIGALRM信号;
  • 系统错误:内存访问越界、除0运算等

linux系统有64种信号,可以通过kill -l查看。这些信号都定义在asm-generic/signal.h头文件中

信号的处理:

  • 由进程发送信号
  • 由内核将信号投递给具体的进程。

记录进程信息的结构体struct task_struct,位于linux/sched.h头文件中

信号的处理方式:

  • 忽略
  • 使用linux默认的处理方式
  • 程序自定义信号处理函数:需注册到内核中,供内核调用
    在这里插入图片描述

发送信号的函数:

  • int kill(pid_t pid, int sig),给指定的进程发送信号;
  • int raise(int sig),给自己发送信号
  • 注意,向其他进程发送信号通常需要相应的权限。通常情况下,只有进程的所有者或者 root 用户才能发送信号给进程

signal/kill/raise/abort函数博客:https://blog.csdn.net/luosuss/article/details/136107989
pause函数博客:https://blog.csdn.net/m0_61629312/article/details/131992361

信号的处理
使用signal()函数:通过该函数,将进程需要自定义处理的信号处理函数注册到内核。当进程收到特定的信号时,调用注册的函数处理信号。
参考博客:https://blog.csdn.net/yockie/article/details/51729774

alarm定时器
alarm函数给进程设置定时器,定时超时后,内核给进程发送SIGALRM信号。

子进程退出异步通知
使用wait等待子进程退出非常消耗资源,并且在阻塞情况下,父进程不能执行其他逻辑。解决方案:

子进程在退出的时候,会给父进程发送SIGCHLD信号,因此父进程可以捕获SIGCHLD来异步等待子进程退出

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/wait.h>


void chld_sig_handler(int sig)
{
    printf("receive child signal: %s\n", strsignal(sig));
    wait(NULL);
    exit(0);
}

int main(int argc, char **argv)
{
    __sighandler_t ret = signal(SIGCHLD, chld_sig_handler);
    if (ret == SIG_ERR) {
        perror("signal");
        exit(EXIT_FAILURE);
    }

    pid_t pid = fork();
    if (pid == 0) {
        sleep(2);
        printf("child exit\n");
        exit(EXIT_SUCCESS);
    } else if (pid > 0){
        while (1);
    } else {
        perror("fork error");
        exit(EXIT_FAILURE);
    }
    return 0;
}

2.1.3 消息队列
2.1.4 共享内存

查询内存页:getconf PAGE_SIZE

应用场景:

  • 进程间共享:经常碰到一种场景,进程需要加载一份配置文件,可能这个文件有100K大,那如果这台机器上多个进程都要加载这份配置文件时,比如有200个进程,那内存开销合计为20M,但如果文件更多或者进程数更多时,这种对内存的消耗就是一种严重的浪费。比较好的解决办法是,由一个进程负责把配置文件加载到共享内存中,然后所有需要这份配置的进程只要使用这个共享内存即可。
  • 生产者消费者模式
  • 父子进程间通讯

共享内存是将分配的物理空间直接映射到进程的用户虚拟地址空间中, 减少数据在内核空间缓存。注意,共享内存是在用户空间,不用拷贝到内核空间。所以,共享内存是一种效率较高的进程间通讯的方式。

博客:https://cloud.tencent.com/developer/article/1551288
博客:https://help.aliyun.com/zh/ecs/user-guide/smc-instructions

3 线程

在 Linux 系统有很多命令可以查看进程,包括 pidstat 、top 、ps ,可以查看进程,也可以查看一个进程下的线程

  • pidstat: -t 显示指定进程所关联的线程; -p 指定 进程 pid:pidstat -t -p 132
  • top 命令查看某一个进程下的线程,需要用到 -H 选项在结合 -p 指定 pid:top -H -p 132
  • ps 命令结合 -T 选项就可以查看某个进程下所有线程:ps -T -p 132

线程间通信

  • 进程间通信的方法也适用于线程间通信,如:管道、消息队列、共享内存、信号、信号量等
  • 父线程参数传给子线程:利用pthread_create的第四个参数
  • 子线程给父线程传参:利用pthread_exit + pthread_join.

4 IO多路复用

4.1 非阻塞IO

使用fcntl()函数,实现非阻塞IO:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>

int main()
{
    int flag = fcntl(0, F_GETFL);
    flag |= O_NONBLOCK;
    if (fcntl(0, F_SETFL, flag) == -1) {
        perror("fcntl");
        exit(1);
    }
    char buf[1024] = "\0";
    while (scanf("%[^\n]+", buf) <= 0) {
        printf("please enter >>\n");
        sleep(2);
    }
    
    printf("inputs:%s\n", buf);

    return 0;
}

4.2 IO多路复用

三种方案:select/poll/epoll

用户进程将需要监控的一组文件描述符集合,拷贝到内核;内核检测文件描述符是否就绪,用户进程被阻塞;
如果内核检测到有就绪的文件描述符,则通知用户进程;用户进程收到通知处理IO

select会将已经就绪的文件描述符直接复制到用户空间传入的文件描述符集合中

  • 因此在非阻塞循环中,用于select的文件描述符集合要做备份处理;
  • timeout结构体的值在select返回前,也会被减去消耗的时间,所以timeout也要备份;设置超时时间,当timeout为NULL时,无限期阻塞
  • 要判断是不是目标文件描述符在不在集合中,因为返回的可能是其他文件描述符

select底层原理
1、文件描述符集合:

  • 阅读源码可知,文件描述符集合fd_set实际上是一个long int __fd_mask[16]类型的数组。在64位的系统中long int类型为8位二进制,所以一个文件描述符集合最多存储1024个文件描述符。
  • fd_set并不是真的存储每一个文件描述符数字,而是使用位图存储来表示文件描述符的就绪状态;位图中的每一位代表一个文件描述符。
  • 调用select的时候,select遍历位图,将就绪的文件描述符在位图中的对应位置为就绪状态。
  • select在内核空间有三个结果集合res_in/res_out/res_exp,select在遍历用户空间拷贝的in/out/exp集合时,如果对应的文件描述符就绪,则将结果集合中对应的文件描述符置为就绪状态。最后将结果集合拷贝到用户空间。
  • 由上可见:select的缺点是遍历、拷贝浪费开销。

poll底层原理

  • 与select相比,poll底层使用链表保存文件描述符,没有文件描述符数量的限制。
  • poll原理博客:https://www.cnblogs.com/luof-man/articles/18274834

epoll底层原理

  • 底层使用红黑树保存被检测的文件描述符;使用callback机制通知应用层文件描述符就绪,而不是轮询,提高了效率。
  • epoll支撑边缘触发(edge-triggered)水平触发(level-trggered)两种模式
  • 边缘触发与水平触发博客:https://zhuanlan.zhihu.com/p/363353777、https://blog.csdn.net/albertsh/article/details/123958013
  • 水平触发会再次进行通知,而边缘触发不会再进行通知。所以,边缘触发需要一次性的把缓冲区的数据读完为止,也就是一直读,直到读到EGAIN为止,EGAIN说明缓冲区已经空了,因为这一点,边缘触发需要设置文件描述符为非阻塞。
//水平触发
ret = read(fd, buf, sizeof(buf));

//边缘触发:一次性读完,直到读到EGAIN为止
while(true) {
    ret = read(fd, buf, sizeof(buf);
    if (ret == EAGAIN) break;
}

// 边缘触发:设置文件描述符非阻塞
#include <fcntl.h>
setnonblocking(fd);

项目:基于多进程/线程的发布与订阅系统

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

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

相关文章

如何使用ssm实现学生公寓管理系统的设计与实现

TOC ssm106学生公寓管理系统的设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;…

LeetCode.3146.两个字符串的排列差

题目描述&#xff1a; 给你两个字符串 s 和 t&#xff0c;每个字符串中的字符都不重复&#xff0c;且 t 是 s 的一个排列。 排列差 定义为 s 和 t 中每个字符在两个字符串中位置的绝对差值之和。 返回 s 和 t 之间的 排列差 输入输出示例&#xff1a; 思路一&#xff1…

TMC2209模块开启无限位归零

TMC2209无限位归零配置步骤&#xff1a; 3.1 配置内部采样电阻。具体在GCONF中internal_Rsens 1&#xff1b; 此步发送数据 05 00 80 00 00 00 83 00 3.2 电机电流设置&#xff0c;配置IHOLD_RUN寄存器&#xff1b; 此处发送数据 05 00 90 00 00 16 16 12 3.3 设置失速电流阈…

QT接收并解析GPS模块串口数据

目录 一、QT读取串口数据 二、解析数据 目标&#xff1a; 使用QT&#xff0c;读取gps模块的串口数据&#xff0c;并解析其中的经纬高数据&#xff0c;然后进行处理 一、QT读取串口数据 变量定义 QSerialPort *serial; QSerialPortInfo SerialPortInfo; QByteArray lineData…

Deepin【2】:Deepin系统盘扩容

Deepin【2】&#xff1a;Deepin系统盘扩容 1、进入live系统1.1、live系统入步骤 2、连接网络3、新增系统仓库4、安装gparted应用5、使用gparted进行扩容操作5.1、观察当前分区5.2、压缩data分区5.3、Rootb分区合并空闲空间5.4、Rootb分区压缩空间5.5、Roota合并空闲空间5.6、核…

【学习笔记】STM32F407探索者HAL库开发(二)STM32F4最小系统设计

【学习笔记】STM32F407探索者HAL库开发&#xff08;二&#xff09;STM32F4最小系统设计 0 什么是最小系统1 电源电路1.1 数字部分电源1.2 模拟部分电源1.3 参考电压1.4 VBAT 2 复位电路3 BOOT启动电路&#xff08;F4&#xff09;4 晶振电路4.1 低速晶振4.2 高速晶振 5 下载调试…

计算机操作员试题(公共科目)

计算机操作员试题(公共科目) ★★★ RZer整理 仅供参考 ★★★ 单项选择题 “人的本质”这一概念是人对自身的认识发展到一定阶段形成的,它力图从根本上回答“人是什么”或“什么是人”这一重大问题。马克思主义认为,人的本质( )。 A.永恒不变 B.可随主观意志而任意改变 C…

【Material-UI】Radio Group中的 Color 属性详解

文章目录 一、Radio Group 组件概述1. 组件介绍2. 基本用法 二、Color 属性详解1. Color 属性的作用2. 使用 Color 属性设置颜色3. 自定义颜色 三、Color 属性的实际应用场景1. 品牌一致性2. 状态指示3. 突出特定选项 四、注意事项1. 色彩对比2. 无障碍设计3. 主题定制 五、总结…

electron仿微信,点击子窗口以外的区域时、关闭子窗口

环境 windows&#xff1a;PC微信 我们可以看到&#xff0c;微信聊天界面点击右上角三个点时&#xff0c;会显示这个人的聊天信息窗口&#xff0c;我们把鼠标点击在其他位置时&#xff0c;这个聊天信息窗口就消失了&#xff0c;那么&#xff0c;这是怎么实现的呢&#xff0c;能…

Git的使用教程及常用语法02

四.将文件添加到仓库 创建仓库 git init查看仓库的状态 git status 添加到暂存区 git add提交 git commitgit status 可以查看当前仓库的状态信息&#xff0c;例如包含哪些分支&#xff0c;有哪些文件以及这些文件当前处在怎样的一个状态。 由于当前没有存储任何的东西&…

深度解析丨AI绘画StableDiffusion如何实现模特精准换装,以及如何将假人模特替换成真人模特!

大家好&#xff0c;我是灵魂画师向阳 今天给大家分享一下用AI绘画Stable Diffusion 进行模特换装&#xff01;本篇教程主要运用StableDiffusion这个工具来进行操作&#xff0c;下面会通过几个小案例&#xff0c;给大家展示不同需求下&#xff0c;我们该如何使用StableDiffusio…

Linux查看jvm相关参数以及设置调优参数

1、查看jvm相关参数 1.1、查看Java进程命令 jcmd会显示出来对应的Java进程id 1.2、查看堆内存各个区域的使用大小、具体大小和GC次数以及耗时 jstat -gc 4036145jstat -gc 进程id 各个参数的含义。 如上图,4036145的进程,FGCT是0.452,FGC是4.平均每次full gc耗时0.11秒。 …

如何选择行为审计管理软件?值得收藏的三款行为审计管理软件

在选择行为审计管理软件时&#xff0c;企业需要考虑多个方面以确保所选软件能够满足其特定的需求和目标。以下是一些关键的选择标准&#xff0c;以及基于这些标准推荐的三款优秀软件。 选择行为审计管理软件的关键标准 1.功能全面性&#xff1a;软件应覆盖行为审计的各个方面&…

Godot《躲避小兵》实战之为游戏添加音效

现在&#xff0c;我们已经完成了游戏的所有功能。以下是一些剩余的步骤&#xff0c;为游戏加点“料”&#xff0c;改善游戏体验。 随意用你自己的想法扩展游戏玩法。 背景 默认的灰色背景不是很吸引人&#xff0c;那么我们就来改一下颜色。一种方法是使用 ColorRect节点。将…

JavaScript学习文档(6):什么是对象、对象使用、遍历对象、内置对象、术语解释

目录 一、什么是对象 1、对象是什么 二、对象使用 1、对象使用 2、对象有属性和方法组成 &#xff08;1&#xff09;对象属性&#xff08;增删改查&#xff09; &#xff08;2&#xff09;对象方法 三、遍历对象 1、遍历对象 2、渲染学生信息案例 四、内置对象 1、…

旅游行业怎么利用C#接口发送短信

旅游企业一般拥有众多的分支机构&#xff0c;同时各地分支机构又有众多下属分散在当地各区的旅游营业报名点&#xff0c;以前传统的解决方案是采用专线、MODEM拔号等方式&#xff0c;专线的成本很高&#xff0c;MODEM拔号更费时&#xff0c;且长途拔号互联成本在多点情况下费用…

scikit-learn特征抽取

为什么需要特征工程 数据和特征决定了机器学习的上限&#xff0c;而模型和算法只是逼近这个上限而已 什么是特征工程 特征工程是使用专业背景知识和技巧处理数据&#xff0c;使得特征能在机器学习算法上发挥更好的作用的过程 意义&#xff1a;会直接影响机器学习的效果 特征…

Type-C无线麦克风方案

在数字化浪潮的推动下&#xff0c;音频设备正经历着前所未有的变革。从传统的有线麦克风到如今的蓝牙无线麦克风&#xff0c;每一次技术的飞跃都极大地丰富了我们的音频体验。而今&#xff0c;随着Type-C接口的普及与技术的不断成熟&#xff0c;Type-C无线麦克风正悄然成为音频…

数据结构----红黑树

小编会一直更新数据结构相关方面的知识&#xff0c;使用的语言是Java&#xff0c;但是其中的逻辑和思路并不影响&#xff0c;如果感兴趣可以关注合集。 希望大家看完之后可以自己去手敲实现一遍&#xff0c;同时在最后我也列出一些基本和经典的题目&#xff0c;可以尝试做一下。…

DRF——Filter条件搜索模块

文章目录 条件搜索自定义Filter第三方Filter内置Filter 条件搜索 如果某个API需要传递一些条件进行搜索&#xff0c;其实就在是URL后面通过GET传参即可&#xff0c;例如&#xff1a; /api/users?age19&category12在drf中也有相应组件可以支持条件搜索。 自定义Filter …