mutex semaphore 区别和mutex的区别

博客访问: 1016240
博文数量: 89
博客积分: 1205
博客等级: 少尉
技术积分: 4282
注册时间:
IT168企业级官微
微信号:IT168qiye
系统架构师大会
微信号:SACC2013
分类: LINUX
关于Linux内核中的mutex机制,一篇很重要的文档来自内核源码中的Documentation/mutex-
design.txt,由Ingo molnar同学起头,标题是"Generic Mutex
Subsystem",这篇文档开宗名义,直接将1楼中最后一个问题给端了出来(因此我估计这个问题此前已经有很多人骚扰过Ingo等同学了):
"Why on earth do we need a new mutex subsystem, and what's wrong with semaphores?"
前面已经讲过,当struct
semaphore中的成员变量为1,就可以用来实现mutex这种东西,而且内核也明确定义了DEFINE_SEMAPHORE宏将count初始化为
1,信号量上的DOWN与UP操作就更不用说了,在内核中也都有很好的实现,难道这种binary
semaphore机制还不能满足我们的要求吗,干嘛还非得弄一个新的mutex机制出来呢?
下面是Ingo同学对此的解释,他说“firstly, there's nothing wrong with semaphores. But if
the simpler mutex semantics are sufficient for your code, then there
are a couple of advantages of
mutexes”,就是说,信号量在Linux中的实现是没任何问题的(上来先安抚一下大家躁动的心情),但是mutex的语义相对来说要较信号量要来得
简单,所以如果你的代码若只是想对某一共享资源进行互斥访问的话,那么使用这种简化了的mutex机制可以带来如下的一坨好处。这句话字面上的理解
是,mutex将binary semaphore的实现简化了(the simper
mutex),因此如果单纯从互斥的角度,用mutex会有很多好处。
其实后面我们会看到,在内核源码中,相对于semaphore的DOWN和UP实现,因为后期引入的特别针对binary
semaphore的性能优化,也就是现在看到的mutex机制,其实现代码要更为复杂。
接下来Ingo列出的一大堆使用mutex的好处,在这个帖子中我们将一条一条地来看,再结合内核源码,看看事实是否的确象他说的那样:
- 'struct mutex' is smaller on most architectures: E.g. on x86, 'struct
semaphore' is 20 bytes, 'struct mutex' is 16 bytes. A smaller structure
size means less RAM footprint, and better CPU-cache utilization.
这条最好验证,尤其还是x86平台,找个简单的内核模块,打印一下sizeof就可以了。在我的x86-64
32位Linux系统(内核版本2.6.37)上, struct semaphore的大小是16字节,而struct
mutex的大小则是20字节,另两台x86-64 64位Linux系统(内核版本3.x)上的结果则是,struct
semaphore的大小是24字节,而struct mutex的大小则是32字节。这里不妨看一下struct mutex在内核中的定义:&include/linux/mutex.h&struct mutex {&&&&&&&&/* 1: unlocked, 0: locked, negative: locked, possible waiters */&&&&&&&&atomic_t
count;&&&&&&&&spinlock_t
wait_lock;&&&&&&&&struct list_head
wait_list;#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)&&&&&&&&struct task_struct
*owner;#endif#ifdef CONFIG_DEBUG_MUTEXES&&&&&&&&const char
*name;&&&&&&&&void
*magic;#endif#ifdef CONFIG_DEBUG_LOCK_ALLOC&&&&&&&&struct lockdep_map
dep_map;#endif};可以看到stuct mutex的定义其实比semaphore要来得复杂,里面有一些条件编译选项在里面。因为我们实际使用当中很少会使用它的调试功能,但是SMP现在则很普遍,我上面测试用的Linux环境都是多处理器系统。所以,mutex的定义实际上可简化为:struct mutex {&&&&&&&&/* 1: unlocked, 0: locked, negative: locked, possible waiters */&&&&&&&&atomic_t
count;&&&&&&&&spinlock_t
wait_lock;&&&&&&&&struct list_head
wait_list;&&&&&&&&struct task_struct
*owner;};对比一下前面struct semaphore的定义你会发现,struct
mutex比semaphore多了一个owner指针,因此上面的结果也就不难理解了,指针在32位系统上是4字节,而64位系统则是8字节。我相信
Ingo同学肯定不会胡说八道,那么明显地,相对于Ingo当时写mutex-design.txt时的情形,Linux内核源码发生了变化,这个在
Linux的开发过程中实在是太正常不过的一件事了:文档总是远远落后于代码的更新--大家都忙着写code,而很少有人想着去更新文档。
所以接下来Ingo提到的tighter code的优势,估计对mutex而言也不复存在了...
(他本人对mutex相对于semaphore在RAM footprint方面的优势不复存在的最新回复是:"Mutex got larger
due to the adaptive spin-mutex performance
optimization",因此我很自然地将这句话理解成,由于要实现所谓的“adaptive spin-mutex performance
optimization",那么就不惜牺牲了"less RAM footprint, and better CPU-cache
utilization",所以我们有理由期待接下来的spin-mutex performance
optimization会给mutex带来性能上比较大的提升...)下面我们来讨论一下mutex所做的性能优化,在将mutex的引入Linux内核这件事上,Ingo同学是带头大哥,喜欢围观Linux内核开发的同学对这厮肯定不会陌生,在我看来,这厮简直是牛逼得一塌糊涂,将kgdb引入内核也是这厮的杰作...
在mutex的性能提升方面,mutex-design.txt文档中有个具体的测试,采用了一个test-mutex的工具,因为google没找到这
个东西,所以我个人猜测是Ingo自己搞出来的东西,本来想趁这两天放假将最新版下的binary
semaphore和mutex的性能测试一把的,结果这两天啥都没干成。我本来是想索要那个test-mutex程序的,但是Ingo只是建议采用
perf来做。我自己找了个sysbench,但是还没时间用,貌似这个是针对数据库的。之所以做这个测试,是我想知道采用mutex到底能比
binary semaphore能带来多大的性能提升。
按照Ingo的测试数据,"the mutex based kernel was 2.4 times faster than the
semaphore based kernel, _and_ it also had 2.8 times less CPU
utilization",因为事先看过mutex的实现源码,所以我对这个数据有点怀疑,这也是为什么我自己要做性能分析的原因。
semaphore和mutex的代码实现中都有fast path和slow path两条路径,所谓的fast
path,就是当前的代码直接获得信号量,而所谓的slow path,则是当前代码没能第一时间获得信号量。semaphore和mutex在fast
path上性能上的变化应该微乎其微,这个在metex-design.txt文档中也有说明。两者之间最大的差别来自对slow
path的处理,先看semaphore,semaphore的slow
path的调用链是down_interruptible--&__down_interruptible --&
__down_common,&&__down_common的代码实现为:&kernel/semaphore.c&static inline int __sched __down_common(struct semaphore *sem, long state,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&long timeout){&&&&&&&&struct task_struct *task = current;&&&&&&&&struct semaphore_waiter waiter;&&&&&&&&list_add_tail(&waiter.list, &sem-&wait_list);&&&&&&&&waiter.task = task;&&&&&&&&waiter.up = 0;&&&&&&&&for (;;) {&&&&&&&&&&&&&&&&if (signal_pending_state(state, task))&&&&&&&&&&&&&&&&&&&&&&&&goto interrupted;&&&&&&&&&&&&&&&&if (timeout &= 0)&&&&&&&&&&&&&&&&&&&&&&&&goto timed_out;&&&&&&&&&&&&&&&&__set_task_state(task, state);&&&&&&&&&&&&&&&&raw_spin_unlock_irq(&sem-&lock);&&&&&&&&&&&&&&&&timeout = schedule_timeout(timeout);&&&&&&&&&&&&&&&&raw_spin_lock_irq(&sem-&lock);&&&&&&&&&&&&&&&&if (waiter.up)&&&&&&&&&&&&&&&&&&&&&&&&return 0;&&&&&&&&}&timed_out:&&&&&&&&list_del(&waiter.list);&&&&&&&&return -ETIME;&interrupted:&&&&&&&&list_del(&waiter.list);&&&&&&&&return -EINTR;}相对mutex对slow
path的处理,semaphore要简单多了,它的主要流程是设置当前进程状态为TASK_INTERRUPTIBLE,然后睡眠到一个等待队列中。所
以semaphore如果第一时间没有获得信号量,那么它接下来就会sleep。但是mutex的slow
path呢,所有关于性能优化的代码都集中在该条路径中,所有它看起来比semaphore复杂许多...
阅读(6279) | 评论(2) | 转发(11) |
相关热门文章
给主人留下些什么吧!~~
我也感觉好运,呵呵
本人感觉mutex好用啊~~可能是习惯原因吧~嘎嘎
请登录后评论。semaphore与Mutex - cutepig - 博客园
随笔 - 753, 文章 - 1, 评论 - 246, 引用 - 0
C# Semaphore Class/en-us/library/system.threading.semaphore.aspxwikiSemaphore (programming)http://en.wikipedia.org/wiki/Semaphore_%28programming%29理解Semaphore和MutexMutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。&Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。&Binary semaphore与Mutex的差异:在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。&另一个概念是spin lock,这是一个内核态概念。spin lock与semaphore的主要区别是spin lock是busy waiting,而semaphore是sleep。对于可以sleep的进程来说,busy waiting当然没有意义。对于单CPU的系统,busy waiting当然更没意义(没有CPU可以释放锁)。因此,只有多CPU的内核态非进程空间,才会用到spin lock。Linux kernel的spin lock在非SMP的情况下,只是关irq,没有别的操作,用于确保该段程序的运行不会被打断。其实也就是类似mutex的作用,串行化对 critical section的访问。但是mutex不能保护中断的打断,也不能在中断处理程序中被调用。而spin lock也一般没有必要用于可以sleep的进程空间。用semaphore实现mutex以及 用mutex 实现semaphorehttp://bbs.chinaunix.net/viewthread.php?tid=963707深层次探讨mutex与semaphore之间的区别
我的图书馆
深层次探讨mutex与semaphore之间的区别
看过Linux内核的同学都知道,Linux内核中除了有semaphore之外,还有一个mutex
lock。前者我们的操作系统教科书称之为信号量,后者不知道教科书有没有具体的名称,但是在Linux内核中,它的称谓是"互斥锁"或者“互斥体”(总
之,称谓不是问题)。为了提升一下本帖的理论密度,特从Wiki中摘录一段关于semaphore的描述:
“In computer science, a semaphore is a
variable or abstract data type that provides a simple but useful
abstraction for controlling access by multiple processes to a common
resource in a parallel programming environment.
A useful way to think of a semaphore is as a record of how many units of
a particular resource are available, coupled with operations to safely
(i.e., without race conditions) adjust that record as units are required
or become free, and if necessary wait until a unit of the resource
becomes available. Semaphores are a useful tool in the prevention of
race cond however, their use is by no means a
guarantee that a program is free from these problems. Semaphores which
allow an arbitrary resource count are called counting semaphores, while
semaphores which are restricted to the values 0 and 1 (or
locked/unlocked, unavailable/available) are called binary semaphores
(same functionality that mutexes have).”。
其中关键信息主要是“a semaphore is a data type for controlling access by multiple
processes to a common resource in a parallel programming environment...
Semaphores which allow an arbitrary resource count are called counting
semaphores, while semaphores which are restricted to the values 0 and 1
(or locked/unlocked, unavailable/available) are called binary semaphores
(same functionality that mutexes
have)”,也即信号量在并行处理环境下对多个processes访问某个公共资源进行保护,后面提到的binary
semaphore,本质上应该就是mutex了,从same functionality that mutexes
have这句话来看,mutex和binary
semaphore功能应该相同。从以上的文字中显然可以看到,相对mutex而言信号量的适用范围更广(mutex只是信号量的用途之一),这个我们接
下来在后续的Linux源码中也可以看到这其中某些细微之处的区分。
*注:昨天写这个帖子时手头没有操作系统方面的书籍拿来参考,今天我翻了一下《现代操作系统》(陈向群等译,机械工业出版社&&1999年11月第1
版), 关于这个话题,书里明确提到的只有"2.2.5
信号量",至于mutex,书中并没有作为一个独立的概念提出来,只是在讲信号量时提到了上面所说的binary
semaphore,并且说“信号量mutex(应该是指binary
semaphore)用于互斥...互斥是避免混乱所必需的操作...信号量的另一种用途是用于实现同步(synchronization)。信号量
full和empty用来保证一定的事件顺序发生或不发生。在本例中,它们保证缓冲区满的时候生产者停止运行,或者当缓冲区空的时候消费者停止运行。这种
用法与互斥是不同的” --- P30-31 *
OK,理论上的概念有了,那么就来看看实际当中Linux下的semaphone到底长的啥样。以下是semaphore在Linux源码中的定义,源码来自3.2.9:
&include/linux/semaphore.h&/* Please don't access any members of this structure directly */
struct semaphore {
& & & & raw_spinlock_t& & & & & & & &
& & & & unsigned int& & & & & & & &
& & & & struct list_head& & & & wait_
};复制代码如果count=1的话,那么semaphore就可以用来进行互斥操作了,早先内核源码中曾有一个专门的宏用来定义一个count=1的信号量DECLARE_MUTEX:#define DECLARE_MUTEX(name) \
& && && && &struct semaphore name = __SEMAPHORE_INITIALIZER(name, 1)复制代码因
为我们知道在Linux内核源码中还有一个DEFINE_MUTEX宏,所以Marcin
Slusarz同学认为DECLARE_MUTEX宏容易让人困惑,毕竟它其实只是定义了一个count=1的信号量,因此该同学早在08年就向内核社区
提交了一个PATCH,要求Rename DECLARE_MUTEX to DEFINE_SEMAPHORE(),
这个PATCH最终被社区所接受(应该是在2.6.35和2.6.39版本之间,2.6.39已经没有了DECLARE_MUTEX,取而代之的是
DEFINE_SEMAPHORE,但2.6.35还有,我当时在写《深入Linux设备驱动程序内核机制》时,最早引用的是2.6.35的版本,虽然在
写作的中晚期将内核版本切换到2.6.39并在定稿阶段曾试图将之前写的文档全部修正到39版,但是DECLARE_MUTEX的残留显然是条漏网之
鱼...)。因为只是rename,所以DEFINE_SEMAPHORE的定义和原来的DECLARE_MUTEX完全相同。
那么既然count=1的信号量可以用来完成mutex lock的功能,那么内核何必再多此一举弄出个mutex lock出来呢? 关于Linux内核中的mutex机制,一篇很重要的文档来自内核源码中的Documentation/mutex-
design.txt,由Ingo molnar同学起头,标题是"Generic Mutex
Subsystem",这篇文档开宗名义,直接将1楼中最后一个问题给端了出来(因此我估计这个问题此前已经有很多人骚扰过Ingo等同学了):
"Why on earth do we need a new mutex subsystem, and what's wrong with semaphores?"
前面已经讲过,当struct
semaphore中的成员变量为1,就可以用来实现mutex这种东西,而且内核也明确定义了DEFINE_SEMAPHORE宏将count初始化为
1,信号量上的DOWN与UP操作就更不用说了,在内核中也都有很好的实现,难道这种binary
semaphore机制还不能满足我们的要求吗,干嘛还非得弄一个新的mutex机制出来呢?
下面是Ingo同学对此的解释,他说“firstly, there's nothing wrong with semaphores. But if
the simpler mutex semantics are sufficient for your code, then there
are a couple of advantages of
mutexes”,就是说,信号量在Linux中的实现是没任何问题的(上来先安抚一下大家躁动的心情),但是mutex的语义相对来说要较信号量要来得
简单,所以如果你的代码若只是想对某一共享资源进行互斥访问的话,那么使用这种简化了的mutex机制可以带来如下的一坨好处。对这句话我的理解
是,mutex将binary semaphore的实现简化了(the simper
mutex),因此如果单纯从互斥的角度,用mutex会有很多好处。
接下来Ingo列出的一大堆使用mutex的好处,在这个帖子中我们将一条一条地来看,再结合内核源码,看看事实是否的确象他说的那样:
- 'struct mutex' is smaller on most architectures: E.g. on x86, 'struct
semaphore' is 20 bytes, 'struct mutex' is 16 bytes. A smaller structure
size means less RAM footprint, and better CPU-cache utilization.
这条最好验证,尤其还是x86平台,找个简单的内核模块,打印一下sizeof就可以了。在我的x86-64
32位Linux系统(内核版本2.6.37)上, struct semaphore的大小是16字节,而struct
mutex的大小则是20字节,另两台x86-64 64位Linux系统(内核版本3.x)上的结果则是,struct
semaphore的大小是24字节,而struct mutex的大小则是32字节。这里不妨看一下struct mutex在内核中的定义:
&include/linux/mutex.h&struct mutex {
& & & & /* 1: unlocked, 0: locked, negative: locked, possible waiters */
& & & & atomic_t& & & & & & & &
& & & & spinlock_t& & & & & & & & wait_
& & & & struct list_head& & & & wait_
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP)
& & & & struct task_struct& & & & *
#ifdef CONFIG_DEBUG_MUTEXES
& & & & const char & & & & & & & & *
& & & & void& & & & & & & & & & & & *
#ifdef CONFIG_DEBUG_LOCK_ALLOC
& & & & struct lockdep_map& & & & dep_
};复制代码可以看到stuct mutex的定义其实比semaphore要来得复杂,里面有一些条件编译选项在里面。因为我们实际使用当中很少会使用它的调试功能,但是SMP现在则很普遍,我上面测试用的Linux环境都是多处理器系统。所以,mutex的定义实际上可简化为:struct mutex {
& & & & /* 1: unlocked, 0: locked, negative: locked, possible waiters */
& & & & atomic_t& & & & & & & &
& & & & spinlock_t& & & & & & & & wait_
& & & & struct list_head& & & & wait_
& & & & struct task_struct& & & & *
};复制代码对
比一下前面struct semaphore的定义你会发现,struct
mutex比semaphore多了一个owner指针,因此上面的结果也就不难理解了,指针在32位系统上是4字节,而64位系统则是8字节。我相信
Ingo同学肯定不会胡说八道,那么明显地,相对于Ingo当时写mutex-design.txt时的情形,Linux内核源码发生了变化,这个在
Linux的开发过程中实在是太正常不过的一件事了:文档总是远远落后于代码的更新--大家都忙着写code,而很少有人想着去更新文档。
所以接下来Ingo提到的tighter code的优势,估计对mutex而言也不复存在了...
(他本人对mutex相对于semaphore在RAM footprint方面的优势不复存在的最新回复是:"Mutex got larger
due to the adaptive spin-mutex performance
optimization",因此我很自然地将这句话理解成,由于要实现所谓的“adaptive spin-mutex performance
optimization",那么就不惜牺牲了"less RAM footprint, and better CPU-cache
utilization",所以我们有理由期待接下来的spin-mutex performance
optimization会给mutex带来性能上的飞跃...)
TA的最新馆藏
喜欢该文的人也喜欢网摘1:Mutex 的发音是 /mjuteks/ ,其含义为互斥(体),这个词是Mutual Exclude的缩写。Mutex在计算机中是互斥也就是排他持有的一种方式,和信号量-Semaphore有可以对比之处。有人做过如下类比:&&& * Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把钥匙交给队列的第一个。一般的用法是用于串行化对critical section代码的访问,保证这段代码不会被并行的运行。&&& * Semaphore是一件可以容纳N人的房间,如果人不满就可以进去,如果人满了,就要等待有人出来。对于N=1的情况,称为binary semaphore。一般的用法是,用于限制对于某一资源的同时访问。对于Binary semaphore与Mutex,这两者之间就存在了很多相似之处:&&& 在有的系统中Binary semaphore与Mutex是没有差异的。在有的系统上,主要的差异是mutex一定要由获得锁的进程来释放。而semaphore可以由其它进程释放(这时的semaphore实际就是个原子的变量,大家可以加或减),因此semaphore可以用于进程间同步。Semaphore的同步功能是所有系统都支持的,而Mutex能否由其他进程释放则未定,因此建议mutex只用于保护critical section。而semaphore则用于保护某变量,或者同步。
网摘2:&mutex与semaphore的区别"互斥(mutext)和旗语(semaphore)之间有什么不同?"这样的问题简短而有力,但要回答却相当困难.即使有经验的实时操作系统(RTOS)用户在区别如何正确使用mutex和semaphore时也存在着困难.但这一点很不幸而且很危险,因为无任这两种原生RTOS中的哪一种被错误使用,都会导致嵌入式系统出现意想不到的错误,特别是这些系统为有关生命安全的产品时.有关mutex和semaphore的荒诞说法是它们是相似的,甚至是可以互换的.正确的事实是尽管mutex和semaphore在它们的执行上有相似之处,但是我们还是应该在使用它们时加以区别对待.最
普遍(但也是不正确)的答案是:mutex和semphore非常相似,它们只有一个区别,那就是semaphores的计数可以超过1.
差不多所有的工程师都能正确的理解:mutex是一个二进制标志,可以通过它来确保执行流在代码关键区(critical section of code)互相排斥,从而对共享资源加一保护.但当他们被要求进一步回答如何使用"计算方法semaphore"的方式时,大部分工程师的回答就如同教科书书一般的刻板---semaphore用于保护多重同类资源.通
过类比办法,我们很容易解释为什么"多重资源"场景是有缺陷的.如果你认为一个
mutex是由操作系统拥有的关键值的话,我们可以很容易地将个别的mutex比喻是城市咖啡店中一间浴室的钥匙.如果你想使用浴室,却找不到钥匙,你就
必须在一个队列中等候.同样地,mutex则协串行化多项任务,以取得全域资源的共享,并且为等待队列中的任务分配一个静候其循序渐进的位置.但这种简单的资源保护协议并不使用于两间相同浴室的情况.如果把一个semaphore概括为一个mutex,使其能保护两个或更多相同的资源,那么在我们的比喻中,它就象是放着两把相同钥匙的蓝子,你可以用任何一把打开任何一扇浴室的门.因此,semaphore本身并不能解决多个相同资源的问题.咖啡店中的客人可能只知道有一把钥匙,但并不知道哪间浴室可用.如果你试图以此方式使用semaphore,你将会发现需要更多的状态信息---它们通常是由不同的mutex所保护的共享资源.正确使用semaphore是为了使信号从一项任务传至另一项任务.mutex意味着取得与释放,使用受保护共享资源的每一次任务都是以这样的顺序进行.相比之下,使用semaphore的任务通常不是发送信号,就是进入等待状态,不可能同时发生.例如,任务1可能包含程序代码,当按下"电源"(power)按钮时,即可提出(如发送信号或增量)一个特别的 任务2则依据相同的semaphore而用于唤醒显示器. 在这种情况下,其中一项任务是信号的生产者,另一项任务是信号的消费者.
用一个例子来做总结,首先展示如何使用mutex:/* Task 1 */mutexWait(mutex_mens_room);// Safely use shared resourcemutexRelease(mutex_mens_room);
/* Task 2 */mutexWait(mutex_mens_room);// Safely use shared resourcemutexRelease(mutex_mens_room);
相应地,你总是采用下列方法使用semaphore:/* Task 1 - Producer */semPost(sem_power_btn); // Send the signal
/* Task 2 - Consumer */semPend(sem_power_btn); // Wait for signal
要的是,semaphores可以被interrupt service
routine(ISR)中断服务程序用来向task发送信号.发送一个semaphore是一个非阻塞的RTOS行为,并且ISR安全.因为这种技术排
除了在task级别的为了是中断不使能而引起的错误的可能性,从ISR中发出信号是一种使嵌入式软件更加可靠的设计方式.
阅读(...) 评论()}

我要回帖

更多关于 dispatch semaphore t 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信