futex同步机制分析之一应用

article/2025/9/30 14:50:25

futex同步机制分析之一应用
一、多线程(进程)的同步机制
c++编程中最难的部分有哪些,估计绝大多数人都会首先提出来是多线程(进程)编程。为什么多线程编程难呢?一个主要的原因就是多线程的同步。在多线程同步中,(Linux平台Posix NPTL)中主要有三个同步手段:Semaphore(信号灯)、Mutex(互斥体)和Condition Variables(条件变量)。在c++11的标准中,同样也有包含有类似的相关数据类型(std::mutex,std::condition_variable)。 
由于CPU的基于流水加上指令顺序的重排(乱序)导致多线程的运行的机制在应用根本无法控制。但在一些具体的业务场景,又要求必须按顺序一步步来。这时候,就必须使用多线程的同步强制线程按要求来工作。但是在使用这些同步机制时,需要编程者清楚操作系统的进程线程调用机制和相关的数据在多线程间的传递进行控制的方式。而这些配合有一个不能准确到位,就可能会引起程序的崩溃。但是需要注意的是,崩溃并不是最可怕的,可怕是程序跑得很好,但是达不到设计的目的。还有更可怕的,程序跑得很好,正常的情况下也符合预期,但是在某个关键点上,程序就出错了。这样的例子,并不少见,一个比一个难以解决。于是,这也成了多线程编程令一般程序员谈之变色的一个主要原因。 
这次聚集于Linux平台下的多线程同步的底层同步实现机制futex.从这个方向上为解决同步问题做一点努力。 

二、Linux中的futex


1、多线程介绍

Futex(Fast Userspace Mutexes)做为一种快速同步机制,从linux 2.5.7开始支持,而c++编程中使用的glibc使用NPTL(Native POSIX Thread Library)做为自己的线程库。而NPTL基本实现了POSIX。 
在Linux早期系统中,并没有线程这个概念,线程的概念在Windows平台上应用居多。而Linux主要是使用Clone的方式来产生进程编程,这也是目前好多人仍然把线程叫做轻量级进程的一个缘由。 
最初,Linux中使用LinuxThreads利用进程来模拟用户空间的线程,但是比较不好的,它不符合Posix的标准,都可以理解的是,CPU在同一进程间的线程之间切换的速度要比多个进程间切换的速度要快。当然,它还有其它很多的缺点。这些缺点,导致开发人员对其并不是非常满意,所以后来IBM和REDHAT团队开始了两个新的实现即:NGPT(Next-Generation POSIX Threads)和NPTL,后来由于某些原因,前者不再维护,硕果仅存,就只有NPTL了。 
LinuxThreads目前很少再维护了,NPTL仍然在发展,不过,NPTL目前还有一些小的问题,比如在SMP架构的CPU上有时会有问题。 

2、早期同步机制


1) 实现方式

在早期的Linux系统中,多线程的同步分为两种机制,即内核空间机制和用户空间机制。在用户空间进行同步,首先需要使用原子指令将代码锁住,如果利用atomic的原子机制编写过代码,可能这些就很好理解了。 
这里举一个比喻,就好像一个高速的循环,不断的去查询一个标志(可以理解成一个整数:0表示没被锁上,1表示被锁住),所以这个又被形象的称为自旋锁。这时候,CPU是被独占的,如果长时间的CPU被独占,可想而知结果会是如何。所以使用这种机制是有一些限定要求的: 
临界区代码尽量控制在100行以内。 
不要调用系统函数(read等),调用其它函数也应尽量短小。 
不要有大循环,不要有类似sleep动作。 
大的内存复制时,memcpy等函数不建议使用。 
而在内核空间中,实现同步机制和用户空间中没有本质的区别,同样也是类似的使用原子指令操作,正是基于此,内核实现进程的等待和睡眠等功能。 

2)缺点

不知道大家对于条件变量的使用有没有踩过坑,在网上陈硕曾经总结过这些,形式化一下类似下面这种: 

