Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS 平台 动态库hook不生效 #238

Closed
shuai132 opened this issue Jul 4, 2022 · 21 comments
Closed

macOS 平台 动态库hook不生效 #238

shuai132 opened this issue Jul 4, 2022 · 21 comments

Comments

@shuai132
Copy link
Contributor

shuai132 commented Jul 4, 2022

目前的http::Client使用curl_easy_perform,这是一个阻塞接口。
使用单个协程调度器时,http请求将阻塞其他协程。

http::Client应该设计成非阻塞协程的,这才能体现协程的优势。推广到其他API,作为协程库里的实现,应当都设计为非阻塞的~

PS:

  1. 非阻塞可通过curl_multi_perform+辅助线程/协程(也许),或者有更好的办法(基于curl)?
  2. 很多测试貌似应该用单个协程调度器,比较容易发现阻塞的问题。
@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

@shuai132
事实上 http 请求不会阻塞其他协程,内部有 hook。

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

不是的,您可以试试,也许是curl内部实现并不是只简单调用的read/write。

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

不是的,您可以试试,也许是curl内部实现并不是只简单调用的read/write。

程序启动时可以加 -hook_log,看看 hook 日志,追踪下具体问题

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

顺便加上 -co_debug_log 打印协程调度相关的日志

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

这应该不是协程库的问题,猜测是curl_easy_perform的机制问题(比如有锁),我简单看了下curl的源码,还没能验证猜测。
因为已经正确hook这些常用的系统调用了,按理说是非阻塞的。

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

我还没能看明白curl里的设计。
如果您有时间,可以试试单元测试:

  1. --co_sched_num 1
  2. 多写几行go(fa);
    https://github.com/idealvin/cocoyaxi/blob/master/test/so/http_cli.cc#L69

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

我还没能看明白curl里的设计。 如果您有时间,可以试试单元测试:

  1. --co_sched_num 1
  2. 多写几行go(fa);
    https://github.com/idealvin/cocoyaxi/blob/master/test/so/http_cli.cc#L69

http::Client 不是线程安全的,不要 多个协程同时操作一个 client

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

只有一个协程操作自己的client~ 每个协程单独的http::Client
正如您的例子~

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

贴下代码看看

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

就是您的例子(简单改了几行):
注意要--co_sched_num 1
预期的是这三个请求并发。实际上他们将阻塞执行。
go(fa);
go(fa);
go(fa);

#include "co/all.h"

DEF_string(s, "https://github.com", "server url");
DEF_string(m, "", "method, GET, POST, DELETE, PUT");
DEF_string(url, "", "url of http request");
DEF_string(data, "{\"api\":\"ping\"}", "data to send");
DEF_string(path, "", "for PUT, path of the file to be uploaded");

void fa() {
    http::Client c(FLG_s.c_str());

    int r;
    LOG << "get /";
    c.get("/");
    r = c.status();
    LOG << "response code: " << r;
    LOG_IF(r == 0) << "error: " << c.strerror();
    LOG << "body size: " << c.body().size();
    LOG << "Content-Length: " << c.header("Content-Length");
    LOG << "Content-Type: " << c.header("Content-Type");
    LOG << c.header();

    // close the client before sending a signal
    c.close();
}

void fb() {
    http::Client c(FLG_s.c_str());
    COUT << FLG_m << " " << FLG_url;
    if (FLG_m == "GET") {
        c.get(FLG_url.c_str());
    } else if (FLG_m == "POST") {
        c.post(FLG_url.c_str(), FLG_data.c_str());
    } else if (FLG_m == "PUT") {
        if (!FLG_path.empty()) {
            c.put(FLG_url.c_str(), FLG_path.c_str());
        }
    } else if (FLG_m == "DELETE") {
        c.del(FLG_url.c_str());
    } else {
        LOG << "method not supported: " << FLG_m;
    }

    LOG << c.header();
    LOG << c.body();
    c.close();
}

int main(int argc, char** argv) {
    flag::init(argc, argv);
    FLG_cout = true;

    if (FLG_m.empty() && FLG_url.empty()) {
        go(fa);
        go(fa);
        go(fa);
    } else {
        go(fb);
    }

    sleep::sec(1000);
    return 0;
}

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

co_sched_num = 1,就是个单线程的东西,是不可能有并发的,只能串行,但一个协程的请求不会阻塞另外协程

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

我理解您说的,我所描述的是:
实际上一个协程的请求,确实阻塞其他协程了。因为它独占一个调度器的线程了。
您可以试一下便知:假如多个协程去各自请求,没有达到并发执行的效果,而是一个结束了,另一个才开始启动。如果最简单的模型来讲,我们hook住socket的read,是能做到并发的,事实上没有。

或者简单来说:
调度器设置一个线程。能否并发(当然了处理器是顺序执行)海量的http请求~ 这是协程的一个很大的优势,不知道您是不是没有把这个当成设计目标?还是说我们理解的偏差~

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

我理解您说的,我所描述的是: 实际上一个协程的请求,确实阻塞其他协程了。因为它独占一个调度器的线程了。 您可以试一下便知:假如多个协程去各自请求,没有达到并发执行的效果,而是一个结束了,另一个才开始启动。如果最简单的模型来讲,我们hook住socket的read,是能做到并发的,事实上没有。

或者简单来说: 调度器设置一个线程。能否并发(当然了处理器是顺序执行)海量的http请求~ 这是协程的一个很大的优势,不知道您是不是没有把这个当成设计目标?还是说我们理解的偏差~

理论上不会出现这种阻塞其他协程的问题,除非有 bug,我调试一下看看

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

嗯呐 感谢,那咱们应该想的一样啦~
也就是这个函数curl_easy_perform会阻塞执行完毕,其他协程才能得到执行。调试可以在它前后加日志。另外,要配调度器为一个线程,否则就使用其他线程了。

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

嗯呐 感谢,那咱们应该想的一样啦~ 也就是这个函数curl_easy_perform会阻塞执行完毕,其他协程才能得到执行。调试可以在它前后加日志。另外,要配调度器为一个线程,否则就使用其他线程了。

你在我们的微信群里吗?

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

木有,群号多少?

@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

木有,群号多少?

你给我发封邮件

@shuai132
Copy link
Contributor Author

shuai132 commented Jul 4, 2022

OK

@idealvin idealvin removed the question label Jul 4, 2022
@idealvin
Copy link
Owner

idealvin commented Jul 4, 2022

已经回复邮件了

@shuai132 shuai132 changed the title 非阻塞http client macOS 平台 动态库hook不生效 Jul 7, 2022
@shuai132
Copy link
Contributor Author

shuai132 commented Jul 7, 2022

找到原因:
在macOS平台,动态库hook未生效。
同样,当协程库本身动态库时,协程hook也都不生效。

@idealvin
Copy link
Owner

dlsym 那套 hook 机制在 mac 上不起作用,换成 fishhook,并修复 hook select 的 bug后,在 mac 上已经能正常运行了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants