克隆系统调用和互斥

发布于 2024-10-26 10:17:20 字数 1738 浏览 1 评论 0原文

我正在尝试使用克隆系统调用和一个由 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,所以通过提高优先级的方式进行互斥失败,为什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

待天淡蓝洁白时 2024-11-02 10:17:20

这里有几个问题:

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

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.
执手闯天涯 2024-11-02 10:17:20

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

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.

奢欲 2024-11-02 10:17:20

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。

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.

我乃一代侩神 2024-11-02 10:17:20

首先,感谢大家的指点、见解和想法,我总是向你们学习。
作为一项学术练习,我试图通过提高优先级来保证互斥,显然,这仅适用于单处理器。

正如 ninjalj 指出的那样,如果“多个 SCHED_FIFO 进程可以在多个 CPU 上同时运行”,则该代码将无法工作。

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

...$taskset -c 0 puerta-clone

效果很好,问候

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文