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

article/2025/9/30 14:49:52

作者简介

胡哲宁,西安邮电大学计算机科学与技术专业大二学生。

Google SwitchTo

由于协程本身对操作系统的不可见性,协程中出现的 BUG 往往不能通过一些已有的工具去排查。在谷歌内部有一套闭源的用户态任务调度框架 SwitchTo, 这个框架可以为谷歌提供延迟敏感的服务,对运行的内容进行细粒度的用户空间控制/调度,它可以让内核来实现上下文的切换,同时将任务何时切换,何时恢复的工作交给了用户态的程序来做,这样既可以实现在任务间协作式切换的功能,又可以不丧失内核对于任务的控制和观察能力。谷歌去年恢复尝试将其 SwitchTo API 上游引入 Linux。相关补丁见:[1],[2],[3],[4].

1pid_t switchto_wait(timespec *timeout)
2/*  Enter an 'unscheduled state', until our control is re-initiated by another thread or external event (signal). */
3void switchto_resume(pid_t tid)
4/* Resume regular execution of tid */
5pid_t switchto_switch(pid_t tid)
6/* Synchronously transfer control to target sibling thread, leaving the current thread unscheduled.Analogous to:Atomically { Resume(t1); Wait(NULL); }
7*/

这是使用 SwitchTo 和使用其他线程间切换的组件的上下文切换性能对比:

BenchmarkTime(ns)CPU(ns)Iterations
BM_Futex290519581000000
BM_GoogleMutex310223261000000
BM_SwitchTo1791783917412
BM_SwitchResume273415541000000

可以看到在使用 SwitchTo 后切换的性能比其他组件提高了一个数量级别。

SwitchTo 是如何做到在切换性能上大幅度领先的呢?我们暂时可能无法看到它们,但让我们来看看 Peter Oskolkov 向 LKML(Linux Kernel Mail List) 提出的补丁中有关 futex_swap() 的实现。可以确定的是,SwitchTo 构建在这个内核函数之上。

什么是 futex

futex 全称 fast user-space locking,快速用户空间互斥锁,作为内核中一种基本的同步原语,它提供了非常快速的无竞争锁获取和释放,用于构建复杂的同步结构:互斥锁、条件变量、信号量等。由于 futex 的一些机制和使用过于复杂,glibc 没有为 futex 提供包装器,但我们仍然可以使用 syscall 来调用这个 极其 hack 的系统调用。

1static int futex(uint32_t *uaddr, int futex_op, uint32_t val,
2                 const struct timespec *timeout, uint32_t *uaddr2,
3                 uint32_t val3)
4 {
5  return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
6}
  • uaddr: 一个四字节的用户空间地址。多个任务间可以通过 *uaddr 的值的变化来控制阻塞或者运行。

  • futex_op: 用于控制 futex 执行的命令 如 FUTEX_WAITFUTEX_WAKEFUTEX_LOCK_PIFUTEX_UNLOCK_PI

  • val: 在不同的 futex_op 具有不同的含义,如在 futex(uaddr, FUTEX_WAKE) 中作为唤醒等待在该 futex 上所有任务的数量。

  • timeout: 作为等待(如 FUTEX_WAIT)的超时时间。

  • uaddr2: uaddr2 参数是一个四字节的用户空间地址 在需要的场景使用(如后文的 FUTEX_SWAP )。

  • val3: 整数参数val3的解释取决于在操作上。

为什么 futex “快速”?

由于用户模式和内核模式之间的上下文切换很昂贵,futex 实现的同步结构会尽可能多地留在用户空间,这意味着它们只需要执行更少的系统调用。futex 的状态存储在用户空间变量中,futex 可以通过一些原子操作在没有竞争的情况下更改 futex 的状态,而无需系统调用的开销。

futex_wait() 和 futex_wake()

在看 futex_swap() 之前让我们先看看 内核中 与 futex 最重要的两个内核函数:

1static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time, u32 bitset);

