44

协程 C/C++ 扩展开发指南(1):内存安全

 5 years ago
source link: https://segmentfault.com/a/1190000018533664?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Swoole4 协程的出现使得 PHP 底层上从原来串行模式变成了并发模式。有很多 PHP 的 C/C++ 扩展在开发时未能考虑到并发性、 可重入 问题,导致无法在 Swoole 协程中使用。本文会详细讲解如何编写协程并发安全的 C/C++ 代码。

可重入性

示例代码:

int t;

void test1(int *x, int *y) {
    t = *x;
    *x = *y;
    //fun1 函数中可能会存在协程切换
    fun1();
    //错误代码
    *y = t;
}
  • t 是一个全局变量或者 static 静态变量
  • 在协程 A 中调用了 test1 函数,使用了全局变量 t
  • 当函数内调用了 fun1() ,这个函数中如果发生了协程切换,这时假如另外一个协程 B 也执行了 test1 函数,那么 t 的值可能会被修改
  • 协程 B 挂起时,重新回到协程 A ,这时 *y = t ,会得到一个错误的值

引用栈内存

这也是一个严重的风险点。协程 1 将自身栈内存的指针发送给另外一个协程 2 ,协程 1 退出时会释放协程栈内存。协程 2 的生命周期长于 1 ,继续读写此内存,就会导致 segment fault

示例:

void co1() {
    char buf[2048];
    //这里启动一个新的协程,buf 是协程1栈上内存
    co2(buf);
    //协程1 退出时会释放栈内存
}

void co2(char *buf) {
    for(int i=0; i<2048; i++) {
        Coroutine::sleep(1);
        //这里 buf 内存可能已经释放了
        buf[i] = 1;
    }
}

协程安全代码

为了保证安全性,在 Swoole4 协程编程中:

  • 不要使用 static 变量和全局变量,坚持只用局部变量
  • 若必须访问全局变量,必须保证只用于计算逻辑,不得存在任何 IOSleep 等引起协程切换的操作
  • 不调用其它任何不可重入的函数
  • 不要引用栈上内存

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK