linux驱动面试题目汇总

http://blog.csdn.net/blueice8601/article/details/7666427

1、linux驱动分类

2、信号量与自旋锁

3、platform总线设备及总线设备如何编写

4、kmalloc和vmalloc的区别

5、module_init的级别

6、添加驱动

7、IIC原理,总线框架,设备编写方法,i2c_msg

8、kernel panic

9、USB总线,USB传输种类,urb等

10、android boot 流程

11、android init解析init.rc

12、同步和互斥

答案:

1、http://baike.baidu.com/view/5363967.htm

Linux设备驱动的分类

  (1)字符设备。

  (2) 块设备。

  (3) 网络设备。

  字符设备指那些必须以串行顺序依次进行访问的设备,如触摸屏、磁带驱动器、鼠标等。块设备可以用任意顺序进行访问,以块为单位进行操作,如硬盘、软驱等。字符设备不经过系统的快速缓冲,而块设备经过系统的快速缓冲。但是,字符设备和块设备并没有明显的界限,如对于Flash设备,符合块设备的特点,但是我们仍然可以把它作为一个字符设备来访问。统里支持对发送数据和接收数据的缓存,提供流量控制机制,提供对多协议的支持。

2、http://www.cnblogs.com/linxinshuo/archive/2009/12/08/1619771.html

自旋锁

  自旋锁是专为防止多处理器并发而引入的一种锁,它应用于中断处理等部分。对于单处理器来说,防止中断处理中的并发可简单采用关闭中断的方式,不需要自旋锁。

  自旋锁最多只能被一个内核任务持有,如果一个内核任务试图请求一个已被争用(已经被持有)的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。要是锁未被争用,请求它的内核任务便能立刻得到它并且继续进行。自旋锁可以在任何时刻防止多于一个的内核任务同时进入临界区,因此这种锁可有效地避免多处理器上并发运行的内核任务竞争共享资源。

  事实上,自旋锁的初衷就是:在短期间内进行轻量级的锁定。一个被争用的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(特别浪费处理器时间),所以自旋锁不应该被持有时间过长。如果需要长时间锁定的话, 最好使用信号量。但是自旋锁节省了上下文切换的开销。

自旋锁的基本形式如下:

  spin_lock(&mr_lock);

  //临界区

  spin_unlock(&mr_lock);

  因为自旋锁在同一时刻只能被最多一个内核任务持有,所以一个时刻只有一个线程允许存在于临界区中。这点很好地满足了对称多处理机器需要的锁定服务。在单处理器上,自旋锁仅仅当作一个设置内核抢占的开关。如果内核抢占也不存在,那么自旋锁会在编译时被完全剔除出内核。

  简单的说,自旋锁在内核中主要用来防止多处理器中并发访问临界区,防止内核抢占造成的竞争。另外自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁——因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。

死锁:假设有一个或多个内核任务和一个或多个资源,每个内核都在等待其中的一个资源,但所有的资源都已经被占用了。这便会发生所有内核任务都在相互等待,但它们永远不会释放已经占有的资源,于是任何内核任务都无法获得所需要的资源,无法继续运行,这便意味着死锁发生了。自死琐是说自己占有了某个资源,然后自己又申请自己已占有的资源,显然不可能再获得该资源,因此就自缚手脚了。递归使用一个自旋锁就会出现这种情况。

信号量

信号量是一种睡眠锁。如果有一个任务试图获得一个已被持有的信号量时,信号量会将其推入等待队列,然后让其睡眠。这时处理器获得自由去执行其它代码。当持有信号量的进程将信号量释放后,在等待队列中的一个任务将被唤醒,从而便可以获得这个信号量。

  信号量的睡眠特性,使得信号量适用于锁会被长时间持有的情况;只能在进程上下文中使用,因为中断上下文中是不能被调度的;另外当代码持有信号量时,不可以再持有自旋锁。

信号量基本使用形式为:

  static DECLARE_MUTEX(mr_sem);//声明互斥信号量

  if(down_interruptible(&mr_sem))