简单来说 对于 futex_wait() 有用的参数就只有 uaddrvalabs_time,就像 futex_wait(uaddr,val,abs_time)。其含义是当这个用户空间地址 uaddr的值等于传入的参数 val 的时候睡眠,即 if (*uaddr == val) wait(). futex_wake() 可以将它唤醒,另外还可以通过指定超时时间来超时唤醒。

 1static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,2              ktime_t *abs_time, u32 bitset)3{4    struct hrtimer_sleeper timeout, *to;5    struct restart_block *restart;6    struct futex_hash_bucket *hb;7    struct futex_q q = futex_q_init;8    int ret;9
10    if (!bitset)
11        return -EINVAL;
12    q.bitset = bitset;
13  /* 设置定时器 */
14    to = futex_setup_timer(abs_time, &timeout, flags,
15                   current->timer_slack_ns);
16retry:
17    /*
18     * Prepare to wait on uaddr. On success, holds hb lock and increments
19     * q.key refs.
20     */
21    /* 获取哈希桶自旋锁 如果 *uaddr == val return -EWOULDBLOCK 否则返回 0 */
22    ret = futex_wait_setup(uaddr, val, flags, &q, &hb);
23    if (ret)
24        goto out;
25
26    /* queue_me and wait for wakeup, timeout, or a signal. */
27    /*将当前任务状态改为TASK_INTERRUPTIBLE,并将当前任务插入到futex等待队列,释放哈希桶自旋锁,然后重新调度*/
28    futex_wait_queue_me(hb, &q, to);
29
30    /* If we were woken (and unqueued), we succeeded, whatever. */
31    ret = 0;
32    /* unqueue_me() drops q.key ref */
33  /* 如果 unqueue_me 返回 0 表示已经被删除则是正常唤醒跳到 out 否则则是超时触发 */
34    if (!unqueue_me(&q))
35        goto out;
36    ret = -ETIMEDOUT;
37    if (to && !to->task)
38        goto out;
39
40    /*
41     * We expect signal_pending(current), but we might be the
42     * victim of a spurious wakeup as well.
43     */
44    if (!signal_pending(current))
45        goto retry;
46
47    ret = -ERESTARTSYS;
48    if (!abs_time)
49        goto out;
50
51    restart = &current->restart_block;
52    restart->futex.uaddr = uaddr;
53    restart->futex.val = val;
54    restart->futex.time = *abs_time;
55    restart->futex.bitset = bitset;
56    restart->futex.flags = flags | FLAGS_HAS_TIMEOUT;
57
58    ret = set_restart_fn(restart, futex_wait_restart);
59
60out:
61    if (to) {
62    /* 即将结束,取消定时任务 */
63        hrtimer_cancel(&to->timer);
64        destroy_hrtimer_on_stack(&to->timer);
65    }
66    return ret;
67}

futex 内部采用了哈希表的数据结构来保存那些需要睡眠的任务。通过用户空间地址 uaddr,flag,以及 futex 的读写状态可以计算出相同的 key 值,将需要睡眠的任务的 task_struct放到对应的哈希桶上的优先链表的节点中。

futex_wait() 流程:

  1. 寻找 futex 对应的 key,获取 key 对应的哈希桶。

  2. 获取哈希桶自旋锁,如果 *uaddr == val 返回错误给用户态。

  3. 否则将当前任务状态改为 TASK_INTERRUPTIBLE,并将当前任务插入到 futex 等待队列,释放哈希桶自旋锁,然后调度器重新调度。

  4. 从睡眠中苏醒,进行超时和唤醒两种情况的相应处理,返回用户态。

1static int futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset);

