【问题标题】:pthread_kill() with invalid thread带有无效线程的 pthread_kill()
【发布时间】:2017-05-23 16:44:07
【问题描述】:

我想确定某个特定线程是否“存在”。

pthread_kill() 似乎适合这个任务,至少根据它的man page

如果 sig 为 0,则不发送信号,但仍会进行错误检查。

或者,正如我系统的手册页所说:

如果sig为0,则不发送信号,但仍进行错误检查;这可用于检查线程 ID 是否存在。

但是,当我尝试传入未初始化的 pthread_t 时,应用程序总是 SEGFAULT。


深入研究,来自pthread_kill.c(来自我的工具链)的以下 sn-p 似乎做了 no 错误检查,并且只是尝试取消引用 threadid(取消引用在pd->tid)。

int
__pthread_kill (threadid, signo)
     pthread_t threadid;
     int signo;
{
  struct pthread *pd = (struct pthread *) threadid;

  /* Make sure the descriptor is valid.  */
  if (DEBUGGING_P && INVALID_TD_P (pd))
    /* Not a valid thread handle.  */
    return ESRCH;

  /* Force load of pd->tid into local variable or register.  Otherwise
     if a thread exits between ESRCH test and tgkill, we might return
     EINVAL, because pd->tid would be cleared by the kernel.  */
  pid_t tid = atomic_forced_read (pd->tid);
  if (__builtin_expect (tid <= 0, 0))
    /* Not a valid thread handle.  */
    return ESRCH;

我们甚至不能依赖零作为一个好的初始化器,原因如下:

# define DEBUGGING_P 0
/* Simplified test.  This will not catch all invalid descriptors but
   is better than nothing.  And if the test triggers the thread
   descriptor is guaranteed to be invalid.  */
# define INVALID_TD_P(pd) __builtin_expect ((pd)->tid <= 0, 0)

此外,我在链接的手册页中注意到以下内容(但不是在我的系统上):

POSIX.1-2008 建议,如果实现在其生命周期结束后检测到线程 ID 的使用,pthread_kill() 应返回错误 ESRCH。在可以检测到无效线程 ID 的情况下,glibc 实现会返回此错误。 但还要注意,POSIX 表示尝试使用生命周期已结束的线程 ID 会产生未定义的行为,并且在调用 pthread_kill() 时尝试使用无效的线程 ID 可以,例如,导致分段错误。


正如here by R.. 所述,我要求的是可怕的未定义行为

看来该手册确实具有误导性——尤其是在我的系统上。

  • 是否有一种好的/可靠的方法来询问是否存在线程? (可能使用pthread_kill()
  • 是否有一个好的值可以用来初始化pthread_t 类型变量,即使我们必须自己捕获它们?

我怀疑答案是使用pthread_cleanup_push() 并保留我自己的is_running 标志,但想听听其他人的想法。

【问题讨论】:

  • '我想确定一个特定的线程是否'存在'。你不能,至少不可靠。设计无所谓。
  • 听起来你可能对检查“特定线程”是否存在感到困惑,在这种情况下,pthread_t 应该使用从pthread_create() 为该线程返回的值初始化,使用“任何其他线程”存在,这是我可以想象的唯一语义含义,你传递一个未初始化的pthread_t,而且,正如你所见,它失败了......但是,正如@ThingyWotsit 所说,你会更好重新设计东西,这样就没有必要了……

标签: c pthreads


【解决方案1】:

我想我在开车回家的时候意识到了这一点,我怀疑很多其他人也会觉得这很有用...

看起来我一直将工作人员(线程)和任务(线程正在做什么)视为一回事,而实际上它们并非如此。

正如我已经从问题中的代码 sn-ps 确定的那样,问“这个线程是否存在”是不合理的,因为 pthread_t 可能只是一个指针(它肯定在我的目标上)。这几乎肯定是个错误的问题。

进程 ID、文件句柄、malloc()'d 内存等也是如此……它们不使用唯一且从不重复的标识符,因此不是可以测试其存在的唯一“实体” .

我在问题中提出的怀疑很可能是真的 - 我将不得不为 task(不是线程)使用类似 is_running 标志的东西。

我考虑过的一种方法是使用初始化为 sem_trywait()sem_post()pthread_cleanup_push() 的信号量,如下例所示(为简洁起见,缺少清理)。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>

struct my_task {
    sem_t can_start;
    pthread_t tid;

    /* task-related stuff */
};

void *my_task_worker(void *arg) {
    struct my_task *task = arg;

    pthread_cleanup_push(sem_post, &(task->can_start));

    fprintf(stderr, "--- task starting!\n");
    usleep(2500000);
    fprintf(stderr, "--- task ending!\n");

    pthread_cleanup_pop(1);

    return NULL;
}

void my_task_start(struct my_task *task) {
    int ret;

    ret = sem_trywait(&(task->can_start));
    if (ret != 0) {
        if (errno != EAGAIN) {
            perror("sem_trywait()");
            exit(1);
        }

        fprintf(stderr, ">>> task already running...\n");
        return;
    }

    ret = pthread_create(&(task->tid), NULL, my_task_worker, task);
    if (ret != 0) {
        perror("pthread_create()");
        exit(1);
    }

    fprintf(stderr, ">>> started task!\n");

    return;
}

int main(int argc, char *argv[]) {
    int ret;
    struct my_task task;
    int i;

    memset(&task, 0, sizeof(0));

    ret = sem_init(&(task.can_start), 0, 1);
    if (ret != 0)
    {
        perror("sem_init()");
        return 1;
    }

    for (i = 0; i < 10; i++) {
        my_task_start(&task);
        sleep(1);
    }

    return 0;
}

输出:

>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!
>>> task already running...
>>> task already running...
--- task ending!
>>> started task!
--- task starting!

【讨论】:

    猜你喜欢
    • 2016-02-21
    • 1970-01-01
    • 1970-01-01
    • 2018-01-06
    • 2019-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-24
    相关资源
    最近更新 更多