void lock(int con) {
    while (!trylock(con)) {
        wait();  
    }
}


上面刚刚说过,多线程的无序性可能会导致trylock和wait间有一个空档,在这个空档期内如果有人释放了锁,这个进程基本就睡不醒了。有没有解决办法呢,有,但是比较复杂。可以利用sigsuspend来间接实现,这不是重点,不展开。有兴趣可以在网上查找相关资料。 
另外在早期的系统中,经常会使用内核对象来实现同步,这样就会导致一个问题,无论用户空间的进程是否实际需要同步,都得进内核空间中进行操作一番,而内核空间与用户空间的交互,代价还是比较大的。 

3、futex的特点

基于上面的种种不便,“物不平则鸣”,牛人们(Hubertus Franke, Matthew Kirkwood, Ingo Molnar and Rusty Russell,无一不是大神啊)创建了futex。这玩意是贯通内核空间和用户空间的一把利器。看看它的中文意义“快速用户空间互斥体”。意味着,快。 
Futex是怎么突出这个快的呢?在同步的线程间,开辟一段共享内存(mmap), futex的变量保存在这段共享内存中,当线程进入(退出)同步区域时,先查询共享内存的中futex变量,如果竞争没有出现,就只修改futex状态,不再进入内核空间调用。反之,还得进入内核控制wait(wake).这样,通过一小段共享内存在用户空间的检查,就极大的避免了不应该进入内核的陷阱。自然,在低频率竞争锁时,效率被大大提高了。 
通过上面的分析可以看出,Futex其实是可以看做内核空间和用户空间两种之间的一种过渡状态,或者类似于物理上的亚稳定状态,根据不同的情况,进行不两只的处理。 

三、Futex的使用


1、接口调用


#include <linux/futex.h>
#include <sys/time.h>
int futex (int *uaddr, int op, int val, const struct timespec *timeout,int *uaddr2, int val3);
#define __NR_futex              240


uaddr:用户态下共享内存的地址,里面存放的是一个对齐的整型计数器。 
op:操作类型。定义的有五种: 
FUTEX_WAIT(Linux 2.6.0): 检查uaddr中计数器的值是否为val(原子操作),如果是则让线程(进程,其下二者等同)休眠,直到FUTEX_WAKE或者超时(time-out)。线程将挂到uaddr相对应的等待队列上去。反之,返回失败及错误值error EAGAIN。 
FUTEX_WAKE(Linux 2.6.0): 最多唤醒val个等待在uaddr上线程。 
FUTEX_FD:用于文件句柄的同步,已经在linux2.6.26被移除,忽略。 
FUTEX_REQUEUE (Linux 2.6.0):和 FUTEX_CMP_REQUEUE 完成相同的功能,但是不检查val3即参数val3被忽略。
FUTEX_CMP_REQUEUE (Linux 2.6.7):检查uaddr是否等于val3,如果不等,返回error EAGAIN。反之,将唤醒最多val个等待者。如果等待线程数量大于val,则其它线程从uaddr的等待队列中删除并添加到uaddr2的等待队列中。val2参数指定uaddr2的等待线程的最大数量。 
val的典型值为0或1,指定为INT_MAX无意义,导致的结果是FUTEX_CMP_REQUEUE 操作和 FUTEX_WAKE 语义相同;val2的值一般为1或INT_MAX,指定为0是没有用的,这样导致 FUTEX_CMP_REQUEUE秋 FUTEX_WAIT语义相同。 
FUTEX_CMP_REQUEUE 较之 FUTEX_REQUEUE更为安全,由于前者会检查uaddr的值,从而防止特定条件下的竞态条件。FUTEX_CMP_REQUEUE 和 FUTEX_REQUEUE 都可以用来避免 FUTEX_WAKE 可能产生的“惊群”现象。 
值得提起注意的是,要区分开Futex的同步机制和Futex的系统调用的不同,前者既有用户空间层面的操作又有内核空间层面的操作。如果希望通过使用futex系统调用来实现性能的提升可能和直接使用自旋锁一样,没啥效果,搞不好还掉坑里去。 
futex的原子操作是使用用CAS(Compare and Swap)完成的,与平台相关。CAS是完成无锁队列的一个重要手段,如果写过无锁队列的应该会清楚。在x86平台利用 cmpxchg指令来完成。 

