Как выгрузить модуль ядра, создавший kproc/kthread во FreeBSD

Я хочу выгрузить модуль с потоками. Я сослался на коды в dev/random, и мой код выглядит так:

$ cat tmp.c 

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>

/* 
 * $ ps auxH | grep kproc 
 */ 

static int kproc_control = 1; 

#define output_id(p, td, fmt, args...)                                  \ 
        printf("%s[%d]:%s[%d]:[%s] %s\n", p->p_comm, p->p_pid,          \ 
            td->td_name, td->td_tid, __func__, msg) 

static void thread_routine(void *arg) 
{ 
        char *msg = arg; 
        struct thread *td = curthread; 
        struct proc *p = td->td_proc; 

        output_id(p, td, msg); 
        pause("-", hz * 100); 
        output_id(p, td, msg); 

        kthread_exit(); 
} 

static void proc_routine(void *arg) 
{ 
        char *msg = arg; 
        struct thread *td = curthread; 
        struct proc *p = td->td_proc; 
        struct thread *ntd; 
        int error; 

        output_id(p, td, msg); 

        error = kthread_add(thread_routine, "I'm kthread", p, &ntd, 
            0, 0, "kthread"); 
        if (error) 
                printf("error: %d\n", error); 

        while (kproc_control >= 0) { 
                pause("-", hz / 10); 
        } 

        wakeup(&kproc_control); 
        kproc_exit(0); 
} 

static int foobar_init(void) 
{ 
        int error; 
        struct proc *p; 

        error = kproc_create(proc_routine, "I'm kproc", &p, 0, 0, "kproc"); 
        uprintf("error: %d\n", error); 

        return error; 
} 

static void foobar_fini(void) 
{ 
        kproc_control = -1; 
        tsleep(&kproc_control, 0, "term", 0); 
        //pause("delay", 2 * hz); 
} 

static int 
foobar_modevent(module_t mod __unused, int event, void *arg __unused) 
{ 
        int error = 0; 

        switch (event) { 
        case MOD_LOAD: 
                error = foobar_init(); 
                break; 
        case MOD_UNLOAD: 
                foobar_fini(); 
                break; 
        default: 
                error = EOPNOTSUPP; 
                break; 
        } 

        return (error); 
} 

static moduledata_t foobar_mod = { 
        "foobar", 
        foobar_modevent, 
        NULL 
}; 

DECLARE_MODULE(foobar, foobar_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 

Когда я выгружаю его с помощью kldunload, у меня происходит сбой ядра и перезагрузка системы. Как правильно решить эту проблему? Любые комментарии будут оценены! ;-)

PS. Могу ли я спать на &p->p_stype? Я вижу следующие коды в exit1():

    /*
     * Note that we are exiting and do another wakeup of anyone in
     * PIOCWAIT in case they aren't listening for S_EXIT stops or
     * decided to wait again after we told them we are exiting.
     */
    p->p_flag |= P_WEXIT;
    wakeup(&p->p_stype);

PS. Обновленные коды:

#include <sys/param.h>
#include <sys/module.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/kthread.h>
#include <sys/lock.h>
#include <sys/mutex.h>

/*
 * $ ps auxH | grep kproc
 */

static int kproc_control = 1;
static struct proc *foobar_proc;
static struct mtx mtx;

#define output_id(p, td, fmt, args...)                                  \
        printf("%s[%d]:%s[%d]:[%s] %s\n", p->p_comm, p->p_pid,          \
            td->td_name, td->td_tid, __func__, msg)

static void thread_routine(void *arg)
{
        char *msg = arg;
        struct thread *td = curthread;
        struct proc *p = td->td_proc;

        output_id(p, td, msg);
        pause("-", hz * 100);
        output_id(p, td, msg);

        kthread_exit();
}