//可被中断的睡眠,当信号来到,睡眠的任务被唤醒

//临界区

up(&mr_sem);

信号量和自旋锁区别

  从严格意义上讲,信号量和自旋锁属于不同层次的互斥手段,前者的实现有赖于后者。

注意以下原则:

如果代码需要睡眠——这往往是发生在和用户空间同步时——使用信号量是唯一的选择。由于不受睡眠的限制,使用信号量通常来说更加简单一些。如果需要在自旋锁和信号量中作选择,应该取决于锁被持有的时间长短。理想情况是所有的锁都应该尽可能短的被持有,但是如果锁的持有时间较长的话,使用信号量是更好的选择。另外,信号量不同于自旋锁,它不会关闭内核抢占,所以持有信号量的代码可以被抢占。这意味者信号量不会对影响调度反应时间带来负面影响。

自旋锁对信号量

需求              建议的加锁方法

低开销加锁           优先使用自旋锁

短期锁定            优先使用自旋锁

长期加锁            优先使用信号量

中断上下文中加锁        使用自旋锁

持有锁是需要睡眠、调度     使用信号量

3、http://www.cnblogs.com/noaming1900/archive/2010/10/26/1861177.html

http://blog.chinaunix.net/space.php?uid=11909535&do=blog&id=2801676

http://slhieanng.blog.163.com/blog/static/1932833562011731982291/

4、http://blog.163.com/[email protected]/blog/static/109968875201241961116110/

kmalloc()和vmalloc()介绍
kmalloc()
用于申请较小的、连续的物理内存
1. 以字节为单位进行分配,在<linux/slab.h>中
2. void *kmalloc(size_t size, int flags) 分配的内存物理地址上连续,虚拟地址上自然连续
3. gfp_mask标志
:什么时候使用哪种标志?如下:
———————————————————————————————-
情形 相应标志
———————————————————————————————-
进程上下文,可以睡眠 GFP_KERNEL
进程上下文,不可以睡眠 GFP_ATOMIC
中断处理程序 GFP_ATOMIC
软中断 GFP_ATOMIC
Tasklet GFP_ATOMIC
用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
用于DMA的内存,不可以睡眠 GFP_DMA | GFP_ATOMIC
———————————————————————————————-
4. void kfree(const void *ptr)
释放由kmalloc()分配出来的内存块

vmalloc()
用于申请较大的内存空间,虚拟内存是连续的
1. 以字节为单位进行分配,在<linux/vmalloc.h>中
2. void *vmalloc(unsigned long size) 分配的内存虚拟地址上连续,物理地址不连续
3. 一般情况下,只有硬件设备才需要物理地址连续的内存,因为硬件设备往往存在于MMU之外,根本不了解虚拟地址;但为了性能上的考虑,内核中一般使用 kmalloc(),而只有在需要获得大块内存时才使用vmalloc(),例如当模块被动态加载到内核当中时,就把模块装载到由vmalloc()分配 的内存上。
4.void vfree(void *addr),这个函数可以睡眠,因此不能从中断上下文调用。

malloc(), vmalloc()和kmalloc()区别
[*]kmalloc和vmalloc是分配的是内核的内存,malloc分配的是用户的内存
[*]kmalloc保证分配的内存在物理上是连续的,vmalloc保证的是在虚拟地址空间上的连续,malloc不保证任何东西(这点是自己猜测的,不一定正确)
[*]kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相对较大
[*]内存只有在要被DMA访问的时候才需要物理上连续
[*]vmalloc比kmalloc要慢

5、http://blog.163.com/[email protected]/blog/static/167563447201010221231507/

#define module_init(x) __initcall(x);
#define __initcall(fn)    device_initcall(fn)

#define device_initcall(fn) __define_initcall("6",fn,6)

可知module_init()的级别为6

在vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
   __initcall_start = .;
   *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
   __initcall_end = .;
   }

