开发者

clone syscall and mutual exclusion

I'm trying to practice with clone syscall and a little example of an incrementing counter shared by 2 threads. Code is as follows:

#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;
}

execution result is:

initial n = 0
final n = 199999

should be 200000, so mutual exclusion by means of raising priority fails, why?


There are several things wrong here:

  • Several SCHED_FIFO processes can run simultaneously on several CPUs.
  • SCHED_FIFO processes may/will be killed if they exceed the RLIMIT_RTTIME soft/hard limit.
  • There's nothing preventing reordering of instructions by the compiler.
  • There's nothing preventing reordering of instructions by the CPU.


According to the man page for clone(), the stack should point to the last address in allocated memory. Your reference of "stack + STACK_SIZE" is actually beyond the allocated memory.


The clone() syscall (particularly the CLONE_VM flag) is not intended for direct use by application programmers creating threads. It is a low-level interface, intended for library authors creating full threading implementations. Similarly, sychronisation primitives for these threads can be built on top of the low-level futex() system call, but again this is not intended for direct use by application programmers.

Instead, you should be using pthreads. Under pthreads, use pthread_create() instead of clone(); pthread_join() instead of wait(); and pthread_mutex_lock() / pthread_mutex_unlock() to protect critical sections. The pthreads version of your program would look like:

#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;
}

Compile with the -pthread flag to gcc.


First of all, thanks to everybody for pointers, insights and ideas, I always learn from you. As an academic exercise, I was trying to guarantee mutual exclusion by rising priorities, which, obviously, only works for uniprocessors.

As ninjalj pointed out the code doesn't work if "Several SCHED_FIFO processes can run simultaneously on several CPUs"

The solution to make this code work is simply to run executable on only one CPU:

...$ taskset -c 0 puerta-clone

works well, regards

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