static void proc_routine(void *arg)
{
        char *msg = arg;
        struct thread *td = curthread;
        struct proc *p = td->td_proc;
        struct thread *ntd;
        int error;

        output_id(p, td, msg);

        error = kthread_add(thread_routine, "I'm kthread", p, &ntd,
            0, 0, "kthread");
        if (error)
                printf("error: %d\n", error);

        mtx_lock(&mtx);
        while (kproc_control >= 0) {
                mtx_unlock(&mtx);
                pause("-", hz / 10);
                mtx_lock(&mtx);
        }
        mtx_unlock(&mtx);
        kproc_exit(0);
}
static int foobar_init(void)
{
        int error;

        mtx_init(&mtx, "foobar_mtx", NULL, MTX_DEF);
        error = kproc_create(proc_routine, "I'm kproc", &foobar_proc, 0, 0, "kproc");
        uprintf("error: %d\n", error);

        return error;
}

static void foobar_fini(void)
{
        mtx_lock(&mtx);
        kproc_control = -1;
        //mtx_sleep(foobar_proc, &mtx, 0, "waiting", 0);
        mtx_sleep(&foobar_proc->p_stype, &mtx, 0, "waiting", 0);
}

static int
foobar_modevent(module_t mod __unused, int event, void *arg __unused)
{
        int error = 0;

        switch (event) {
        case MOD_LOAD:
                error = foobar_init();
                break;
        case MOD_UNLOAD:
                foobar_fini();
                break;
        default:
                error = EOPNOTSUPP;
                break;
        }

        return (error);
}

static moduledata_t foobar_mod = {
        "foobar",
        foobar_modevent,
        NULL
};

DECLARE_MODULE(foobar, foobar_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);

person user1907234    schedule 16.12.2012    source источник


Ответы (1)


В принципе, вы ждете потока или дескриптора процесса, чтобы убедиться, что он завершился.

Вы должны использовать какую-либо блокировку для синхронизации доступа к общим переменным, таким как ваш флаг kproc_control. Затем вы также можете использовать mtx_sleep для атомарного снятия блокировки и ожидания дескриптора процесса, чтобы избежать условий гонки при обработке событий завершения.

Модель, которую я использую, выглядит примерно так:

void proc(whatver)
{
    mtx_lock(&sc->m_lock);

    while (!sc->time_to_die) {
        mtx_unlock(&sc->m_lock);
        /* Do whatever */
        mtx_lock(&sc->m_lock);
    }

    mtx_unlock(&sc->m_lock);

    kproc_exit(0);
}

void detach(whatever)
{
    mtx_lock(&sc->m_lock);
    sc->time_to_die = 1;
    mtx_sleep(sc->m_proc, &sc->m_lock, 0, "waiting", 0);
    /* proc cleaned up, safe to continue */
}
person janm    schedule 16.12.2012
comment
Большое спасибо за ваши ценные комментарии. Но я думаю, что лучше спать на &sc-›m_proc-›p_stype. Потому что 'wakeup(sc-›m_proc)' выполняется в kproc_exit() до завершения работы любых других потоков, а 'wakeup(&sc-›m_proc-›p_stype)' выполняется в exit1() после завершения всех других потоков. Я попытался заснуть на sc-›m_proc (на самом деле mtx_sleep(foobar_proc, &mtx, 0, ожидание, 0);), но ядро ​​снова падает. Возможно, это связано с «паузой (-, hz * 100)» в thread_routine(). Однако, даже если я использую mtx_sleep(&foobar_proc-›p_stype, &mtx, 0, ожидание, 0), ядро ​​все равно падает. ;-( - person user1907234; 16.12.2012
comment
Нужно ли добавлять 'mtx_unlock(&sc-›m_lock);' перед 'kproc_exit(0);' в 'void proc(что угодно) {}'? - person user1907234; 16.12.2012
comment
Вам нужно дождаться всего, что вы начали. В вашем случае, я думаю, вы вызвали kthread_add() и kproc_create(). Вы должны дождаться выхода обоих из них. Я не рассматривал ваш другой код подробно, но вы зависите от таймеров, а не от правильного механизма синхронизации. - person janm; 16.12.2012