实现基于简单clone()的多线程库的好方法是什么?

时间:2013-06-10 08:01:46

标签: c linux multithreading

我正在尝试使用clone()和其他内核实用程序构建基于linux的简单多线程库。我已经到了一个点,我不确定什么是正确的做事方式。我试过通过原始的NPTL代码,但它有点太多了。

这就是我想象的创建方法:

typedef int sk_thr_id;
typedef void *sk_thr_arg;
typedef int (*sk_thr_func)(sk_thr_arg);


sk_thr_id sk_thr_create(sk_thr_func f, sk_thr_arg a){

  void* stack;

  stack = malloc( 1024*64 );
  if ( stack == 0 ){
         perror( "malloc: could not allocate stack" );
         exit( 1 );
  }

  return ( clone(f, (char*) stack + FIBER_STACK, SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, a ) );


}

1:我不确定正确的clone()标志应该是什么。我刚刚在一个简单的例子中发现了这些。欢迎任何一般性指示。

以下是使用futexes创建的互斥原语的一部分(现在不是我自己的代码):

#define cmpxchg(P, O, N) __sync_val_compare_and_swap((P), (O), (N))

#define cpu_relax() asm volatile("pause\n": : :"memory")

#define barrier() asm volatile("": : :"memory")


static inline unsigned xchg_32(void *ptr, unsigned x)
{
    __asm__ __volatile__("xchgl %0,%1"
                :"=r" ((unsigned) x)
                :"m" (*(volatile unsigned *)ptr), "0" (x)
                :"memory");

    return x;
}


static inline unsigned short xchg_8(void *ptr, char x)
{
    __asm__ __volatile__("xchgb %0,%1"
                :"=r" ((char) x)
                :"m" (*(volatile char *)ptr), "0" (x)
                :"memory");

    return x;
}



int sys_futex(void *addr1, int op, int val1, struct timespec *timeout, void *addr2, int val3)
{
    return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
}




typedef union mutex mutex;

union mutex
{
    unsigned u;
    struct
    {
        unsigned char locked;
        unsigned char contended;
    } b;
};


int mutex_init(mutex *m, const pthread_mutexattr_t *a)
{
    (void) a;
    m->u = 0;
    return 0;
}

int mutex_lock(mutex *m)
{
    int i;

    /* Try to grab lock */
    for (i = 0; i < 100; i++)
    {
        if (!xchg_8(&m->b.locked, 1)) return 0;

        cpu_relax();
    }

    /* Have to sleep */
    while (xchg_32(&m->u, 257) & 1)
    {
        sys_futex(m, FUTEX_WAIT_PRIVATE, 257, NULL, NULL, 0);
    }

    return 0;
}

int mutex_unlock(mutex *m)
{
    int i;

    /* Locked and not contended */
    if ((m->u == 1) && (cmpxchg(&m->u, 1, 0) == 1)) return 0;

    /* Unlock */
    m->b.locked = 0;

    barrier();

    /* Spin and hope someone takes the lock */
    for (i = 0; i < 200; i++)
    {
        if (m->b.locked) return 0;

        cpu_relax();
    }

    /* We need to wake someone up */
    m->b.contended = 0;

    sys_futex(m, FUTEX_WAKE_PRIVATE, 1, NULL, NULL, 0);

    return 0;
}

2:我的主要问题是如何实现“join”原语?我知道它也应该基于futexes。现在想出一些东西对我来说很难。

3:在线程完成后我需要一些方法来清理东西(比如分配的堆栈)。我也无法做到这一点。

对于这些我可能需要在用户空间中为每个线程提供额外的结构,并在其中保存一些信息。有人能指出我解决这些问题的良好方向吗?

4:我想知道一个线程运行了多长时间,自上次调度以来有多长时间以及其他类似的东西。是否有一些内核调用提供此类信息?

提前致谢!

2 个答案:

答案 0 :(得分:2)

可以存在“多线程库”作为与标准库的其余部分分开的第三方库的想法是过时且有缺陷的概念。如果你想这样做,你将不得不首先放弃使用标准库;特别是,如果您自己致电malloc,则致clone的电话完全不安全,因为:

  1. malloc将不知道存在多个线程,因此可能无法执行正确的同步。

  2. 即使知道它们存在,malloc也需要访问位于线程指针给定地址的未指定的特定于实现的结构。由于此结构是特定于实现的,因此您无法创建这样的结构,系统的libc的当前版本和所有未来版本都将正确解释该结构。

  3. 这些问题不仅适用于malloc,也适用于大多数标准库;甚至异步信号安全函数也可能不安全,因为它们可能会取消引用线程指针以实现与取消相关的目的,执行最佳的系统调用机制等。

    如果你真的坚持自己的线程实现,你将不得不放弃使用glibc或任何与线程集成的现代libc,而是选择像klibc更天真的东西。这可能是一个教育实验,但它不适合部署的应用程序。

答案 1 :(得分:0)

1)您正在使用LinuxThreads的示例。我不会重写好的方向参考,但我建议你迈克尔·克里斯克的“Linux编程接口”,第28章。它在25页中解释了你需要的东西。

2)如果设置了CLONE_CHILD_CLEARID标志,则当子节点终止时,克隆的ctid参数将被清除。如果将该指针视为futex,则可以实现join原语。祝你好运:-)如果你不想使用futexes,还要看看wait3和wait4。

3)我不知道你想要清理什么,但你可以使用克隆tls arugment。这是一个线程本地存储缓冲区。如果线程已完成,则可以清理该缓冲区。

4)参见getrusage。