2、例子分析

这里用mutex来实现一个例子并用strace来查看futex的调用过程。

1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <time.h>
 4 #include <semaphore.h>
 5 #include <pthread.h>
 6 #include <unistd.h>
 7
 8 sem_t sem_a;
 9 pthread_mutex_t lock;
10 int num = 0;
11 void *thread_process(void *);
12 void *thread_mutex(void*);
13
14 int main(void)
15 {
16     int ret = 0;
17     pthread_t pHandle_;
18     pthread_t pHMutex_;
19     sem_init(&sem_a,0,1);
20
21     pthread_mutex_init(&lock,NULL);
22
23     ret = pthread_create(&pHandle_,NULL,thread_process,NULL);
24     ret = pthread_create(&pHMutex_,NULL,thread_mutex,NULL);
25     pthread_join(pHandle_,NULL);
26     pthread_join(pHMutex_,NULL);
27 }
28
29 void *thread_mutex(void *params)
30 {   
31     int total =0;
32     pthread_mutex_lock(&lock);
33     num++;
34     pthread_mutex_unlock(&lock);
35
36 }
37 void *thread_process(void *params)
38 {   
39     int total = 0;
40     sem_wait(&sem_a);
41     sleep(5);
42     
43     sem_getvalue(&sem_a,&total);
44     printf("sem value is:%d\n",total);
45     sem_post(&sem_a);
46 }     
 


一个非常简单的测试例程,下面用strace跟踪一下调用过程: 