futex_wake() 的参数稍微简单一些,最重要的只有一个用户地址 uaddr,以及触发唤醒的任务数最大值 nr_wake

 1/*2 * Wake up waiters matching bitset queued on this futex (uaddr).3 */4static int5futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)6{7    struct futex_hash_bucket *hb;8    struct futex_q *this, *next;9    union futex_key key = FUTEX_KEY_INIT;
10    int ret;
11    DEFINE_WAKE_Q(wake_q);
12
13    if (!bitset)
14        return -EINVAL;
15
16  /* 寻找 futex 对应的 key */
17    ret = get_futex_key(uaddr, flags & FLAGS_SHARED, &key, FUTEX_READ);
18    if (unlikely(ret != 0))
19        return ret;
20
21  /* 获取 key 对应的哈希桶 */
22    hb = hash_futex(&key);
23
24    /* Make sure we really have tasks to wakeup */
25  /* 如果哈希桶桑没有等待者...那么谁也不需要被唤醒 */
26    if (!hb_waiters_pending(hb))
27        return ret;
28
29  /* 获取当前哈希桶的自旋锁 */
30    spin_lock(&hb->lock);
31
32  /* 遍历这个哈系桶上的优先链表 */
33    plist_for_each_entry_safe(this, next, &hb->chain, list) {
34    /* 如果 this 项的 key 与 futex 对应的 key 相同 说明该项在等待 futex */
35        if (match_futex (&this->key, &key)) {
36            if (this->pi_state || this->rt_waiter) {
37                ret = -EINVAL;
38                break;
39            }
40
41            /* Check if one of the bits is set in both bitsets */
42            if (!(this->bitset & bitset))
43                continue;
44
45      /* 将 this 添加到唤醒队列 wake_q 中 */
46            mark_wake_futex(&wake_q, this);
47      /* ret此时为0 递增至 nr_wake 最大唤醒任务数量则退出循环 */
48            if (++ret >= nr_wake)
49                break;
50        }
51    }
52
53    spin_unlock(&hb->lock);
54    wake_up_q(&wake_q);
55    return ret;
56}

futex_wake() 流程:

  1. 寻找 futex 对应的 key,获取 key 对应的哈希桶。

  2. 获取哈希桶的自旋锁,遍历这个哈系桶上的优先链表,如果当前任务的 keyfutex 对应的 key 相同,说明该任务在等待 futex,将当前任务添加到唤醒队列 wake_q 中,如果达到了 nr_wake 个,则退出循环。

  3. 释放哈希桶自旋锁,唤醒队列 wake_q 中每一个任务。

因此,通过 futex_wait()futex_wake(),我们可以实现任务的等待和唤醒,见 [5] man 手册中的小 demo。

FUTEX_SWAP 相关补丁

基于以上的了解,现在我们来看 Peter Oskolkov 向内核提交的 FUTEX_SWAP 补丁系列。首先看 [4] 中有关 FUTEX_SWAP 的相关测试补丁,其中关键的功能函数 futex_swap_op() 引起了我们的注意:

 1void futex_swap_op(int mode, futex_t *futex_this, futex_t *futex_that)2{3    int ret;45    switch (mode) {6    case SWAP_WAKE_WAIT:7        futex_set(futex_this, FUTEX_WAITING);8        futex_set(futex_that, FUTEX_WAKEUP);9        futex_wake(futex_that, 1, FUTEX_PRIVATE_FLAG);
10        futex_wait(futex_this, FUTEX_WAITING, NULL, FUTEX_PRIVATE_FLAG);
11        if (*futex_this != FUTEX_WAKEUP) {
12            fprintf(stderr, "unexpected futex_this value on wakeup\n");
13            exit(1);
14        }
15        break;
16
17    case SWAP_SWAP:
18        futex_set(futex_this, FUTEX_WAITING);
19        futex_set(futex_that, FUTEX_WAKEUP);
20        ret = futex_swap(futex_this, FUTEX_WAITING, NULL,
21                 futex_that, FUTEX_PRIVATE_FLAG);
22        if (ret < 0 && errno == ENOSYS) {
23            /* futex_swap not implemented */
24            perror("futex_swap");
25            exit(1);
26        }
27        if (*futex_this != FUTEX_WAKEUP) {
28            fprintf(stderr, "unexpected futex_this value on wakeup\n");
29            exit(1);
30        }
31        break;
32
33    default:
34        fprintf(stderr, "unknown mode in %s\n", __func__);
35        exit(1);
36    }
37}

其中比较了使用 FUTEX_WAIT, FUTEX_WAKE 实现线程切换以及使用 FUTEX_SWAP 实现线程切换的两种方式。

其中 通过调用参数含有 futex_thisfutex_thatfutex_swap()

1ret = futex_swap(futex_this, FUTEX_WAITING, NULL,
2                 futex_that, FUTEX_PRIVATE_FLAG);

代替了下面 futex_wake()futex_wait() 的两步操作,

1futex_wake(futex_that, 1, FUTEX_PRIVATE_FLAG);
2futex_wait(futex_this, FUTEX_WAITING, NULL, FUTEX_PRIVATE_FLAG);

