IPC之十四:使用libdbus通过select()接收D-Bus消息的实例

在『进程间通信』系列文章中前面已经有三篇关于D-Bus的文章,本文继续讨论D-Bus;libdbus抽象了实现IPC时实际使用的方式(管道、socket等),libdbus允许在一个D-Bus连接上添加一个watch,通过watch对实际IPC中使用的文件描述符进行监视,本文讨论了如何在D-Bus连接中添加watch,如何使用在socket编程中常用的select从D-Bus返回的文件描述符中接收到D-Bus消息,本文给出了具体实例,附有完整的源代码;本文实例在 Ubuntu 20.04 上编译测试通过,gcc版本号为:9.4.0;本文不适合 Linux 编程的初学者阅读。

1 基本概念

  • 阅读本文前,建议先阅读前面的关于 D-Bus 的三篇文章:《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》、《IPC之十二:使用libdbus在D-Bus上异步发送/接收信号的实例》 和 《IPC之十三:使用libdbus通过D-Bus请求系统调用实现域名解析的实例》
  • 在文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中介绍了 D-Bus 的一些基本概念,介绍了 libdbus 库的一些基本 API,在实例中使用 libdbus 库的 APIs 实现了客户进程向服务进程请求服务的过程;
  • 在文章 《IPC之十二:使用libdbus在D-Bus上异步发送/接收信号的实例》 中,我们介绍了 D-Bus 中信号的概念,在实例中使用 libdbus 库的 match 订阅了信号,并使用过滤器(filter)异步接收信号;
  • 在文章 《IPC之十三:使用libdbus通过D-Bus请求系统调用实现域名解析的实例》 中,我们介绍了如何通过 D-Bus 调用系统服务,有很多系统服务都有 D-Bus 接口,透过这个接口,应用程序可以调用系统服务,从而快速地实现一些复杂的功能;
  • 在『进程间通信』系列文章中的前十篇文章中,介绍了许多 IPC 的方法,有管道、共享内存、UNIX DOMAIN SOCKET 等,其实 D-Bus 在实现 IPC 时也是用其中的一种方法实现的,但是 libdbus 将具体的实现抽象化了,所以,我们并不知道具体用的那种方法;
  • D-Bus 通过 dbus-daemon 来管理总线,服务进程通过一种传统 IPC 方法与 dbus-daemon 进行通信,客户进程可能通过另一种 IPC 方式与 dbus-daemon 进行通信,dbus-daemon 对客户进程与服务进程之间的消息进行转发;
  • 实际上,我们也确实无需知道具体的实现方法,在 Linux 系统下,有所谓"一切皆文件"的原则,不管是管道还是 socket,最终返回给应用程序的都是一个文件描述符(File descriptor);
  • 在进行 socket 编程时,服务端在处理多个连接请求时,常常会使用 select(),将正在侦听的多个 socket 的文件描述符放到一个’集’中,当’集’中的某个文件描述符有可读数据时,select() 就会返回,然后应用程序就可以对相应的文件描述符做出处理;
  • 如果在 D-Bus 编程中也能通过 select() 来处理消息的话,将给编程带来很大方便,当应用程序中要处理 D-Bus 消息、socket 通信以及文件读写时,使用 select() 可以合并处理,这将大大提高程序的效率;