可以看出initcall总共分为0~7八个级别,表示系统在启动过程中调用do_initcalls()时,会根据它们的级别顺序调用。可知模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。

6、

就静态加载和动态加载:
静态加载是系统启动的时候由内核自动加载的,这个要事先将驱动编译进内核才行;
动态加载,也就是模块加载方式,这种方式下驱动以模块的形式存放在文件系统中,需要时动态载入内核,这种主要用在调试的时候,比较方便灵活。insmod module.ko

7、

http://liu1227787871.blog.163.com/blog/static/2053631972012521191287/                        IIC 总线基础知识

http://blog.163.com/[email protected]/blog/static/109968875201243094211492/   IIC总线驱动程序架构分析

http://liu1227787871.blog.163.com/blog/static/20536319720125333531134/           IIC驱动程序分析

http://liu1227787871.blog.163.com/blog/static/2053631972012539436224/

http://liu1227787871.blog.163.com/blog/static/2053631972012531048573/

http://liu1227787871.blog.163.com/blog/static/2053631972012531620760/

8、

http://blog.51osos.com/linux/linux-kernel-panic/

有两种主要类型kernel panic:

1.hard panic(也就是Aieee信息输出)
2.soft panic (也就是Oops信息输出)

9、

USB总线:

USB总线属于一种轮询式总线,主机控制端口初始化所有的数据传输。每一总线动作最多传送三个数据包,包括令牌(Token)、数据(Data)、联络(HandShake)。按照传输前制定好的原则,在每次传送开始时,主机送一个描述传输动作的种类、方向、USB设备地址和终端号的USB数据包,这个数据包通常被称为令牌包(TokenPacket)。USB设备从解码后的数据包的适当位置取出属于自己的数据。数据传输方向不是从主机到设备就是从设备到主机。在传输开始时,由标志包来标志数据的传输方向,然后发送端开始发送包含信息的数据包或表明没有数据传送。接收端也要相应发送一个握手的数据包表明是否传送成功。发送端和接收端之间的USB数据传输,在主机和设备的端口之间,可视为一个通道。USB中有一个特殊的通道一缺省控制通道,它属于消息通道,设备一启动即存在,从而为设备的设置、状态查询和输入控制信息提供一个入口。

USB总线的四种传输类型:

1、中断传输:由OUT事务和IN事务构成,用于键盘、鼠标等HID设备的数据传输中 2、批量传输:由OUT事务和IN事务构成,用于大容量数据传输,没有固定的传输速率,也不占用带宽,当总线忙时,USB会优先进行其他类型的数据传输,而暂时停止批量转输。 3、同步传输:由OUT事务和IN事务构成,有两个特别地方,第一,在同步传输的IN和OUT事务中是没有返回包阶段的;第二,在数据包阶段任何的数据包都为DATA0 4、控制传输:最重要的也是最复杂的传输,控制传输由三个阶段构成(初始配置阶段、可选数据阶段、状态信息步骤),每一个阶段能够看成一个的传输,也就是说控制传输其实是由三个传输构成的,用来于USB设备初次加接到主机之后,主机通过控制传输来交换信息,设备地址和读取设备的描述符,使得主机识别设备,并安装相应的驱动程式,这是每一个USB研发者都要关心的问题。
URB:
USB请求块(USB request block,urb)是USB设备驱动中用来描述与USB设备通信所用的基本载体和核心数据结构,非常类似于网络设备驱动中的sk_buff结构体,是USB主机与设备通信的“电波”。
http://book.51cto.com/art/200803/66930.htm

10、

http://www.cnblogs.com/idiottiger/archive/2012/05/22/2513001.html   android boot process from power on

http://www.cnblogs.com/idiottiger/archive/2012/05/23/2513494.html   android boot 代码流程 1

http://www.cnblogs.com/idiottiger/archive/2012/05/25/2516295.html  android boot 代码流程 2

11、

http://ytydyd.blog.sohu.com/136255592.html

