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
精彩评论