2 在D-Bus中与 watch 相关的函数介绍

  • 添加 watch 的目的就是为了监视实际实现 IPC 的文件描述符,watch 添加成功才可以获取实际使用的文件描述符,从而实现在文件描述符一级的监视;

  • libdbus 库的 API 调用手册:D-Bus low-level public API

  • 本节并不会介绍所有与 watch 相关的函数,仅介绍在本文实例中用到的函数;

  • 使用 dbus_bus_get() 连接到 dbus-daemon,使用 dbus_bus_request_name() 获取总线名称都在以前的文章中做过介绍;

  • 函数 dbus_connection_set_watch_functions()

    • 设置 watch 相关的函数
    dbus_bool_t dbus_connection_set_watch_functions(DBusConnection *connection,
                                                    DBusAddWatchFunction add_function,
                                                    DBusRemoveWatchFunction remove_function,
                                                    DBusWatchToggledFunction toggled_function,
                                                    void *data,
                                                    DBusFreeFunction free_data_function)	    
    
    • connection:通过 dbus_bus_get() 获取的连接
    • add_function:添加 watch 时执行的函数
    • remove_function:删除 watch 时执行的函数
    • toggled_function:切换 watch 时执行的函数
    • data:传递给 add_function()remove_function()toggled_function() 的用户参数;
    • free_data_function:释放 data 的函数;

  • 函数 DBusAddWatchFunction add_function

    • DBusAddWatchFunction 的定义为:typedef dbus_bool_t(* DBusAddWatchFunction) (DBusWatch *watch, void *data)

    • 所以 add_function() 函数的格式应该为:

      dbus_bool_t add_function(DBusWatch *watch, void *data) {
          ........
      }
      
    • 当调用 dbus_connection_set_watch_functions() 时,add_function() 将被自动调用,watch 将由系统传递给函数,data 是传递给 add_function() 的用户数据,在调用 dbus_connection_set_watch_functions() 时由参数 data 指定;

    • watch 的数据类型是 DBusWatch,实际上就是一个结构,C 语言不是一个面向对象的语言,当要实现对象时,通常都是使用数据结构,一般情况下,无需了解其数据结构的具体情况;

    • 系统传入的 watch 要保存好,因为在应用程序的其它地方可能需要它;

    • 用户传入的 data 可以是任何变量或者结构,在实例中会有具体的做法;

    • add_function() 中通常需要判断 watch 是否被启用,然后通过调用 dbus_watch_get_unix_fd() 获取被监视的文件描述符;

    • add_function() 返回 FALSE 时表示内存不够,所以通常情况下即便出现错误也不要返回 FALSE;


  • 函数 DBusRemoveWatchFunction remove_function

    • DBusRemoveWatchFunction 的定义为:typedef void(* DBusRemoveWatchFunction) (DBusWatch *watch, void *data)

    • 所以 remove_function() 函数的格式应该为:

      void remove_function(DBusWatch *watch, void *data) {
          ........
      }
      
    • 在一个 watch 被禁用时,通常需要调用 remove_function(),一般情况下,当一个 watch 被禁用时系统会调用 toggled_function(),然后在 toggled_function() 中调用 remove_funcion()

    • data 参数与 add_function() 中的 data 参数一样,在设置时指定;


  • 函数 DBusWatchToggledFunction toggled_function

    • DBusWatchToggledFunction 的定义为:typedef void(* DBusWatchToggledFunction) (DBusWatch *watch, void *data)

    • 所以 toggled_function() 函数的格式应该为:

      void toggled_function(DBusWatch *watch, void *data) {
          ........
      }
      
    • 当 watch 被启用或者禁用时,系统通过调用 toggled_function() 来通知应用程序,系统将发生变化的 watch 作为参数传给 toggled_function()

    • data 参数与 add_function() 中的 data 参数一样,在设置时指定;

    • toggled_function() 中,通常需要通过 dbus_watch_get_enabled() 判断 watch 是启用还是禁用,如果是启用,调用 add_function(),如果是禁用则调用 remove_function()


  • 函数 dbus_watch_get_enabled()

    • 检查 watch 是否已启用
    dbus_bool_t dbus_watch_get_enabled(DBusWatch *watch);	
    
    • 返回 TRUE 表示 watch 已经启用,FALSE 表示 watch 已经禁用;

  • 函数 dbus_watch_get_unix_fd()

    int dbus_watch_get_unix_fd(DBusWatch *watch);	
    
    • 返回 watch 监视的文件描述符,这个描述符可能是管道、socket,也可能是其它类型的描述符;

  • 函数 dbus_watch_get_flags()

    unsigned int dbus_watch_get_flags(DBusWatch *watch);
    
    • 获取 watch 对文件描述符的监视内容;
    • 返回值可能有两个标志:DBUS_WATCH_READABLEDBUS_WATCH_WRITABLE
    • DBUS_WATCH_READABLE 表示对文件描述符的’读’进行监视;
    • DBUS_WATCH_WRITABLE 表示对文件描述符的’写’进行监视;
    • 默认 watch 会监视文件描述符的错误状态(DBUS_WATCH_ERROR)和挂起状态(DBUS_WATCH_HANGUP),所以这两个标志不会在这个函数的返回值中出现;

  • 函数 dbus_watch_handle()

    dbus_bool_t dbus_watch_handle(DBusWatch *watch,
                                  unsigned int flags);	
    
    • 当 watch 所监视的文件描述符有可读取的数据时,需要调用此函数通知 libdbus 库;
    • 当被监视的文件描述符中有数据可以读取时,调用函数 dbus_watch_handle() 将把数据从从文件描述符中读出,解析为 D-Bus 的消息,并把消息放入消息队列,然后才可以用 libdbus 库的 API 从队列中读出消息并进行处理,如果不调用 dbus_watch_handle(),则文件描述符上将一直处于有数据可读状态;
    • dbus_watch_handle() 返回为 FALSE 时表示执行失败,执行失败的原因只有一个,就是内存不够,如果忽略返回的 FALSE,通常不会有致命问题,直至有足够内存时,dbus_watch_handle() 便会返回 TRUE;
    • 参数 watch 为使用 dbus_connection_set_watch_functions() 设置的 watch,也就是系统在调用 add_function() 时传递过来的 watch;
    • 参数 flags 与函数 dbus_watch_get_flags() 返回的值含义相同,DBUS_WATCH_READABLE 表示文件描述符中有可读数据,DBUS_WATCH_WRITABLE 表示文件描述符中有可写数据;

  • 函数 dbus_connection_get_dispatch_status()

    DBusDispatchStatus dbus_connection_get_dispatch_status(DBusConnection *connection);	
    
    • 获取接收消息队列的当前状态;
    • 其返回值 DBusDispatchStatus 是一个枚举类型,其值有三个:
      1. DBUS_DISPATCH_DATA_REMAINS:表示接收消息队列中可能有消息;
      2. DBUS_DISPATCH_COMPLETE:表示接收消息队列为空;
      3. DBUS_DISPATCH_NEED_MEMORY:表示可能有消息,但如果没有更多内存,则无法确定是否有消息;
    • 当返回状态为 DBUS_DISPATCH_DATA_REMAINS 时,表示接收队列中有消息,或者正在将原始数据转换为 D-Bus 的消息,此时会返回 DBUS_DISPATCH_DATA_REMAINS 状态,但由于消息还没有解析完毕,所以队列中还没有完整的消息;

  • 函数 dbus_connection_borrow_message()

    DBusMessage *dbus_connection_borrow_message(DBusConnection *connection);	
    
    • 这个函数很有意思,它从消息队列中"借用"一个消息,"借用"消息和读取消息的区别在于"借用"的消息不会从消息队列中删除,而读取的消息会从消息队列中删除;
    • 当一个消息被"借用"以后,这条消息尽管还在队列中,但会被锁定,其它进程(线程)将无法再读取该消息,所以"借用"的消息要么尽快"还"回来,要么尽快读取出来,否则会影响消息的处理;
    • 之所以要"借用"消息,是因为不能确定这条消息是否应该由当前程序处理,所以要"借用"出来做一下判断,如果不该当前程序处理,则立即"还"回去,否则应该立即将该消息读出来;

  • 函数 dbus_connection_return_message()

    void dbus_connection_return_message(DBusConnection *connection,
                                        DBusMessage *message);
    
    • 该函数将由函数 dbus_connection_borrow_message() "借用"的消息"还"回到消息队列中;
    • 参数 message 是要"还"回去的消息;

  • 函数 dbus_connection_steal_borrowed_message()

    void dbus_connection_steal_borrowed_message(DBusConnection *connection,
                                                DBusMessage *message);
    
    • 该函数将由函数 dbus_connection_borrow_message() "借用"的消息从消息队列中读取出来;
    • 参数 message 为要读出的消息,该函数执行完成后,该消息将从消息队列中被删除;

  • 其它在实例中用到的函数已经在前面几篇关于 D-Bus 的文章中做过介绍,这里不再赘述;

3 使用 select() 处理 D-Bus 消息的步骤

  • 使用 dbus_bus_get() 连接到 dbus-daemon,使用会话总线;

  • 使用 dbus_bus_request_name() 设置总线名称,以便客户端可以访问到;

  • 使用 dbus_connection_set_watch_functions() 添加一个 watch,考虑将一个结构传给设置的 watch 函数,用于存储 watch 的相关信息;

    struct watch_struct {
        DBusWatch *watched_watch;   // Currently used watch
        int watched_rd_fd;          // Readable file descriptor being watched
        int watched_wr_fd;          // Writable file descriptor being watched
    }watch_fds;
    
    dbus_connection_set_watch_functions(conn, 
                                        add_watch, 
                                        remove_watch,
                                        toggle_watch,
                                        (void *)&watch_fds, NULL))
    
  • 为添加 watch 编写三个函数:add_watch()remove_watch()toggle_watch()

    add_wath() 中,首先使用 dbus_watch_get_enabled() 判断添加的 watch 是否已经启用,如未启用则无法继续,然后使用 dbus_watch_get_unix_fd() 获取被监视的文件描述符,使用 dbus_watch_get_flags() 判断被监视的这个描述符是可读还是可写,并保存好相关信息;

    static dbus_bool_t add_watch(DBusWatch *w, void *data) {
        if (!dbus_watch_get_enabled(w)) {               // Returns whether a watch is enabled or not.
            return TRUE;
        }
    
        struct watch_struct *watch_fds = (struct watch_struct *)data;
        int fd = dbus_watch_get_unix_fd(w);             // Returns a UNIX file descriptor to be watched, 
                                                        // which may be a pipe, socket, or other type of descriptor.
        unsigned int flags = dbus_watch_get_flags(w);   // Gets flags from DBusWatchFlags indicating what conditions 
                                                        // should be monitored on the file descriptor.
        if (flags & DBUS_WATCH_READABLE) {              // the watch is readable
            watch_fds->watched_rd_fd = fd;
        }
        if (flags & DBUS_WATCH_WRITABLE) {              // the watch is writable
            watch_fds->watched_wr_fd = fd;
        }
        watch_fds->watched_watch = w;
    
        return TRUE;
    }
    

    remove_watch() 的主要作用就是在禁用 watch 时可以释放在执行 add_watch() 时所占用的资源;

    static void remove_watch(DBusWatch *w, void *data) {
        struct watch_struct *watch_fds = (struct watch_struct *)data;
    
        watch_fds->watched_watch = NULL;
        watch_fds->watched_rd_fd = 0;
        watch_fds->watched_wr_fd = 0;
    }
    

    toggle_watch() 首先使用 dbus_watch_get_enabled() 判断系统传过来的 watch 时禁用还是启用状态,如果是禁用状态,调用 remove_watch(),如果是启用状态,调用 add_watch()

    static void toggle_watch(DBusWatch *w, void *data) {
        if (dbus_watch_get_enabled(w)) {
            add_watch(w, data);
        } else {
            remove_watch(w, data);
        }
    }
    
  • 初始化 select() 的文件描述符集,设置 select() 超时时间,执行 select()

    fd_set readfds;            // Reading fd set, Writing fd set and ... fd set
    int max_fd = 0;
    int activity;
    
    FD_ZERO(&readfds);
    if ((watch_fds.watched_watch != NULL) && (watch_fds.watched_rd_fd > 0)) {
        FD_SET(watch_fds.watched_rd_fd, &readfds);      // set watched FD into fd set
        max_fd = watch_fds.watched_rd_fd;
    } else {
        exit(EXIT_FAILURE);
    }
    struct timeval timeoutval = {5, 0};     // timeout is 5 seconds
    activity = select(max_fd + 1, &readfds, NULL, NULL, &timeoutval);
    
  • 文件描述符上有可读数据时,调用 dbus_watch_handle() 和消息接收函数 select_recv()

    if (FD_ISSET(watch_fds.watched_rd_fd, &readfds)) {
        if (dbus_watch_handle(watch_fds.watched_watch, DBUS_WATCH_READABLE)) {
            select_recv(conn);
        }
    }
    
    • 调用 dbus_watch_handle() 的目的是把文件描述符上的数据读出并解析为 D-Bus 消息放入消息队列;
    • 如果 dbus_watch_handle() 返回 FALSE 表示需要更多的内存才能解析数据,此时文件描述符仍然处于有可读数据的状态,所以下次执行 select() 仍可以处理数据,不会有灾难后果,也可以在 dbus_watch_handle() 返回 FALSE 后做一个短延时(比如100ms);
  • 接收消息程序 select_recv()

    • 这个程序是需要自己编写的,其目的就是接收、处理消息并向客户端发送回复消息;
    • 首先使用 dbus_connection_get_dispatch_status() 检查状态,如果不是 DBUS_DISPATCH_DATA_REMAINS 表示消息队列中没有消息,则不能继续进行;
    • 这个接收函数的通常做法是先使用 dbus_connection_borrow_message() 借用消息,然后根据其消息类型、对象路径、接口名称等将消息分发给其它函数,需要丢弃的消息则将其读出直接丢弃;
    • 本文实例中仅留下了需要处理的消息,实际上丢弃了所有其它的消息;
    • 在这个函数中要处理到所有消息类型的消息,自行处理、分发到其他函数处理或者丢弃,不处理的话无法处理消息队列中的下一条消息;