Android初始化语言(Android Init Language

Android初始化脚本语言包含四种类型的语句:

  • 动作(Actions)
  • 指令(Commands)
  • 服务(Services)
  • 选项(Options)

该语言的语法包括下列约定:

  • 所有类型的语句都是基于行(line-oriented)的, 一个语句包含若干个tokens,token之间通过空格字符分隔. 如果一个token中需要包含空格字符,则需要通过C语言风格的反斜线(‘\‘)来转义,或者使用双引号把整个token引起来。反斜线还可以出现在一行的末尾,表示下一行的内容仍然属于当前语句。
  • 以‘#‘开始的行是注释行。
  • 动作(Actions)和服务(Services)语句隐含表示一个新的段落(section)的开始。 所有的指令(commands)和选项(options)归属于上方最近的一个段落。在第一个段落之前的指令(commands)和选项(options)是无效的。
  • 动作(Actions)和服务(Services)拥有唯一性的名字。如果出现重名,那么后出现的定义将被作为错误忽略掉。

动作(Actions)

动作(Actions)是一个有名字的指令(commands)序列。每个动作(Actions)都定义一个触发条件(trigger),用于指示什么时候执行这个动作。当与动作的触发器匹配的事件发生时,该动作将被添加到一个即将被执行的队列的队尾(除非它已经在队列中)。

队列中的每一个动作被依次取出执行,动作中的每一个指令也将依次执行。初始化程序(Init)在执行一个动作的各项指令的期间,还需要处理其它操作(比如,设备创建/销毁,属性设置,进程重启)。

一个动作定义的形式如下:

on <trigger>
  <command>
  <command>
  <command>

服务(Services)

服务是初始化程序需要启动的一些程序,初始化程序还有可能会在这些程序退出之后重启它们。Services take 一个服务定义的形式如下:

  service <name> <pathname> [ <argument> ]*
  <option>
  <option>
  ...

选项(Options)

选项将影响控制初始化程序运行服务的时机和方法。可能的选项如下表。

选项 说明
disabled This service will not automatically start with its class. It must be explicitly started by name.
socket <name> <type>  <perm> [ <user> [ <group> ] ] Create a unix domain socket named /dev/socket/<name> and pass its fd to the launched process. Valid <type> values include dgram and streamuser and groupdefault to 0.
user <username> Change to username before exec‘ing this service. Currently defaults to root.
group <groupname> [ <groupname> ]* Change to groupname before exec‘ing this service.  Additional  groupnames beyond the first, which is required, are used to set additional groups of the process (withsetgroups()). Currently defaults to root.
capability [ <capability> ]+ Set linux capability before exec‘ing this service
oneshot Do not restart the service when it exits.
class <name> Specify a class name for the service.  All services in a named class must start and stop together. A service is considered of class "default" if one is not specified via the class option.

触发器(Triggers)

触发器是一个字符串,用于匹配特定的事件,这些事件将触发触发器所属动作(Actions)的执行。

触发器 说明
boot This is the first trigger that occurs when init starts (after /init.conf is loaded).
<name>=<value> Triggers of this form occur when the property <name> is set to the specific value<value>.
device-added-<path>
device-removed-<path>
Triggers of these forms occur when a device node is added or removed.
service-exited-<name> Triggers of this form occur when the specified service exits.

指令(Commands)

Command Description
exec <path> [ <argument> ]* Fork and execute a program (<path>). This will block until the program completes execution. Try to avoid exec. Unlike the builtin commands, it runs the risk of getting init "stuck".
export <name> <value> Set the environment variable <name> equal to <value> in the global environment (which will be inherited by all processes started after this command is executed).
ifup <interface> Bring the network interface <interface> online.
import <filename> Parse an init config file, extending the current configuration.
hostname <name> Set the host name.
class_start <serviceclass> Start all services of the specified class if they are not already running.
class_stop <serviceclass> Stop all services of the specified class if they are currently running.
domainname <name> Set the domain name.
insmod <path> Install the module at <path>.
mkdir <path> Make a directory at <path>.
mount <type> <device> <dir> [ <mountoption> ]* Attempt to mount the named device at the directory <dir> <device>. This may be of the form [email protected] to specify a mtd block device by name.
setkey - currenlty undefined -
setprop <name> <value> Set system property <name> to <value>.
setrlimit <resource> <cur> <max> Set the rlimit for a resource.
start <service> Start a service running if it is not already running.
stop <service> Stop a service from running if it is currently running.
symlink <target> <path> Create a symbolic link at <path> with the value <target>.
write <path> <string> [ <string> ]* Open the file at <path> and write one or more strings to it with write(2).

属性(Properties)

初始化程序(Init)可以根据需要修改一些系统的属性。

属性 说明
init.action Equal to the name of the action currently being executed or "" if none.
init.command Equal to the command being executed or "" if none.
init.svc.<name> State of a named service ("stopped", "running", or "restarting").

init.rc文件示例

on boot
  export PATH /sbin:/system/sbin:/system/bin
  export LD_LIBRARY_PATH /system/lib

  mkdir /dev
  mkdir /proc
  mkdir /sys

  mount tmpfs tmpfs /dev
  mkdir /dev/pts
  mkdir /dev/socket
  mount devpts devpts /dev/pts
  mount proc proc /proc
  mount sysfs sysfs /sys

  write /proc/cpu/alignment 4

  ifup lo

  hostname localhost
  domainname localhost

  mount yaffs2 [email protected] /system
  mount yaffs2 [email protected] /data

  import /system/etc/init.conf

  class_start default

service adbd /sbin/adbd
  user adb
  group adb

service usbd /system/bin/usbd -r
  user usbd
  group usbd
  socket usbd 666

service zygote /system/bin/app_process -Xzygote /system/bin --zygote
  socket zygote 666

service runtime /system/bin/runtime
  user system
  group system

on device-added-/dev/compass
  start akmd

on device-removed-/dev/compass
  stop akmd

service akmd /sbin/akmd
  disabled
  user akmd
  group akmd

12、http://www.cnblogs.com/linxinshuo/archive/2009/12/09/1620413.html

同步和互斥

  相交进程之间的关系主要有两种,同步与互斥。所谓互斥,是指散步在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。所谓同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。

  显然,同步是一种更为复杂的互斥,而互斥是一种特殊的同步。

  也就是说互斥是两个线程之间不可以同时运行,他们会相互排斥,必须等待一个线程运行完毕,另一个才能运行,而同步也是不能同时运行,但他是必须要安照某种次序来运行相应的线程(也是一种互斥)!

  总结:

  互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

  同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

时间: 2024-10-27 08:33:56

linux驱动面试题目汇总的相关文章

几个Linux驱动面试题目

这几天面试几个想做安卓Linux驱动的,总体感觉上驱动基础还是比较薄弱,大部分情况是虽然做过驱动,但是基本上都是采用内核现成的,或者是开发板上已经有的,单独写过模块驱动很少,驱动机制理解不是很透彻.以下是几个随口问过的基础问题,供参考. 1.字符型驱动设备你是怎么创建设备文件的,就是/dev/下面的设备文件,供上层应用程序打开使用的? 2.写一个中断服务需要注意哪些?如果中断产生之后要做比较多的事情你是怎么做的? 3.自旋锁和信号量在互斥使用时需要注意哪些?在中断服务程序里面的互斥是使用自旋锁还

测试开发面试题目汇总一

测试开发面试题目汇总 1. 项目经验 2. 测试的过程 3. 京东登录页面怎么测? 4. 如果一个普通用户,他的百度首页打不开,问题怎么定位?写出定位流程. 5.问简历上的第一个项目的详细情况,包括测试用例怎么写?怎么判断测试通过?项目的原理? 6.如果是做功能测试,能接受吗? 7.说一下你们工作中的测试流程 8.用她的手机给我看了下百度贴吧的发帖功能的界面,给我张纸,让我写出测试点(只需要考虑内容,表情,添加图片,@功能),写完讲一遍逻辑. 9  针对发朋友圈这个功能设计你的测试用例,请给出用

HTML5面试题目汇总(二)

HTML5面试题目汇总(二) 标签: javascript 2016-07-19 10:15 639人阅读 评论(0) 收藏 举报  分类: javascript(16)  1.怎样添加.移除.移动.复制.创建和查找节点? 1)创建新节点 createDocumentFragment() //创建一个DOM片段 createElement() //创建一个具体的元素 createTextNode() //创建一个文本节点 2)添加.移除.替换.插入 appendChild() //添加 remo