......
1 6936  execve("./futex", ["./futex"], 0x7ffd06e47680 /* 54 vars */) = 0 <0.000261>
......
7 6936  mmap(NULL, 91985, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdcac494000 <0.000032>
8 6936  close(3)                          = 0 <0.000030>
9 6936  access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000032>
10 6936  openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3 <0.000034>
11 6936  read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0000b\0\0\0\0\0\0"..., 832) = 832 <0.000032>
12 6936  fstat(3, {st_mode=S_IFREG|0755, st_size=144976, ...}) = 0 <0.000031>
13 6936  mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdcac492000 <0.000031>
14 6936  mmap(NULL, 2221184, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdcac065000 <0.000032>
15 6936  mprotect(0x7fdcac07f000, 2093056, PROT_NONE) = 0 <0.000033>
16 6936  mmap(0x7fdcac27e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19000) = 0x7fdcac27e000 <0.000041>
17 6936  mmap(0x7fdcac280000, 13440, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdcac280000 <0.000027>
18 6936  close(3)                          = 0 <0.000036>
19 6936  access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) <0.000033>
20 6936  openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000043>
21 6936  read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832 <0.000036>
 
22 6936  fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0 <0.000067>
23 6936  mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdcabc74000 <0.000033>
24 6936  mprotect(0x7fdcabe5b000, 2097152, PROT_NONE) = 0 <0.000033>
25 6936  mmap(0x7fdcac05b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fdcac05b000 <0.000035>
26 6936  mmap(0x7fdcac061000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdcac061000 <0.000033>
27 6936  close(3)                          = 0 <0.000030>
28 6936  mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdcac48f000 <0.000031>
29 6936  arch_prctl(ARCH_SET_FS, 0x7fdcac48f740) = 0 <0.000020>
30 6936  mprotect(0x7fdcac05b000, 16384, PROT_READ) = 0 <0.000034>
31 6936  mprotect(0x7fdcac27e000, 4096, PROT_READ) = 0 <0.000032>
32 6936  mprotect(0x561b1137c000, 4096, PROT_READ) = 0 <0.000032>
33 6936  mprotect(0x7fdcac4ab000, 4096, PROT_READ) = 0 <0.000032>
34 6936  munmap(0x7fdcac494000, 91985)     = 0 <0.000040>
35 6936  set_tid_address(0x7fdcac48fa10)   = 6936 <0.000030>
36 6936  set_robust_list(0x7fdcac48fa20, 24) = 0 <0.000030>
......
46 6936  mmap(NULL, 8392704, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x7fdcaac72000 <0.000032>
47 6936  mprotect(0x7fdcaac73000, 8388608, PROT_READ|PROT_WRITE <unfinished ...>
48 6937  set_robust_list(0x7fdcabc739e0, 24 <unfinished ...>
49 6936  <... mprotect resumed> )          = 0 <0.000022>
50 6936  clone( <unfinished ...>
51 6937  <... set_robust_list resumed> )   = 0 <0.000068>
52 6937  nanosleep({tv_sec=5, tv_nsec=0},  <unfinished ...>
53 6936  <... clone resumed> child_stack=0x7fdcab471fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=    0x7fdcab4729d0, tls=0x7fdcab472700, child_tidptr=0x7fdcab4729d0) = 6938 <0.000069>
54 6936  futex(0x7fdcabc739d0, FUTEX_WAIT, 6937, NULL <unfinished ...>
55 6938  set_robust_list(0x7fdcab4729e0, 24) = 0 <0.000068>
......                 
58 6938  +++ exited with 0 +++
59 6937  <... nanosleep resumed> 0x7fdcabc72e90) = 0 <5.001496>
60 6937  fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0 <0.000022>
61 6937  mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0x7fdca2c72000 <0.000025>
.....
68 6937  +++ exited with 0 +++
 
......
71 6936  +++ exited with 0 +++
 


从上面的跟踪来看,一共有三个线程6936,6937,6938,特别是后面两个子线程中,分别使用了信号量和互斥体,set_robust_list说明使用的robust规则。在新的glibc中,mmap创建共享内存,然后用保护mprotect对其进行控制。而非原来单一一个整形值。 
在这里可以看出,新的库里在这种无锁竞态的情况下已经不再调用内核的唤醒查看过程,这是和老的库的区别,估计也是觉得原来的到内核看一看比较不符合正常的逻辑,所以新的版本将其改正了。在robust规则下futex状态字段有三类,是否上锁,是否锁竞争,是否持锁线程死。 
是否上锁,为整型的0-29位,0代表无锁,非0代表上锁,并且上锁状态同时保存持锁线程的id号。 
是否持锁线程死,为整型30位。 
是否锁竞争,为整型31位。 
两个配对锁操作 lll_robust_lock 和 lll_robust_unlock。看它调用的锁: 

int
__lll_robust_lock (void \*ptr, int flags)
{
  int \*iptr = (int \*)ptr;
  int id = __getpid ();
  int wait_time = 25;
  unsigned int val;
 
  /* Try to set the lock word to our PID if it's clear. Otherwise,
     mark it as having waiters.  \*/
  while (1)
    {
      val = \*iptr;
      if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
        return 0;
      else if (atomic_compare_and_exchange_bool_acq (iptr,
               val | LLL_WAITERS, val) == 0)
        break;
    }
 
  for (id |= LLL_WAITERS ; ; )
    {
      val = \*iptr;
      if (!val && atomic_compare_and_exchange_bool_acq (iptr, id, 0) == 0)
        return 0;
      else if (val && !valid_pid (val & LLL_OWNER_MASK))
        {
          if (atomic_compare_and_exchange_bool_acq (iptr, id, val) == 0)
            return EOWNERDEAD;
        }
      else
        {
          lll_timed_wait (iptr, val, wait_time, flags);
          if (wait_time < MAX_WAIT_TIME)
            wait_time <<= 1;
        }
    }
}
void
__lll_robust_unlock (void *ptr, int flags)
{
  unsigned int val = atomic_load_relaxed ((unsigned int *)ptr);
  while (1)
    {
      if (val & LLL_WAITERS)
        {
          lll_set_wake (ptr, 0, flags);
          break;
        }
      else if (atomic_compare_exchange_weak_release ((unsigned int *)ptr, &val, 0))
        break;
    }
}
 


如果去和老版本比较,会发现增加了大量的条件判断。 

四、总结

通过上面的分析可以看出,futex是Linux内核支持的一个强大的同步机制,不过在内核和glibc的最新代码中可以看到,很多的实现都有了进一步的升级和改进。所以在下一篇,会针对其进行glibc源码级别的分析。 



http://chatgpt.dhexx.cn/article/rdrsmhbD.shtml

相关文章

FUTEX_SWAP补丁分析-SwitchTo 如何大幅度提升切换性能?

作者简介 胡哲宁&#xff0c;西安邮电大学计算机科学与技术专业大二学生。 Google SwitchTo 由于协程本身对操作系统的不可见性&#xff0c;协程中出现的 BUG 往往不能通过一些已有的工具去排查。在谷歌内部有一套闭源的用户态任务调度框架 SwitchTo, 这个框架可以为谷歌提供延…

futex问答

一、什么是futex&#xff1f; futex是Fast Userspace muTEX的缩写&#xff0c;该机制是由Rusty Russell、Hubertus Franke和Mathew Kirkwood在2.5.7版本的内核中引入&#xff0c;虽然名字中有互斥锁&#xff08;mutex&#xff09;的含义&#xff0c;但实际它是一种用于用户空间…

Futex系统调用,Futex机制,及具体案例分析

Futex 1、背景1.1 自己实现锁1.1.1 自旋锁1.1.2 sleep自旋1.1.3 小结 1.2 futex1.2.1 什么是Futex1.2.2 futex诞生之前1.2.3 futex诞生之后 2、Futex系统调用3、Futex机制4、具体案例分析4.1 在Bionic中的实现4.2 C语言实现 5、参考及扩展阅读 首先要区分一下futex系统调用和fu…

深度讲解futex问答(上)

一、什么是futex&#xff1f; futex是Fast Userspace muTEX的缩写&#xff0c;该机制是由Rusty Russell、Hubertus Franke和Mathew Kirkwood在2.5.7版本的内核中引入&#xff0c;虽然名字中有互斥锁&#xff08;mutex&#xff09;的含义&#xff0c;但实际它是一种用于用户空间…

数组中的字节数

##sizeof查看定义的数组所占用字节数 #include<iostream> int main(){using namespace std;int Inter [10];short sh [10];char ch [10];long lg [10];float fl [10];double dou [10];cout << "int style has " << sizeof Inter << " …

int转byte数组以及相关原理

前言 本文由int转byte数组这样的题目代码引发的思考&#xff0c;其中涉及到多个让我混淆的地方。 直接上代码 public byte[] toBytes(int number){byte[] bytes new byte[4];bytes[3] (byte)number;bytes[2] (byte) ((number >> 8) & 0xFF);bytes[1] (byte) ((…

java的byte数组的不同写法

经常看到java中对byte数组的不同定义&#xff0c;粗略整理的一下&#xff1a; 一个字节&#xff08;byte&#xff09;&#xff1d;8位&#xff08;bit&#xff09;&#xff0c;“byte数组”里面全部是“byte”&#xff0c;即每一个byte都可以用二进制、十六进制、十进制来表示。…

byte数组快速拷贝,byte数组合并,System.arraycopy详解

博客来源&#xff1a; 项目过程中用到byte[]数组相加问题&#xff0c;给出两个byte[] 需要合并成一个byte[]进行计算…那么需求来了……数据量达10W级&#xff0c;怎么合并 调用系统自带方法&#xff08;System.arraycopy&#xff09; 参考程序 org.junit.Test public void f…

程序、进程、线程的区别

程序、进程、线程的区别 进程是程序的实体&#xff0c;而线程又是进程的实体。进程又是线程的容器。 程序、进程、线程三者区别如下: 1.程序&#xff1a;程序并不能单独执行&#xff0c;是静止的&#xff0c;只有将程序加载到内存中&#xff0c;系统为其分配资源后才能够执…

操作系统-进程与线程的区别

操作系统-进程与线程的区别 1.什么是进程 简单的讲&#xff0c;进程是执行的程序&#xff0c;资源分配的最小单位。 进程包括&#xff1a;文本段&#xff08;程序代码&#xff09;、程序计数器的值、CPU寄存器的内容、堆、栈、数据段 进程的状态&#xff1a;新的、就绪、运行…

进程和线程的区别(重点)

来源&#xff1a;http://www.cnblogs.com/lmule/archive/2010/08/18/1802774.html 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程&#xff0c;使得多线程程序的并发性高。 另外&#xff0c;进程在执行过程中拥有独立的内存单元&#xff0c…

进程与线程的区别和联系

程序并不能单独执行&#xff0c;只有将程序加载到内存中&#xff0c;系统为他分配资源后才能够执行&#xff0c;这种执行的程序称之为进程&#xff0c;也就是说进程是系统进行资源分配和调度的一个独立单位&#xff0c;每个进程都有自己单独的地址空间。所以说程序与进程的区别…

进程和线程的区别和联系

我们都知道计算机的核心是CPU,它承担了所有的计算任务,而操作系统是计算机的管理者,它负责任务的调度,资源的分配和管理,统领整个计算机硬件;应用程序是具有某种功能的程序,程序是运行于操作系统之上的。 进程 进程是一个具有一定独立功能的程序在一个数据集上的一次动…

进程与线程的区别及联系

目录 1. 操作系统功能简介 2. 进程 2.1 认识进程 2.2 进程操作系统中如何管理 2.3 PCB如何描述 2.3.1 pid 2.3.2 内存指针 2.3.3 文件描述符表 2.3.4 进程调度相关属性 3. 内存管理 4. 线程 4.1 认识线程 4.2 进程与线程的关系 4.3 线程安全问题 1.操作系统功能简…

Linux进程与线程的区别

进程与线程的区别&#xff0c;早已经成为了经典问题。自线程概念诞生起&#xff0c;关于这个问题的讨论就没有停止过。无论是初级程序员&#xff0c;还是资深专家&#xff0c;都应该考虑过这个问题&#xff0c;只是层次角度不同罢了。一般程序员而言&#xff0c;搞清楚二者的概…

win10安装时,提示“我们无法创建新的分区,也找不到现有分区”

win10安装时&#xff0c;提示“我们无法创建新的分区&#xff0c;也找不到现有分区”&#xff0c;如图所示&#xff1a; 解决办法&#xff1a; 将win10安装包&#xff08;ios文件&#xff09;解压&#xff0c;将以下文件复制到系统盘&#xff0c;然后重启电脑&#xff0c;自动…

我们无法创建新分区。【错误:0x80042468】

一台服务器6块1.8T SAS 10K&#xff0c;做RAID10. 安装windows server 2012R2系统在分区时报错&#xff0c;总有个分区不能创建成功。&#xff08;正常安装系统后&#xff0c;磁盘管理也有一个磁盘不能创建新的分区&#xff09; 提示“我们无法创建新分区。【错误&#xff1a;0…

重装系统“无法创建新的分区也找不到现有分区”

如题&#xff0c;一开始我使用分区工具对硬盘进行分区后再进入安装系统&#xff0c;一直出现这个问题报错。 (因我没拍照片&#xff0c;从网上找的图片&#xff0c;侵权请联系我) 后来我删除硬盘分区&#xff0c;直接在这里进行分区&#xff0c;就可以装了。如图&#xff0c…

解决无法创建新的分区,也找不到现有的分区。

注意&#xff1a;问题多发生在m2装系统&#xff08;要拔出其他硬盘&#xff09; 原创b站理想与天阳