首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么pthread_join()不能在mq_notify()创建一个可连接的线程之后呢?

为什么pthread_join()不能在mq_notify()创建一个可连接的线程之后呢?
EN

Stack Overflow用户
提问于 2021-06-04 13:49:08
回答 1查看 120关注 0票数 0

我正在学习LINUX编程接口这本书,并在这里感到困惑。我调用pthread_attr_getdetachstate()并发现由mq_notify()创建的线程是可连接的,但是如何将这个可缩放的线程(结束后) pthread_join()呢?我看到了手册页,但他不使用join。

EN

回答 1

Stack Overflow用户

发布于 2021-06-04 14:37:01

我只是看了一下glibcmq_notify源代码。

mq_notify使用PTHREAD_CREATE_DETACHED启动单个控制线程。因此,它是不可结合的。

pthread_tpthread_create的调用所设置的pthread_create值对您不可用。因此,无论您在pthread_attr_getdetachstate调用中使用什么值,都与mq_notify调用无关。

mq_notify创建的这个控制线程可以创建其他线程。但是,它们也使用pthread_detach(pthread_self())独立运行。

更新:

,我想知道OP是否在谈论使用SIGEV_THREAD接收消息通知。他们想知道如何管理调用其指定函数的线程,这是有意义的。-约翰·博林格

glibc包装函数mq_notify中..。

第一次调用mq_notify时,它创建一个用于通信的netlink套接字和一个控制线程。

在调用方的sigevent结构中,如果sigev_notifySIGEV_THREAD,则结构可以在sigev_notify_attributes中具有一个非空pthread_attr_t *值。这些属性被复制并通过内部mq_notify传递到实际的struct系统

这是因为尽管syscall可以通过内部clone调用创建一个线程,但它不能通过信号处理程序和pthread_*函数所做的所有事情(例如)来创建线程。内核无法设置pthread_t

因此,在此之后,内部struct最终通过netlink上的消息从内核发送到控制线程。控制线程的线程函数在下面的代码中:helper_function

控制线程将该消息/struct中的属性设置为pthread_create,调用它来启动每个消息通知线程。

但是..。pthread_create中指定的开始例程不是调用者的通知/线程函数。正如我前面提到的,它是另一个调用pthread_detach的包装函数(例如,pthread_detach)。然后调用用户的开始函数。

我可能误读了代码,但是,AFAICT,当用户的回调函数被调用时,它在一个已经被分离的单独线程中。

因此,同样,线程是不可连接的。pthread_detach调用是否更新了每条消息线程的属性,以便调用方能够完成:pthread_attribute_getdetachstate(pthread_self(),...)并获得这一点,这是一个有意义的问题。我们知道这是分离的。

因此,用户/调用者的线程函数本身不能pthread_join。而且,调用方的原始线程无法再管理通知线程,因为它是分离的,实际上没有任何事情可做或可以完成的任何事情。

以下是sysdeps/unix/sysv/linux/mq_notify.c的摘录

代码语言:javascript
运行
复制
/* The function used for the notification.  */
static void *
notification_function(void *arg)
{
    /* Copy the function and parameter so that the parent thread can go on with its life.  */
    volatile union notify_data *data = (volatile union notify_data *) arg;
    void (*fct) (union sigval) = data->fct;
    union sigval param = data->param;

    /* Let the parent go.  */
    (void) __pthread_barrier_wait(&notify_barrier);

    /* Make the thread detached.  */
    (void) pthread_detach(pthread_self());

    /* The parent thread has all signals blocked.  This is probably a bit surprising for this thread.  So we unblock all of them.  */
    (void) change_sigmask(SIG_UNBLOCK, NULL);

    /* Now run the user code.  */
    fct(param);

    /* And we are done.  */
    return NULL;
}

/* Helper thread.  */
static void *
helper_thread(void *arg)
{
    while (1) {
        union notify_data data;

        ssize_t n = __recv(netlink_socket, &data, sizeof(data),
            MSG_NOSIGNAL | MSG_WAITALL);

        if (n < NOTIFY_COOKIE_LEN)
            continue;

        if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP) {
            /* Just create the thread as instructed.  There is no way to report a problem with creating a thread.  */
            pthread_t th;

            if (__builtin_expect(pthread_create(&th, data.attr, notification_function, &data)
                    == 0, 0))
                /* Since we passed a pointer to DATA to the new thread we have to wait until it is done with it.  */
                (void) __pthread_barrier_wait(&notify_barrier);
        }
        else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
            /* The only state we keep is the copy of the thread attributes.  */
            free(data.attr);
    }
    return NULL;
}

