【问题标题】:How can I kill/end/cancel/join a socket blocking thread?如何杀死/结束/取消/加入套接字阻塞线程?
【发布时间】:2020-07-21 11:09:13
【问题描述】:

我创建了一个线程,可以读取手机发送的蓝牙消息。 当我意识到我无法安全地杀死那个线程时,我的问题就来了。到目前为止的方法是用户输入,它从该线程内调用exit(),当我将其移出该线程并进入主线程时,所有奇怪的事情都开始发生(检测到堆栈粉碎和分段错误)。显然,如果我有另一个用于 wifi 呼叫的线程或者只是不想完全退出所有内容,这种方法也不会真正很好地扩展。

这是我目前的代码。

将蓝牙相关内容传递给线程的结构:

typedef struct {
    struct sockaddr_rc loc_addr;
    struct sockaddr_rc rem_addr;
    socklen_t opt;
    char blu_buffer[1024];
    int blu_sock;    
} Bluetooth_stuff;

线程监听新连接:

void * BluetoothListiner(void * argv){

    Bluetooth_stuff * bt_set = (Bluetooth_stuff * ) argv;

    int bytes_read;
    int local_client;   
    int option = 0; 

    listen(bt_set->blu_sock, 1);
    memset(bt_set->blu_buffer, 0, sizeof(bt_set->blu_buffer));

    while(true){

        local_client = accept(bt_set->blu_sock, (struct sockaddr *)&((*bt_set).rem_addr), &((*bt_set).opt));        

        // read data from the client
        bytes_read = read(local_client, bt_set->blu_buffer, sizeof(bt_set->blu_buffer));
        if( bytes_read > 0 ) {
            // process data
            }   
        }
        else{
            // accept again?
        }       

        // clear the buffer
        memset(bt_set->blu_buffer, 0, sizeof(bt_set->blu_buffer));

        // close connection
        close(local_client);

        usleep(10);
    }
}

套接字配置:

void BluetoothSocketConfig(Bluetooth_stuff * bt_set){   

    // allocate socket
    bt_set->blu_sock = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);        
    bt_set->opt= sizeof(bt_set->rem_addr);

    (*bt_set).loc_addr.rc_family = AF_BLUETOOTH;
    (*bt_set).loc_addr.rc_bdaddr = *BDADDR_ANY;
    (*bt_set).loc_addr.rc_channel = (uint8_t) 1;        

    // bind socket to port 1 of the first available 
    bind(bt_set->blu_sock, (struct sockaddr *)&(bt_set->loc_addr), sizeof(bt_set->loc_addr));

}

测试主要:

int main(int argc, char *argv[]){       
    Bluetooth_stuff bt_set;     
    BluetoothSocketConfig(&bt_set);     
    pthread_create(&blu_listiner, NULL, BluetoothListiner, bt_set);
    while(1);  // I modify that for testing to exit, having a global flag also gave me segmentation errors somehow
}

调用exit(1) 不会清理其他线程,而来自另一个线程的pthread_killpthread_cancel 通常会导致进程仍在后台运行,从而阻止我再次运行应用程序而无需使用ps -A 手动杀死进程和kill -9 PROCESS

基于此article,我认为最好的方法是在线程中引入if 语句并将其pthread_exit 本身置于遗忘状态。如:

if(external_exit){
    ret2  = 200;
    pthread_exit(&ret2);
}

现在由于read() 是一个阻塞调用,如果没有外部发生的事情,我永远无法到达if 语句。这个answer 建议使用超时来实现这一点,我按照answer 的语法在一段时间后设法从read() 突破。所以我在线程的主while循环之前添加了这个(我认为这应该只定义一次,而不是在每个循环中,虽然不确定因为找不到具体的例子)。

现在我的问题是它会在第一个循环中阻塞read(),但随后会像什么都没有一样跳过它并无限循环。我试图通过重新调用accept() listen()bind() 来“重新启用read()”,但没有成功。似乎使用select() 是首先检查是否有要阅读的内容的首选选项,但如手册中所述

"在 Linux 下,select() 可能会报告一个套接字文件描述符为“ready 用于阅读”,而随后的读取块。这 例如,当数据到达但经过检查时可能发生 校验和错误并被丢弃"

因此,即使使用它(我不确定它是否可以像在 tcp 套接字上一样在蓝牙套接字上工作),它仍然有可能仍然卡在read() 上而无法安全地杀死它。

1) 如何安全地终止该线程(或任何阻塞线程)?
2) select() 是否真的需要阻塞方法,因为我没有多个连接?
3) 初始超时后,在阻塞模式下重新启用read() 正确使用的程序是什么?
4) 除了初始化不同之外,所有套接字基本上都等效 unix/can/tcp/bluetooth 吗?
5) 有什么其他方法可以得到我想要的结果或我的部分逻辑上有严重缺陷吗?

感谢您提供的任何帮助,如果有人有蓝牙服务器线程实现的链接,我会很高兴看到它(或任何其他套接字 unix/can/tcp)。

【问题讨论】:

  • read() 并不总是阻塞(尽管默认阻塞,这是 Unix 脑损伤,继续伤害每个人)。您可以将文件描述符设置为非阻塞,然后read()(或者更好地使用recvfrom()recvmsg())不会阻塞。
  • 这个问题太没有重点了。请把它剪成一个或分成几个。问题的当前标题is easy enough to answer,但正文在exit(错误)、“重新启用”read(什么?)、错误使用pthread_kill_cancel(错误)上引入了段错误,一个问题关于不同系列的套接字初始化等等。
  • accept 默认也是阻塞的,如果没有人尝试连接循环可能会卡住,所以相应地读取阻塞..

标签: c linux multithreading sockets bluez


【解决方案1】:
  1. 使用SOCK_NONBLOCK 打开套接字,并在其整个生命周期内保持非阻塞模式。
  2. 在初始化阶段,调用eventfd(0,0)创建另一个文件描述符。
  3. 不要阻塞read,而是设置并调用select 以等待 eventfd 文件描述符和套接字(传递NULL 用于超时)。这可能会永远阻塞。
  4. 选择返回时:
    • 如果套接字可读,请调用read。你是对的,select 可能发出了错误的信号,但在这种情况下,你会得到一个EAGAIN,并且可以循环回来并再次调用select。如果read 返回任何其他错误,请通过关闭连接等“处理”它。
    • 如果 eventfd 文件描述符可读,则关闭连接并执行正常关闭
  5. 在另一个线程上,您之前试图杀死阻塞的头,现在您可以write 到 eventfd 文件描述符以“中断”选择操作。

【讨论】:

    猜你喜欢
    • 2014-06-27
    • 1970-01-01
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多