3 实例

  • 源程序:dbus-select.c (点击文件名下载源程序,建议使用UTF-8字符集)演示了使用 libdbus 通过 select() 接收方法调用消息的过程;

  • 该程序与文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中的实例 dbus-methods.c 完成相同的任务,其中客户端进程的程序完全一样;

  • 与 dbus-methods.c 不同的是服务端进程,dbu-select.c 在服务端进程为 D-Bus 连接添加了一个 watch,使用 dbus_watch_get_unix_fd() 从 watch 中获取实际使用的文件描述符;

  • 当文件描述符上有数据可以读取时,调用接收程序读取消息并处理消息,然后向客户端发出回复消息;

  • 该程序是个多进程程序,建立了一个服务端进程和若干个(默认为 3 个)客户端进程,服务端进程执行的函数为:dbus-server(),客户端进程执行的函数为:dbus-client()

  • 客户端进程向服务端进程的三个方法分别发出请求,服务端进程一一做出回复,客户端进程在调用完 quit 方法并收到服务端回复后主动结束进程;

  • 服务端进程在收到所有客户端进程发来的对 quit 方法的请求消息并做出回复后主动退出进程;

  • 编译:gcc -Wall -g dbus-select.c -o dbus-select `pkg-config --libs --cflags dbus-1`

  • 有关 pkg-config --libs --cflags dbus-1 可以参阅文章 《IPC之十一:使用D-Bus实现客户端向服务端请求服务的实例》 中的简要说明;

  • 运行:./dbus-select

  • 运行截图:

    Screenshot of dbus-select