实现了让当前线程睡眠,并切换到指定线程的作用。

 1$ ./futex_swap -i 100000234------- running SWAP_WAKE_WAIT -----------567completed 100000 swap and back iterations in 820683263 ns: 4103 ns per swap8PASS9
10
11------- running SWAP_SWAP -----------
12
13
14completed 100000 swap and back iterations in 124034476 ns: 620 ns per swap
15PASS

可见在 100k 级别的任务切换批处理上,使用新接口 futex_swap() 的上下文切换性能要比之前好很多。

 1/*2- * Wake up waiters matching bitset queued on this futex (uaddr).3+ * Prepare wake queue matching bitset queued on this futex (uaddr).4  */5 static int6-futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)7+prepare_wake_q(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset,8+           struct wake_q_head *wake_q)9 {
10     struct futex_hash_bucket *hb;
11     struct futex_q *this, *next;
12     union futex_key key = FUTEX_KEY_INIT;
13     int ret;
14-    DEFINE_WAKE_Q(wake_q);
15
16     if (!bitset)
17         return -EINVAL;
18@@ -1629,20 +1629,34 @@ futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
19             if (!(this->bitset & bitset))
20                 continue;
21
22-            mark_wake_futex(&wake_q, this);
23+            mark_wake_futex(wake_q, this);
24             if (++ret >= nr_wake)
25                 break;
26         }
27     }
28
29     spin_unlock(&hb->lock);
30-    wake_up_q(&wake_q);
31 out_put_key:
32     put_futex_key(&key);
33 out:
34     return ret;
35 }
36
37+/*
38+ * Wake up waiters matching bitset queued on this futex (uaddr).
39+ */
40+static int
41+futex_wake(u32 __user *uaddr, unsigned int flags, int nr_wake, u32 bitset)
42+{
43+    int ret;
44+    DEFINE_WAKE_Q(wake_q);
45+
46+    ret = prepare_wake_q(uaddr, flags, nr_wake, bitset, &wake_q);
47+    wake_up_q(&wake_q);
48+
49+    return ret;
50+}
51+
52 s

首先是通过从 futex_wake() 中抽出一个 prepare_wake_q() 获得 nr_wake 个等待在 futex 的任务并填入到传入的唤醒队列 wake_q 中。

 1+static int futex_swap(u32 __user *uaddr, unsigned int flags, u32 val,2+              ktime_t *abs_time, u32 __user *uaddr2)3+{4+    u32 bitset = FUTEX_BITSET_MATCH_ANY;5+    struct task_struct *next = NULL;6+    DEFINE_WAKE_Q(wake_q);7+    int ret;8+9+    ret = prepare_wake_q(uaddr2, flags, 1, bitset, &wake_q);
10+    if (!wake_q_empty(&wake_q)) {
11+        /* Pull the first wakee out of the queue to swap into. */
12+        next = container_of(wake_q.first, struct task_struct, wake_q);
13+        wake_q.first = wake_q.first->next;
14+        next->wake_q.next = NULL;
15+        /*
16+         * Note that wake_up_q does not touch wake_q.last, so we
17+         * do not bother with it here.
18+         */
19+        wake_up_q(&wake_q);
20+    }
21+    if (ret < 0)
22+        return ret;
23+
24+    return futex_wait(uaddr, flags, val, abs_time, bitset, next);
25+}

futex_swap() 流程:

  1. 获得等待在 uaddr2 上的预备唤醒队列,记录队列第一个任务为 next,对其他任务则执行唤醒。

  2. uaddr1 执行 futex_wait(),传入 next

我们看看 futex_wait() 上发生了哪些更改:

 1@@ -2600,9 +2614,12 @@ static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)2  * @hb:        the futex hash bucket, must be locked by the caller3  * @q:        the futex_q to queue up on4  * @timeout:    the prepared hrtimer_sleeper, or null for no timeout5+ * @next:    if present, wake next and hint to the scheduler that we'd6+ *        prefer to execute it locally.7  */8 static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,9-                struct hrtimer_sleeper *timeout)
