克隆系统调用和互斥

时间:2011-03-24 16:20:45

标签: c clone system-calls

我正在尝试使用clone syscall和一个由2个线程共享的递增计数器的示例。代码如下:

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#include <sched.h>

#include "error.h"

/*------------------------- Mutual exclusion ------------------------------*/

void EnterCZ()

{
   if (sched_setscheduler(getpid(), SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) == -1)
      SysError("EntrarZC:sched_setscheduler");
}

void ExitCZ()
{
   if (sched_setscheduler(getpid(), SCHED_OTHER, &(struct sched_param) { .sched_priority = 0 }) == -1)
      SysError("SalirZC:sched_setscheduler");
}

/*-------------------------------------------------------------------------*/

#define STACK_SIZE 65536
#define N 100000

int main(int argc, char *argv[])
{
   int n = 0;
   char *stack;

   int Child(void *args) {
      int i, temp;

      for (i = 0; i < N; i++) {
         EnterCZ();
         temp = n;
         temp++;
         n = temp;
         ExitCZ();
      }
      return 0;
   }

   printf("initial n = %d\n", n);

   if ((stack = malloc(STACK_SIZE)) == NULL)
      RTError("main:malloc");
   if (clone(Child, stack + STACK_SIZE, SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, NULL) == -1)
      SysError("main:clone");
   if ((stack = malloc(STACK_SIZE)) == NULL)
      RTError("main:malloc");
   if (clone(Child, stack + STACK_SIZE, SIGCHLD | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, NULL) == -1)
      SysError("main:clone");

   while (wait(NULL) != -1) ;

   printf("final n = %d\n", n);

   return 0;
}

执行结果是:

initial n = 0
final n = 199999

应该是200000,所以通过提高优先级互相排斥会失败,为什么?

4 个答案:

答案 0 :(得分:1)

这里有几个问题:

  • 多个SCHED_FIFO进程可以在多个CPU上同时运行。
  • 如果SCHED_FIFO进程超过RLIMIT_RTTIME软/硬限制,则可能/将被终止。
  • 没有什么能阻止编译器重新排序指令。
  • 没有什么能阻止CPU重新排序指令。

答案 1 :(得分:0)

根据clone()的手册页,堆栈应指向已分配内存中的最后一个地址。您对“stack + STACK_SIZE”的引用实际上超出了已分配的内存。

答案 2 :(得分:0)

clone()系统调用(特别是CLONE_VM标志)不适合创建线程的应用程序员直接使用。它是一个低级接口,供图书馆作者创建完整的线程实现。类似地,这些线程的同步原语可以构建在低级futex()系统调用之上,但同样不适合应用程序员直接使用。

相反,你应该使用pthreads。在pthreads下,使用pthread_create()代替clone(); pthread_join()代替wait();并pthread_mutex_lock() / pthread_mutex_unlock()保护关键部分。你的程序的pthreads版本看起来像:

#include <stdio.h>
#include <pthread.h>

#include "error.h"

/*-------------------------------------------------------------------------*/

#define N 100000

int main(int argc, char *argv[])
{
   int n = 0;
   pthread_mutex_t n_lock = PTHREAD_MUTEX_INITIALIZER;
   pthread_t child1, child2;

   void *Child(void *args) {
      int i, temp;

      for (i = 0; i < N; i++) {
         pthread_mutex_lock(&n_lock);
         temp = n;
         temp++;
         n = temp;
         pthread_mutex_unlock(&n_lock);
      }
      return 0;
   }

   printf("initial n = %d\n", n);

   if (pthread_create(&child1, NULL, Child, NULL) != 0)
      SysError("main:pthread_create");
   if (pthread_create(&child2, NULL, Child, NULL) != 0)
      SysError("main:pthread_create");

   pthread_join(child1, NULL);
   pthread_join(child2, NULL);

   printf("final n = %d\n", n);

   return 0;
}

使用-pthread标志编译为gcc。

答案 3 :(得分:0)

首先,感谢所有人的指导,见解和想法,我总是向你学习。 作为一项学术活动,我试图通过提高优先级来保证相互排斥,这显然只适用于单处理器。

正如ninjalj所指出的那样,如果“多个SCHED_FIFO进程可以在多个CPU上同时运行”,则代码不起作用

使此代码工作的解决方案只是在一个CPU上运行可执行文件:

... $ taskset -c 0 puerta-clone

效果很好,问候