欢迎订阅 『进程间通信专栏』


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL之导入、导出

目录 一.navicat导入导出二.mysqldump命令导入导出2.1 导出2.2 导入 三.LOAD DATA INFILE命令导入导出四.远程备份五.思维导图 一.navicat导入导出 使用Navicat工具导入t_log 共耗时 45s; 二.mysqldump命令导入导出 2.1 导出 导出表数据和表结构: 语法&#xff…

IntelliJ IDEA如何使用固定地址公网远程访问本地Mysql数据库

文章目录 1. 本地连接测试2. Windows安装Cpolar3. 配置Mysql公网地址4. IDEA远程连接Mysql小结 5. 固定连接公网地址6. 固定地址连接测试 IDEA作为Java开发最主力的工具,在开发过程中需要经常用到数据库,如Mysql数据库,但是在IDEA中只能连接本…

转专业(UPC练习)

题目描述 根据教育部的规定,大学生进校后符合条件的可申请转专业。在校本科生在完成大学一年级课程,进入二年级之前,符合以下条件之一者,可以申请转专业:(1)在某一学科方面确有特长的学生&#…

什么是云安全?如何保护云资源

云计算允许组织通过互联网按需向其客户、合作伙伴或员工提供关键业务应用程序、服务和资源。换句话说,不再需要物理维护资源。每当您通过 Internet 从计算机访问文件或服务时,您都是在访问云。 迁移到云可以帮助企业增强安全性、简化运营并降低成本。企…

智数融合|低代码入局,推动工业数字化转型走"深"向"实"

当下,“数字化、智能化”已经不再是新鲜词汇。事实上,早在几年前,就有企业开始大力推动数字化转型,并持续进行了一段时间。一些业内人士甚至认为,“如今的企业数字化已经走过了成熟期,进入了深水区。” 但事…

如何在Linux上部署1Panel面板并远程访问内网Web端管理界面

文章目录 推荐 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳…

python_selenium_安装基础学习

