当前位置: 首页 > news >正文

sigwaittest测试超标的调试过程

1,问题描述

硬件环境:飞腾S2500(64核)

OS:kylinOS, linux preempt rt, 4.19.90

测试命令:sigwaittest -p 90 -i 1000 -a 1

测试结果:信号混洗值最大超过了80us,与飞腾其他CPU的设备相比较,增大了很多

2,调试过程

1)日志分析

使用trace-cmd命令来抓取ftrace log:trace-cmd start -e all; sigwaittest -p 90 -i 1000 -a 1 -b 80

得到如下日志:

sigwaitt-13091   1....0  1741.963614: sys_enter:            NR 131 (3321, 3322, a, 20c49ba5e353f7cf, 3b9ac9ff, 44cc0)
sigwaitt-13091   1...10  1741.963615: sys_enter_tgkill:     tgid: 0x00003321, pid: 0x00003322, sig: 0x0000000a
sigwaitt-13091   1d..11  1741.963616: sched_waking:         comm=sigwaittest pid=13090 prio=9 target_cpu=001
sigwaitt-13091   1d..21  1741.963617: sched_wakeup:         sigwaittest:13090 [9] success=1 CPU:001
sigwaitt-13091   1....1  1741.963618: signal_generate:      sig=10 errno=0 code=-6 comm=sigwaittest pid=13090 grp=0 res=0
=====chensong:70us
sigwaitt-13091   1d..21  1741.963689: sched_waking:         comm=sigwaittest pid=13089 prio=120 target_cpu=041
sigwaitt-13091   1d..31  1741.963690: sched_wakeup:         sigwaittest:13089 [120] success=1 CPU:041
=====
sigwaitt-13091   1....0  1741.963691: kfree:                (__audit_syscall_exit+0x1d8) call_site=ffff0000081d26b0 ptr=(nil)
sigwaitt-13091   1....0  1741.963691: sys_exit:             NR 131 = 0
sigwaitt-13091   1...10  1741.963692: sys_exit_tgkill:      0x0

对比正常时候的日志:

sigwaitt-13091   1....0  1741.962569: sys_enter:            NR 131 (3321, 3322, a, 20c49ba5e353f7cf, 3b9ac9ff, 44cc0)
sigwaitt-13091   1...10  1741.962570: sys_enter_tgkill:     tgid: 0x00003321, pid: 0x00003322, sig: 0x0000000a
sigwaitt-13091   1d..11  1741.962571: sched_waking:         comm=sigwaittest pid=13090 prio=9 target_cpu=001
sigwaitt-13091   1d..21  1741.962572: sched_wakeup:         sigwaittest:13090 [9] success=1 CPU:001
sigwaitt-13091   1....1  1741.962573: signal_generate:      sig=10 errno=0 code=-6 comm=sigwaittest pid=13090 grp=0 res=0
sigwaitt-13091   1....0  1741.962574: kfree:                (__audit_syscall_exit+0x1d8) call_site=ffff0000081d26b0 ptr=(nil)
sigwaitt-13091   1....0  1741.962574: sys_exit:             NR 131 = 0
sigwaitt-13091   1...10  1741.962574: sys_exit_tgkill:      0x0
sigwaitt-13091   1....0  1741.962575: sys_enter:            NR 137 (fffdf677e848, fffdf677e6b8, 0, 8, 0, fffdf677f0e0)
sigwaitt-13091   1...10  1741.962575: sys_enter_rt_sigtimedwait: uthese: 0xfffdf677e848, uinfo: 0xfffdf677e6b8, uts: 0x00000000, sigsetsize: 0x00000008
sigwaitt-13091   1d..10  1741.962576: rcu_utilization:      Start context switch
sigwaitt-13091   1d..10  1741.962577: rcu_utilization:      End context switch
sigwaitt-13091   1d..20  1741.962578: sched_switch:         sigwaittest:13091 [9] S ==> sigwaittest:13090 [9]

我们会发现,在signal_generate后,sigwaittest的测试线程又唤醒了一个线程,消耗了70us。

2)过程分析

命令“sigwaittest -p 90 -i 1000 -a 1 -b 80”会创建三个线程,一个主线程main thread,,是一个CFS的普通线程,很大一部分工作是打印测试的结果,一个是sender,是一个实时线程,负责发送信号,还有一个是receiver,负责接收信号,sigwaittest主要就是测试sender发送信号到receiver接收到信号所花费的时间。