2019最新Android中级面试题目汇总解答

注:因为实际开发与参考答案会有所不同,再者怕误导大家,所以这些面试题答案还是自己去理解!面试官会针对简历中提到的知识点由浅入深提问,所以不要背答案,多理解. Android进阶延伸点 1.如何进行单元测试,如何保证App稳定 ? 参考回答: 要测试Android应用程序,通常会创建以下类型自动单元测试 本地测试:只在本地机器JVM上运行,以最小化执行时间,这种单元测试不依赖于Android框架,或者即使有依赖,也很方便使用模拟框架来模拟依赖,以达到隔离Android依赖的目的,模拟框架如Goog

Python面试题目--汇总

原文链接-https://github.com/taizilongxu/interview_python Python语言特性 1 Python的函数参数传递 2 Python中的元类(metaclass) 3 @staticmethod和@classmethod 4 类变量和实例变量 5 Python自省 6 字典推导式 7 Python中单下划线和双下划线 8 字符串格式化:%和.format 9 迭代器和生成器 10 *args and **kwargs 11 面向切面编程AOP和装饰器

2016年Web前端面试题目汇总

今天收集了一些web前端工程师的经典面试题以及自己面试过程中无法解决的问题,通过对知识的整理以及经验的总结,重新巩固自身的web前端基础知识(http://www.maiziedu.com/course/web/),如有错误或更好的答案,欢迎指正. HTML/CSS部分 1.什么是盒子模型? 在网页中,一个元素占有空间的大小由几个部分构成,其中包括元素的内容(content),元素的内边距(padding),元素的边框 (border),元素的外边距(margin)四个部分.这四个部分占有的空间

web安全职位面试题目汇总

Domain 解释一下同源策略 同源策略,那些东西是同源可以获取到的 如果子域名和顶级域名不同源,在哪里可以设置叫他们同源 如何设置可以跨域请求数据?jsonp是做什么的? Ajax Ajax是否遵循同源策略? json注入如何利用 浏览器策略 不同浏览器之间,安全策略有哪些不同,比如chrome,firefox,IE CSP是什么?如何设置CSP? SQLi 如何判断sql注入,有哪些方法 为什么有的时候没有错误回显,用php举例 宽字符注入的原理?如何利用宽字符注入漏洞,payload如何构

[C/C++][面试]面试题目汇总

之前面试被问到C++里static的作用是什么,但我却只知道static在java里的作用是什么,于是就google了一下c++相关面试题,发现这个同学总结的很棒. 就记录一下. 原文地址:C/C++面试知识总结 侵删 --------------------------正文分割线------------------------------------ const // 类 class A { private: const int a; // 常对象成员,只能在初始化列表赋值 public: /

前端工程师必须知道的vue前端面试题目汇总

①:说说Vue和Angular.ReactJS的相同点和不同点 ②:简单描述一下Vue中的MVVM模型③:v-if和v-show指令有什么区别?④:如何阻止Vue中的绑定事件不发生冒泡⑤:父.子组件间是如何通信的?⑥:非父子层级的组件如何实现通信?⑦:什么是动态组件?他的作用是什么?⑧:为什么组件中的data属性的值必须是一个函数?答案与详解Q说说Vue和Angular.ReactJS的相同点和不同点与React的相同:●都使用了Virtual DOM●提供了响应式和组件化的视图组件●将注意力集