10+                struct hrtimer_sleeper *timeout,
11+                struct task_struct *next)
12 {
13     /*
14      * The task state is guaranteed to be set before another task can
15@@ -2627,10 +2644,27 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
16          * flagged for rescheduling. Only call schedule if there
17          * is no timeout, or if it has yet to expire.
18          */
19-        if (!timeout || timeout->task)
20+        if (!timeout || timeout->task) {
21+            if (next) {
22+                /*
23+                 * wake_up_process() below will be replaced
24+                 * in the next patch with
25+                 * wake_up_process_prefer_current_cpu().
26+                 */
27+                wake_up_process(next);
28+                put_task_struct(next);
29+                next = NULL;
30+            }
31             freezable_schedule();
32+        }
33     }
34     __set_current_state(TASK_RUNNING);
35+
36+    if (next) {
37+        /* Maybe call wake_up_process_prefer_current_cpu()? */
38+        wake_up_process(next);
39+        put_task_struct(next);
40+    }
41 }
42
43@@ -2710,7 +2744,7 @@ static int futex_wait_setup(u32 __user *uaddr, u32 val, unsigned int flags,
44 }
45
46 static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
47-              ktime_t *abs_time, u32 bitset)
48+              ktime_t *abs_time, u32 bitset, struct task_struct *next)
49 {
50     struct hrtimer_sleeper timeout, *to;
51     struct restart_block *restart;
52@@ -2734,7 +2768,8 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
53         goto out;
54
55     /* queue_me and wait for wakeup, timeout, or a signal. */
56-    futex_wait_queue_me(hb, &q, to);
57+    futex_wait_queue_me(hb, &q, to, next);
58+    next = NULL;
59
60     /* If we were woken (and unqueued), we succeeded, whatever. */
61     ret = 0;
62@@ -2767,6 +2802,10 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
63     ret = -ERESTART_RESTARTBLOCK;
64
65 out:
66+    if (next) {
67+        wake_up_process(next);
68+        put_task_struct(next);
69+    }
70     if (to) {
71         hrtimer_cancel(&to->timer);
72         destroy_hrtimer_on_stack(&to->timer);
73@@ -2774,7 +2813,6 @@ static int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val,
74     return ret;
75 }

我们可以看到 futex_wait() 传入的 next 任务在两种情况下会被唤醒:

  1. 当前任务从对 uaddr 的等待中苏醒,接着在futex_wait结束的时候执行 wake_up_process() 切换到 next 任务。

  2. futex_wait_queue_me() 中等待超时(也代表着当前任务从对锁的等待中结束),执行 wake_up_process()切换到 next 任务。

通过对 futex 的魔改, 我们仿佛有了在用户态使用 switch_to() 指定任务切换的能力,这真是让人感到兴奋!这就是用户模式线程的用途:极低的切换开销,意味着我们操作系统可以支持的数以千计的线程可以提高到 10 倍以上甚至百万级别!

不过很可惜,该补丁似乎被 Linux 内核社区遗弃。如今补丁作者 Peter Oskolkov 正在试图向 Linux 内核引入另外一套 Google 的用户态任务调度框架 Fiber [7],来支持 Linux 世界中 c 系程序员对协作式任务切换的需求。

参考资料

[1] https://lore.kernel.org/lkml/414e292195d720c780fab2781c749df3be6566aa.camel@posk.io/

[2] https://lore.kernel.org/lkml/48058b850de10f949f96b4f311adb649b1fb3ff2.camel@posk.io/

[3] https://lore.kernel.org/lkml/d5cf58486a6a5e41581bed9183e8a831908ede0b.camel@posk.io/

[4] https://lore.kernel.org/lkml/a06a25f1380e0da48946b1bb958e1745e5fac964.camel@posk.io/

[5] https://man7.org/linux/man-pages/man2/futex.2.html

[6] https://lwn.net/Articles/360699/

[7] https://lore.kernel.org/lkml/20210520183614.1227046-1-posk@google.com/

本文来源于 陈莉君老师 “Linux内核之旅”公众号。


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

相关文章

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站理想与天阳

关于windows安装过程中“我们无法创建新的分区,也找不到现有的分区”问题解决办法

最近在安装电脑系统过程中碰到了这个问题&#xff0c;首先说明下我电脑bios已经设置了uefi引导启动&#xff0c;硬盘分区格式也是GPT格式&#xff0c;还是出现这个问题有点纳闷&#xff0c;后面折腾了好久才找到解决办法&#xff1a; 即在对磁盘进行分区的时候不要创建ESP分区…