目录 1.为什么使用selenium 2.安装selenium 2.1Chrome浏览器 2.2驱动 2.3下载selenium 2.4测试连接 3.selenium元素定位 3.1根据id来找到对象 3.2根据标签属性的属性值来获取对象 3.3根据xpath语句来获取对象 3.4根据标签的名字获取对象 3.5使用bs4的语法来获取对象…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷①

2023年全国职业院校技能大赛(高职组) “云计算应用”赛项赛卷1 目录 需要竞赛软件包环境以及备赛资源可私信博主!!! 2023年全国职业院校技能大赛(高职组) “云计算应用”赛项赛卷1 模块一 …

【数据库原理】期末突击(1)

有不会的题可以后台问我的哦,看见了就会回。 本文章主要是选择题、填空题,下章将更新综合题,祝大家期末心想事成。 一、选择题 下列关系运算中,( C )运算不属于专门的关系运算。 A.选择 …

随机输一次

大家应该都会玩“锤子剪刀布”的游戏:两人同时给出手势,胜负规则如图所示: 现要求你编写一个控制赢面的程序,根据对方的出招,给出对应的赢招。但是!为了不让对方意识到你在控制结果,你需要隔 K …

npm run dev,vite 配置 ip 访问

启动项目通过本地 ip 的方式访问 方式一.通过修改 package.json "scripts": {"dev": "vite --host 0.0.0.0",}, 方式二.通过修改 vite.config.ts export default defineConfig({plugins: [vue(), vueJsx()],server: { // 配置 host 与 port 方…

ERD助力研发资产沉淀研发提效

一、从痛点中思考答案 痛点一:复杂系统的设计和逻辑碎片化散落,缺少沉淀导致系统后期维护、迭代以及架构升级都非常困难。 痛点二:由于新需求或新项目导致的系统的老旧逻辑梳理往往耗费大量人力,甚至造成人才的流失。 痛点三&a…

双指针(简化哈希)力扣15.三数之和

题目 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组。 …

ADS仿真 之 瞬态仿真

瞬态仿真常用于低频模拟和数字电路的仿真,是用来模拟电压或者电流随时间的变化趋势, ADS在Simulation-transient面板中提供了与瞬态仿真相关的控件, 主要是瞬态仿真控件,一般的瞬态仿真主要关注时间的设置和时间的控制方式&#x…

编码技巧(二) element-ui table中根据状态控制是否可以勾选

项目中使用element-ui时,表格中的数据有不同的状态,需要对某个状态的数据进行 勾选操作 如图所示: 只有id为12的符合条件可以进行勾选 <el-table-column type="selection" header-align="center" :selectable="selectable" align="c…

1.4.1机器学习——梯度下降+α学习率大小判定

1.4.1梯度下降 4.1、梯度下降的概念 ※【总结一句话】&#xff1a;系统通过自动的调节参数w和b的值&#xff0c;得到最小的损失函数值J。 如下&#xff1a;是梯度下降的概念图。 我们有一个损失函数 J(w,b)&#xff0c;包含两个参数w和b&#xff08;你可以想象成J(w,b) w*x…

竞赛保研 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

GCF:在线市场异质治疗效果估计的广义因果森林

英文题目&#xff1a;GCF: Generalized Causal Forest for Heterogeneous Treatment Effects Estimation in Online Marketplace 中文题目&#xff1a;GCF&#xff1a;在线市场异质治疗效果估计的广义因果森林 单位&#xff1a;滴滴&美团 时间&#xff1a;2022 论文链接…

postgresql迁移到mysql

1.工具方法&#xff1a;Navicat Premium16 2. 手工方法&#xff1a; 迁移流程 下面是将 Postgresql 数据库迁移到 MySQL 的步骤流程&#xff1a; 步骤描述1. 创建MySQL表结构在MySQL中创建与Postgresql中的表结构相同的表2. 导出Postgresql数据将Postgresql中的数据导出为SQ…

【学术会议】第三届神经计算青年研讨会 学习笔记

第三届神经计算青年研讨会 学习笔记 会议时间&#xff1a;2024-1-6至2024-1-7 会议地点&#xff1a;电子科技大学 会议介绍&#xff1a; 为提升我国神经计算⻘年研究队伍的学术⽔平和国际影响⼒&#xff0c;研讨会主题涵盖&#xff1a;神经系统建模与模拟、脑机接⼝与类脑智能、…