3)代码分析

首先看一下发送信号的kill函数,对应内核中系统调用tgkill:

使用function_grath来看一下tgkill的调用过程:trace-cmd start -p function_graph -g sys_tgkill;sigwaittest -p 90 -i 1000 -a 1 -l 100;trace-cmd stop

# tracer: function_graph
#
# CPU  DURATION                  FUNCTION CALLS
# |     |   |                     |   |   |   |1)               |  sys_tgkill() {1)               |    do_tkill() {1)               |      __task_pid_nr_ns() {1)   0.770 us    |        __rcu_read_lock();1)   0.500 us    |        __rcu_read_unlock();1)   3.062 us    |      }1)               |      from_kuid_munged() {1)   0.500 us    |        map_id_up();1)   1.563 us    |      }1)               |      do_send_specific() {1)   0.479 us    |        __rcu_read_lock();1)   0.666 us    |        find_task_by_vpid();1)               |        __task_pid_nr_ns() {1)   0.500 us    |          __rcu_read_lock();1)   0.479 us    |          __rcu_read_unlock();1)   2.542 us    |        }1)               |        check_kill_permission() {1)               |          audit_signal_info() {1)               |            auditd_test_task() {1)   0.500 us    |              __rcu_read_lock();1)   0.479 us    |              __rcu_read_unlock();1)   2.604 us    |            }1)               |            audit_signal_info_syscall() {1)   0.500 us    |              __rcu_read_lock();1)   0.500 us    |              __rcu_read_unlock();1)   2.458 us    |            }1)   7.042 us    |          }1)   0.604 us    |          security_task_kill();1)   9.271 us    |        }1)               |        do_send_sig_info() {1)               |          __lock_task_sighand() {1)   0.500 us    |            __rcu_read_lock();1)               |            rt_spin_lock() {1)   0.500 us    |              __rcu_read_lock();1)   0.500 us    |              migrate_disable();1)   2.480 us    |            }1)   0.500 us    |            __rcu_read_unlock();1)   5.459 us    |          }1)               |          send_signal() {1)   0.563 us    |            siginfo_layout();1)   0.479 us    |            __rcu_read_lock();1)   0.500 us    |            __rcu_read_lock();1)   0.500 us    |            __rcu_read_unlock();1)   0.500 us    |            __rcu_read_unlock();1)   0.563 us    |            task_active_pid_ns();1)               |            __task_pid_nr_ns() {1)   0.500 us    |              __rcu_read_lock();1)   0.480 us    |              __rcu_read_unlock();1)   2.479 us    |            }1)               |            __send_signal() {1)   0.583 us    |              prepare_signal();1)               |              __sigqueue_do_alloc() {1)   0.563 us    |                __rcu_read_lock();1)   0.480 us    |                __rcu_read_unlock();1)               |                kmem_cache_alloc() {1)   0.479 us    |                  should_failslab();1)   1.708 us    |                }1)   4.833 us    |              }1)               |              complete_signal() {1)   0.604 us    |                _raw_spin_lock_irqsave();1)   0.500 us    |                _raw_spin_unlock_irqrestore();1)               |                signal_wake_up_state() {1)               |                  wake_up_state() {1)               |                    try_to_wake_up() {1)   0.562 us    |                      _raw_spin_lock_irqsave();1)   0.542 us    |                      _raw_spin_lock();1)   0.541 us    |                      update_rq_clock();1)               |                      ttwu_do_activate() {1)               |                        activate_task() {1)               |                          enqueue_task_rt() {1)               |                            dequeue_rt_stack() {1)   0.521 us    |                              dequeue_top_rt_rq();1)   1.521 us    |                            }1)   0.500 us    |                            update_rt_migration();1)   0.500 us    |                            _raw_spin_lock();1)   0.521 us    |                            enqueue_top_rt_rq();1)   5.604 us    |                          }1)   6.958 us    |                        }1)               |                        ttwu_do_wakeup() {1)               |                          check_preempt_curr() {1)   0.625 us    |                            check_preempt_curr_rt();1)   1.625 us    |                          }1)   0.521 us    |                          task_woken_rt();1)   4.605 us    |                        }1) + 13.084 us   |                      }1)   0.541 us    |                      _raw_spin_unlock_irqrestore();1) + 19.104 us   |                    }1) + 20.146 us   |                  }1) + 21.188 us   |                }1) + 24.542 us   |              }1) + 32.709 us   |            }1) + 42.792 us   |          }1)               |          rt_spin_unlock() {1)   0.563 us    |            migrate_enable();1)   0.521 us    |            __rcu_read_unlock();1)   2.750 us    |          }1) + 53.271 us   |        }1)   0.541 us    |        __rcu_read_unlock();1) + 70.500 us   |      }1) + 77.562 us   |    }1) + 81.333 us   |  }------------------------------------------1) sigwait-31931  => sigwait-31930 ------------------------------------------

