为什么此代码会导致"EXC_BAD_INSTRUCTION"?

use*_*627 21 iphone grand-central-dispatch ios

dispatch_semaphore_t aSemaphore = dispatch_semaphore_create(1);        
dispatch_semaphore_wait(aSemaphore, DISPATCH_TIME_FOREVER);
dispatch_release(aSemaphore);
Run Code Online (Sandbox Code Playgroud)

当程序运行到dispatch_release(aSemaphore)时,它将导致"EXC_BAD_INSTRUCTION",然后崩溃.为什么?

mat*_*way 40

我试过这个代码,确实因非法指令而死.所以我做了一些挖掘,发现它在_dispatch_semaphore_dispose中死亡.那么让我们来看看它是什么(ARMv7在这里,因为它很容易理解!):

__dispatch_semaphore_dispose:
000040a0            b590        push    {r4, r7, lr}
000040a2            4604        mov     r4, r0
000040a4            af01        add     r7, sp, #4
000040a6        e9d40108        ldrd    r0, r1, [r4, #32]
000040aa            4288        cmp     r0, r1
000040ac            da00        bge.n   0x40b0
000040ae            defe        trap
...
Run Code Online (Sandbox Code Playgroud)

它死在0x40ae,这是一个duff指令放在那里,如果bge.n它不会让我们分支跳过它崩溃.

它失败的原因是因为r0必须小于r1.r0并且r1从内存中加载r4 + 32已经回到堆栈中来计算它我认为 r4aSemaphore在示例代码中,即传入的内容dispatch_semaphore_release.该+ 32表示在读取32个字节到该结构体aSemaphore是指向(这是一个指向dispatch_semaphore_s结构).总的来说它正在做什么从它读取4个字节aSemaphore + 32并将它们放入r0并读取4个字节aSemaphore + 36并将它们放入r1.

比较,然后比较有效的价值aSemaphore + 32aSemaphore + 36.读什么dispatch_semaphore_create不,我可以看到它存储在双方传递的价值aSemaphore + 32aSemaphore + 36.我也发现dispatch_semaphore_waitdispatch_semaphore_signal触摸值aSemaphore + 32,增加和减少它.这意味着它被破坏的原因是因为信号量的当前值小于传入的值dispatch_semaphore_create.因此,当当前值小于其创建的值时,您无法处置信号量.

如果你已经读到这里并理解我的随意,那么做得好!希望能帮助到你!

更新:

在这里查看源代码(JustSid指出)可能更好 - http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c - 查看_dispatch_semaphore_dispose我们看到的函数:

if (dsema->dsema_value < dsema->dsema_orig) {
    DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use");
}
Run Code Online (Sandbox Code Playgroud)

所以,是的,你去了,这就是崩溃的原因!

  • 这是源文件,其中包含`_dispatch_semaphore_dispose()`http://opensource.apple.com/source/libdispatch/libdispatch-187.7/src/semaphore.c应该更容易阅读,还包括一些关于它死的原因的好文本. (2认同)

jkh*_*jkh 17

更简洁的答案:您正在使用错误的值创建信号量,它应该为零.使用值1创建它意味着您稍后释放仍然"正在使用"的信号量,并且GCD故意生成非法指令,以帮助您调试您拥有更多服务员信号量的事实.

  • 感谢4行版本.换句话说 - 确保你的`dispatch_semaphore_create()`的初始值是<=你希望信号量在你的dispatch_release()时得到的值.如果这意味着在创建它之后立即执行`dispatch_semaphore_signal()`,那就这样吧. (3认同)

cya*_*ide 5

您可以创建一个值为零的信号量,但我相信它毫无用处。我在一个类中有一个信号量字段,导致它在去初始化时崩溃。这就是我修复它的方法(Swift 代码):

deinit {
  while (dispatch_semaphore_signal(semaphore) != 0) {}
}
Run Code Online (Sandbox Code Playgroud)

一个相当尴尬的补丁,但它有效!