static void
reset_once(void)
{
    once = PTHREAD_ONCE_INIT;
}

static void
init_mq_netlink(void)
{
    /* This code might be called a second time after fork().  The file descriptor is inherited from the parent.  */
    if (netlink_socket == -1) {
        /* Just a normal netlink socket, not bound.  */
        netlink_socket = __socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 0);
        /* No need to do more if we have no socket.  */
        if (netlink_socket == -1)
            return;
    }

    int err = 1;

    /* Initialize the barrier.  */
    if (__builtin_expect(__pthread_barrier_init(&notify_barrier, NULL, 2) == 0, 0)) {
        /* Create the helper thread.  */
        pthread_attr_t attr;

        (void) pthread_attr_init(&attr);
        (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
        /* We do not need much stack space, the bare minimum will be enough.  */
        (void) pthread_attr_setstacksize(&attr, __pthread_get_minstack(&attr));

        /* Temporarily block all signals so that the newly created thread inherits the mask.  */
        sigset_t oss;
        int have_no_oss = change_sigmask(SIG_BLOCK, &oss);

        pthread_t th;

        err = pthread_create(&th, &attr, helper_thread, NULL);

        /* Reset the signal mask.  */
        if (!have_no_oss)
            pthread_sigmask(SIG_SETMASK, &oss, NULL);

        (void) pthread_attr_destroy(&attr);

        if (err == 0) {
            static int added_atfork;

            if (added_atfork == 0 && pthread_atfork(NULL, NULL, reset_once) != 0) {
                /* The child thread will call recv() which is a cancellation point.  */
                (void) pthread_cancel(th);
                err = 1;
            }
            else
                added_atfork = 1;
        }
    }

    if (err != 0) {
        __close_nocancel_nostatus(netlink_socket);
        netlink_socket = -1;
    }
}

/* Register notification upon message arrival to an empty message queue
   MQDES.  */
int
mq_notify(mqd_t mqdes, const struct sigevent *notification)
{
    /* Make sure the type is correctly defined.  */
    assert(sizeof(union notify_data) == NOTIFY_COOKIE_LEN);

    /* Special treatment needed for SIGEV_THREAD.  */
    if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
        return INLINE_SYSCALL(mq_notify, 2, mqdes, notification);

    /* The kernel cannot directly start threads.  This will have to be done at userlevel.  Since we cannot start threads from signal handlers we have to create a dedicated thread which waits for notifications for arriving messages and creates threads in response.  */

    /* Initialize only once.  */
    pthread_once(&once, init_mq_netlink);

    /* If we cannot create the netlink socket we cannot provide SIGEV_THREAD support.  */
    if (__glibc_unlikely(netlink_socket == -1)) {
        __set_errno(ENOSYS);
        return -1;
    }

    /* Create the cookie.  It will hold almost all the state.  */
    union notify_data data;

    memset(&data, '\0', sizeof(data));
    data.fct = notification->sigev_notify_function;
    data.param = notification->sigev_value;

    if (notification->sigev_notify_attributes != NULL) {
        /* The thread attribute has to be allocated separately.  */
        data.attr = (pthread_attr_t *) malloc(sizeof(pthread_attr_t));
        if (data.attr == NULL)
            return -1;

        memcpy(data.attr, notification->sigev_notify_attributes, sizeof(pthread_attr_t));
    }

    /* Construct the new request.  */
    struct sigevent se;

    se.sigev_notify = SIGEV_THREAD;
    se.sigev_signo = netlink_socket;
    se.sigev_value.sival_ptr = &data;

    /* Tell the kernel.  */
    int retval = INLINE_SYSCALL(mq_notify, 2, mqdes, &se);

    /* If it failed, free the allocated memory.  */
    if (__glibc_unlikely(retval != 0))
        free(data.attr);

    return retval;
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67838401

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档