简化一下调用过程就是:

tgkill--> do_tkill-->do_send_specific-->do_send_sig_info--> lock_task_sighand //申请锁tsk->sighand->siglocksend_signal //唤醒receiverunlock_task_sighand(p, &flags); //释放tsk->sighand->siglock

在通常情况下,sender调用send_signal唤醒receiver,这个过程就结束了。但在发生错误的日志中,我们发现sender还唤醒了main thread,那么,很可能main thread也在申请tsk->sighand->siglock,这个时候它正在siglock的等待队列中等待,那么,当sender调用unlock_task_sighand的时候,就会去唤醒main thread。

我们再来看看main thread的代码:

26     while (!mustshutdown) {
527         int printed;
528         int errorlines = 0;
529 
530         for (i = 0; i < num_threads; i++)
531             mustshutdown |= receiver[i].shutdown |
532                 sender[i].shutdown;
533 
534         if (receiver[0].samples > oldsamples || mustshutdown) {...//打印结果581         pthread_sigmask(SIG_SETMASK, &sigset, NULL);
582 
583         nanosleep(&maindelay, NULL);
584 
585         sigemptyset(&sigset);
586         pthread_sigmask(SIG_SETMASK, &sigset, NULL);}其中pthread_sigmask(SIG_SETMASK, &sigset, NULL)会进入内核调用:
2870 int sigprocmask(int how, sigset_t *set, sigset_t *oldset)
2871 {
2872     struct task_struct *tsk = current;
2873     sigset_t newset;
2874 
2875     /* Lockless, only current can change ->blocked, never from irq */
2876     if (oldset)
2877         *oldset = tsk->blocked;
2878 
2879     switch (how) {
2880     case SIG_BLOCK:
2881         sigorsets(&newset, &tsk->blocked, set);
2882         break;
2883     case SIG_UNBLOCK:
2884         sigandnsets(&newset, &tsk->blocked, set);
2885         break;
2886     case SIG_SETMASK:
2887         newset = *set;
2888         break;
2889     default:
2890         return -EINVAL;
2891     }
2892 
2893     __set_current_blocked(&newset);
2894     return 0;
2895 }
在函数 __set_current_blocked(&newset)中,也需要申请tsk->sighand->siglock
2846 void __set_current_blocked(const sigset_t *newset)
2847 {
2848     struct task_struct *tsk = current;
2849 
2850     /*
2851      * In case the signal mask hasn't changed, there is nothing we need
2852      * to do. The current->blocked shouldn't be modified by other task.
2853      */
2854     if (sigequalsets(&tsk->blocked, newset))
2855         return;
2856 
2857     spin_lock_irq(&tsk->sighand->siglock);
2858     __set_task_blocked(tsk, newset);
2859     spin_unlock_irq(&tsk->sighand->siglock);
2860 }

所形成的关系大概是这样

sender                                          main threadlock_task_sighandsend_signal(sig, info, p, type)         spin_lock_irq(&tsk->sighand->siglock) //被阻塞unlock_task_sighand(p, &flags)           获取锁,继续调用sigprocmask其他的事情 __set_task_blocked(tsk, newset); spin_unlock_irq(&tsk->sighand->siglock);//释放锁      

本来这个锁的释放,唤醒进程都是很简短的过程,通常都是几微秒,为什么这个设备上会出现70us的问题呢,我们看sender和receiver都运行在CPU1上,而main thread是运行在CPU41上,是不是不在一个numa node上,对远端的内存访问会消耗很长时间?

3, 解决方案:将main thread与sender,receiver放到同一个node上

taskset -c 2 ./sigwaittest -p 90 -i 1000 -a 1 -b 80  //不再重现

如果将main thread强制放在CPU41上呢:

taskset -c 41 ./sigwaittest -p 90 -i 1000 -a 1 -b 80 // 很快重现

相关文章:

sigwaittest测试超标的调试过程

1&#xff0c;问题描述硬件环境&#xff1a;飞腾S2500&#xff08;64核&#xff09;OS&#xff1a;kylinOS, linux preempt rt&#xff0c; 4.19.90测试命令&#xff1a;sigwaittest -p 90 -i 1000 -a 1测试结果&#xff1a;信号混洗值最大超过了80us&#xff0c;与飞腾其他CPU…...

Python进阶-----面对对象4.0(面对对象三大特征之--继承)

目录 前言&#xff1a; Python的继承简介 1.什么是继承 2.继承的好处 3.object类 继承的相关用法 1.继承的定义与法则 2.对继承的重写 3.&#xff08;单继承&#xff09;多层继承 4.多继承 5.多继承重写时调用父类方法 前言&#xff1a; 在讲之前&#xff0c;我想说说中…...

九龙证券|利好政策密集发布,机构扎堆看好的高增长公司曝光

新能源轿车销量和保有量快速增长&#xff0c;带来了充电桩商场的微弱需求。 日前&#xff0c;商务部部长王文涛表明&#xff0c;本年将在落实好方针的一起&#xff0c;活跃出台新方针办法&#xff0c;比方辅导当地展开新能源轿车下乡活动&#xff0c;优化充电等使用环境&#x…...

stm32CubeIDE FMC 驱动LCD(8080)

一&#xff0c;TFT屏硬件接口16位&#xff0c;80并口。二&#xff0c;FMC介绍。FSMC&#xff08;Flexible Static Memory Controller&#xff09;&#xff0c;译为灵活的静态存储控制器。STM32F1 系列芯片使用 FSMC 外设来管理扩展的存储器&#xff0c;它可以用于驱动包括 SRAM…...

Java 数据类型

数据类型用于对数据归类&#xff0c;以便开发者理解和操作。 基本数据类型 Java 确定了每种基本数据类型所占存储空间的大小&#xff0c;不会像其它语言那样随机器硬件架构的变化而变化&#xff0c;这使 Java 程序更具可移植性。 Java 中定义了如下的基本数据类型。 byte …...

Prometheus 监控云Mysql和自建Mysql(多实例)

本文您将了解到 Prometheus如何配置才能监控云Mysql(包括阿里云、腾讯云、华为云)和自建Mysql。 Prometheus 提供了很多种Exporter&#xff0c;用于监控第三方系统指标&#xff0c;如果没有提供也可以根据Exporter规范自定义Exporter。 本文将通过MySQL server exporter 来监控…...

Vue3中的h函数

文章目录简介简单使用参数使用计数器进阶使用函数组件插槽专栏目录请点击 简介 众所周知&#xff0c;vue内部构建的其实是虚拟DOM&#xff0c;而虚拟DOM是由虚拟节点生成的&#xff0c;实质上虚拟节点也就是一个js对象事实上&#xff0c;我们在vue中写的template,最终也是经过…...

阿尔法开发板 IMX6ULL 说明

一. IMX6ULL开发板 IMX6ULL开发板即正点原子的阿尔法(ALPHA)开发板&#xff0c;采用恩智浦芯片&#xff0c;cortex-A7架构的。 二. IM6ULL开发板说明 1. IO说明 对于IMX6ULL芯片&#xff0c;一个IO对应两个寄存器&#xff0c;第一个寄存器负责配置其复用功能&#xff0c;…...

Altium Designer19 #学习笔记# | 基础应用技巧汇总

全文目录一.元件符号库二.元件封装库1.AD09 集成元件库/封装库三.电路原理图1. 巧用查找"相似对象功能"1.1 查找相同元件1.2. 查找相同文本1.3. 查找相同网络 &#xff1a;E - S - C四.PCB原理图【AD PCB模式下的常用快捷键】PCB视图放大/缩小PCB视图左/右移动PCB切换…...

Python 元类编程实现一个简单的 ORM

概述 什么是ORM?    ORM全称“Object Relational Mapping”&#xff0c;即对象-关系映射&#xff0c;就是把关系数据库的一行映射为一个对象&#xff0c;也就是一个类对应一个表&#xff0c;这样&#xff0c;写代码更简单&#xff0c;不用直接操作SQL语句。 现在我们就要实…...

《C++ Primer Plus》第18章:探讨 C++ 新标准(7)

C11 新增的其他功能 C11 增加了很多功能&#xff0c;本书无法全面介绍&#xff1b;另外&#xff0c;本书编写期间&#xff0c;其中很多功能还未得到广泛实现。然而&#xff0c;有些功能有必要简要地介绍一下。 并行编程 当前&#xff0c;为提高计算机性能&#xff0c;增加处…...

Redis学习(二):Redis安装测试

概述 Redis是什么 Redis, Remote Dictionary Server, 即远程字典服务。免费开源的数据库。 由C语言编写&#xff0c;支持网络&#xff0c;可基于内存亦可持久化的日志型、KV数据库&#xff0c;并提供所种语言的API。 Redis能干嘛 用于内存存储&#xff0c;持久化。rdb、ao…...

Vector - CAPL - 简介及数据结构

对于想进入车载行业或者已经在车载行业工作的朋友对于CAPL这个词都会相当的熟悉&#xff0c;都知道他是做车载网络测试脚本的语言&#xff0c;并且跟C有点类似&#xff0c;但是它到底是什么呢&#xff1f;CAPL全称&#xff08;Communication Access Programming Language&#…...

20230304英语学习

What Would Happen if the Moon Disappeared Tomorrow? 如果明天月球消失了会怎样&#xff1f; The closest object to our planet, the Moon, may seem like Earth’s little sibling.Since its birth, the satellite has mostly just hung around, playing gravitational t…...

【基础算法】单链表的OJ练习(3) # 移除链表元素 # 相交链表 #

文章目录前言移除链表元素相交链表写在最后前言 本章的OJ练习也是相对简单的&#xff0c;只要能够理解解题的思路&#xff0c;并且依照这个思路能够快速的写出代码&#xff0c;我相信&#xff0c;你的链表水平已经足够了。 对于OJ练习&#xff08;2&#xff09; : ->传送门…...

【自用】SpringBoot项目通用类整理

文章目录全局Json序列化Controller日志切面全局异常拦截GlobalExceptionHandlerApiResultBusinessExceptionResponseEntityUtil全局返回体包装MP自动填充接口文档配置类自定义Async异步线程池本文主要整理各类项目中通用的配置类、工具类&#xff0c;便于复查自用。 全局Json序…...

动态规划法(总述)多阶段决策最优化问题

动态规划: 研究最优控制问题提出的 该问题有n个输入&#xff0c;问题的解由这n个输入组成&#xff0c;这个子集必须满足事先给定的条件&#xff0c;这些条件称为约束条件&#xff0c;满足约束条件的可行解可能不只有一个为了衡量可行解的优劣&#xff0c;通常以一些函数的形式&…...

MySQL跨服务器数据映射

MySQL跨服务器数据映射环境准备1. 首先是要查看数据库的federated引擎 开启/关闭 状态2. 打开任务管理器&#xff0c;并重启mysql服务3. 再次查看FEDERATED引擎状态&#xff0c;引擎已启动映射实现问题总结在日常的开发中经常进行跨数据库进行查询数据。 同服务器下跨数据库进…...

利用反射实现通过读取配置文件对类进行实例化-课后程序(JAVA基础案例教程-黑马程序员编著-第十二章-课后作业)

【案例12-3】&#xff1a;利用反射实现通过读取配置文件对类进行实例化 【案例介绍】 1.案例描述 现在有一个项目&#xff0c;项目中创建了一个Person类&#xff0c;在Person类中定义了一个sleep()方法。在工程中还定义了一个Student类继承Person类&#xff0c;在Student类中…...

1.2 CSS文本属性

CSS Text(文本)属性&#xff1a; 定义文本外观&#xff0c;颜色&#xff0c;装饰&#xff0c;缩进&#xff0c;行间距来修饰文本 文本样式 文本缩进 text-indent文本水平对齐方式&#xff1a;text-align文本修饰&#xff1a;text-decoration行高 line-height CSS文本颜色属性…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!

一、引言 在数据驱动的背景下&#xff0c;知识图谱凭借其高效的信息组织能力&#xff0c;正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合&#xff0c;探讨知识图谱开发的实现细节&#xff0c;帮助读者掌握该技术栈在实际项目中的落地方法。 …...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...