您当前的位置:首页 > 电脑百科 > 软件技术 > 操作系统 > linux百科

在 Linux 内核中调试 FUSE 死锁

时间:2023-05-21 13:54:35  来源:今日头条  作者:闪念基因

 

.NETflix 的计算团队负责管理 Netflix 的所有 AWS 和容器化工作负载,包括自动缩放、容器部署、问题修复等。作为该团队的一员,我致力于修复用户报告的奇怪问题。

这个特殊问题涉及自定义内部FUSE 文件系统:ndrive。它已经溃烂了一段时间,但需要有人坐下来愤怒地看着它。/proc这篇博文描述了在将问题发布到内核邮件列表并了解内核等待代码的实际工作原理之前,我是如何深入了解发生了什么的!

症状:卡住 Docker Kill 和僵尸进程

我们有一个停滞的 docker API 调用:

goroutine 146 [选择,8817 分钟]:
net/http.(*persistConn).roundTrip(0xc000658fc0, 0xc0003fc080, 0x0, 0x0, 0x0) 
        /usr/local/go/src/net/http/transport.go:2610 +0x765 
net/http.(*Transport).roundTrip(0xc000420140, 0xc000966200, 0x30, 0x1366f20, 0x162) 
        /usr/local/go/src/net/http/transport.go:592 +0xacb 
net/http.(*Transport).往返(0xc000420140、0xc000966200、0xc000420140、0x0、0x0)
        /usr/local/go/src/net/http/roundtrip.go:17 +0x35 
net/http.send(0xc000966200、0x161eba0、0xc000420 140、0x0、0x0、0x0、 0xc00000e050, 0x3, 0x1, 0x0) 
        /usr/local/go/src/net/http/client.go:251 +0x454 
net/http.(*Client).send(0xc000438480, 0xc000966200, 0x0, 0x0, 0x0, 0xc00000e 050 , 0x0, 0x1, 0x10000168e)
        /usr/local/go/src/net/http/client.go:175 +0xff 
net/http.(*客户端)。做(0xc000438480, 0xc000966200, 0x0, 0x0, 0x0) 
        /usr/local/go/src/net/http/client.go:717 +0x45f 
net/http.(*Client).Do(...) 
        /usr/ local/go/src/net/http/client.go:585 
golang.org/x/net/context/ctxhttp.Do(0x163bd48, 0xc000044090, 0xc000438480, 0xc000966100, 0x0, 0x0, 0x0) 
        /go/pkg/mod/ golang.org/x/net@v0.0.0-20211209124913-491a49abca63/context/ctxhttp/ctxhttp.go:27 +0x10f 
Github.com/docker/docker/client.(*Client).doRequest(0xc0001a8200, 0x163bd48, 0xc00004409 0, 0xc000966100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...) 
        /go/pkg/mod/github.com/moby/moby@v0.0.0-20190408150954-50ebe4562dfc/client/request.go:132 +0xbe
github.com/docker/docker/client.(*Client).sendRequest(0xc0001a8200, 0x163bd48, 0xc000044090, 0x13d8643, 0x3, 0xc00079a720, 0x51, 0x0, 0x0, 0x0, ...) 
        /go/pkg/mod /github。 com/moby/moby@v0.0.0-20190408150954-50ebe4562dfc/client/request.go:122 +0x156 
github.com/docker/docker/client.(*Client).get(...) 
        /go/pkg/mod /github.com/moby/moby@v0.0.0-20190408150954-50ebe4562dfc/client/request.go:37 
github.com/docker/docker/client.(*Client).ContAInerInspect(0xc0001a8200, 0x163bd48, 0xc000044090, 0xc 0006a01c0, 0x40 , 0x0, 0x0, 0x0, 0x0, 0x0, ...) 
        /go/pkg/mod/github.com/moby/moby@v0.0.0-20190408150954-50ebe4562dfc/client/container_inspect.go:18 +0x128
github.com/Netflix/titus-executor/executor/runtime/docker.(*DockerRuntime).Kill(0xc000215180, 0x163bdb8, 0xc000938600, 0x1, 0x0, 0x0) 
        /var/lib/buildkite-agent/builds/ip-192- 168-1-90-1/netflix/titus-executor/executor/runtime/docker/docker.go:2835 +0x310 
github.com/Netflix/titus-executor/executor/runner.(*Runner).doShutdown(0xc000432dc0, 0x163bd10, 0xc000938390, 0x1, 0xc000b821e0, 0x1d, 0xc0005e4710) 
        /var/lib/buildkite-agent/builds/ip-192-168-1-90-1/netflix/titus-executor/executor/runner/runner.go:3 26 +0x4f4 
github.com/Netflix/titus-executor/executor/runner.(*Runner).startRunner(0xc000432dc0, 0x163bdb8, 0xc00071e0c0, 0xc0a502e28c08b488, 0x24572b8, 0x1df5980)
        /var/lib/buildkite-agent/builds/ip-192-168-1-90-1/netflix/titus-executor/executor/runner/runner.go:122 +0x391
由 github.com/Netflix/titus- 创建执行者/执行者/runner.StartTaskWithRuntime 
        /var/lib/buildkite-agent/builds/ip-192-168-1-90-1/netflix/titus-executor/executor/runner/runner.go:81 +0x411

在这里,我们的管理引擎对 Docker API 的 unix 套接字进行了 HTTP 调用,要求它终止一个容器。我们的容器配置为通过SIGKILL. 但这很奇怪。kill(SIGKILL)应该是比较致命的,那么容器是干什么的呢?

$ docker exec -it 6643cd073492 bash 
OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: process_linux.go:130: executing setns process caused: exit status 1: 未知

唔。似乎它还活着,但setns(2)失败了。为什么会这样?如果我们通过查看进程树ps awwfux,我们会看到:

_ containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/6643cd073492ba9166100ed30dbe389ff1caef0dc3d35 
| _ [码头工人初始化] 
| _ [ndrive] <已失效>

好的,所以容器的 init 进程仍然存在,但是它有一个僵尸子进程。容器的初始化进程可能在做什么?

# cat /proc/1528591/stack 
[<0>] do_wait+0x156/0x2f0 
[<0>] kernel_wait4+0x8d/0x140 
[<0>] zap_pid_ns_processes+0x104/0x180 
[<0>] do_exit+0xa41/0xb80 
[< 0>] do_group_exit+0x3a/0xa0 
[<0>] __x64_sys_exit_group+0x14/0x20 
[<0>] do_syscall_64+0x37/0xb0 
[<0>] entry_SYSCALL_64_after_hwframe+0x44/0xae

它正在退出,但似乎卡住了。不过,唯一的子进程是处于 Z(即“僵尸”)状态的 ndrive 进程。Zombies 是已成功退出的进程,正在等待wait()其父进程的相应系统调用对其进行收割。那么内核怎么会卡在等待僵尸呢?

# ls /proc/1544450/任务
1544450 1544574

啊哈,线程组里有两个线程。其中一个是僵尸,也许另一个不是:

# cat /proc/1544574/stack 
[<0>] request_wait_answer+0x12f/0x210 
[<0>] fuse_simple_request+0x109/0x2c0 
[<0>] fuse_flush+0x16f/0x1b0 
[<0>] filp_close+0x27/0x70 
[< 0>] put_files_struct+0x6b/0xc0 
[<0>] do_exit+0x360/0xb80 
[<0>] do_group_exit+0x3a/0xa0 
[<0>] get_signal+0x140/0x870 
[<0>] arch_do_signal_or_restart+0xae/0x7c0 
[< 0>] exit_to_user_mode_prepare+0x10f/0x1c0 
[<0>] syscall_exit_to_user_mode+0x26/0x40 
[<0>] do_syscall_64+0x46/0xb0 
[<0>] entry_SYSCALL_64_after_hwframe+0x44/0xae

事实上它不是僵尸。它试图尽可能地成为一个,但由于某种原因它在 FUSE 内部阻塞。为了找出原因,让我们看一些内核代码。如果我们查看zap_pid_ns_processes(),它会:

/* 
* 在我们忽略 SIGCHLD 之前获取我们拥有的 EXIT_ZOMBIE 孩子。
* kernel_wait4() 也将阻塞,直到我们从
* parent 命名空间追踪到的孩子被分离并变成 EXIT_DEAD。
*/
做{ 
        clear_thread_flag(TIF_SIGPENDING); 
        rc = kernel_wait4( -1 , NULL , __WALL, NULL ); 
} while (rc != -ECHILD);

这是我们卡住的地方,但在此之前,它已经完成了:

/* 不允许更多进程进入 pid 命名空间 */
 disable_pid_allocation(pid_ns);

这就是为什么 docker 不能setns()——命名空间是一个僵尸。好的,所以我们不能setns(2),但为什么我们被困在里面kernel_wait4()?要了解原因,让我们看看另一个线程在 FUSE 中做了什么request_wait_answer():

/* 
* 要么请求已经在用户空间中,要么是强制的。
* 等等。
*/
 wait_event(req->waitq, test_bit(FR_FINISHED, &req->flags));

好的,所以我们正在等待一个事件(在这种情况下,用户空间已经回复了 FUSE 刷新请求)。但是zap_pid_ns_processes()发了一个SIGKILL!SIGKILL对一个进程应该是非常致命的。如果我们看一下这个过程,我们确实可以看到有一个 pending SIGKILL:

# grep Pnd /proc/1544574/status 
SigPnd: 0000000000000000 
ShdPnd: 0000000000000100

这样查看进程状态,可以看到0x100(即第9位被置位)ShdPnd,是对应的信号号SIGKILL。挂起信号是由内核生成但尚未传送到用户空间的信号。信号仅在特定时间传递,例如进入或离开系统调用时,或等待事件时。如果内核当前正在代表任务做某事,则信号可能处于挂起状态。信号也可以被任务阻塞,因此它们永远不会被传递。被阻止的信号也将出现在它们各自的待处理集中。然而,man 7 signal他说:“信号SIGKILL不能SIGSTOP被捕获、阻止或忽略。” 但是内核在这里告诉我们,我们有一个未决的SIGKILL,也就是即使在任务等待时它也被忽略了!

红鲱鱼:信号是如何工作的?

嗯,这很奇怪。等待代码(即include/linux/wait.h)在内核中无处不在:信号量、等待队列、完成等。它当然知道寻找SIGKILLs。那么wait_event()实际上是做什么的呢?通过宏扩展和包装器挖掘,它的核心是:

# define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)  
({  
        __label__ __out;  
        struct wait_queue_entry __wq_entry;  
        long __ret = ret;        /* 显式阴影 */                             
                                                                                 
        init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); 
        为 (;;) { 
                long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state); 
                                                                                 
                if (条件)  
                        break;  
                                                                                 
                if (___wait_is_interruptible(state) && __int) {  
                        __ret = __int; 
                        转到 __out;  
                } 
                                                                                
                命令; 
        }  
        finish_wait(&wq_head, &__wq_entry);  
__out: __ret;  
})

所以它永远循环,做prepare_to_wait_event(),检查条件,然后检查我们是否需要中断。然后它确实如此cmd,在这种情况下是schedule(),即“暂时做其他事情”。prepare_to_wait_event()好像:

long  prepare_to_wait_event ( struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)
 {
        无符号 长标志;
        长ret = 0 ; 

        spin_lock_irqsave(&wq_head->lock, flags); 
        if (signal_pending_state(state, current)) { 
                /* 
                 * 如果它被唤醒选择,独占的等待者不能失败,
                 * 它应该“消耗”我们正在等待的条件。
                 * 
                 * 调用者将重新检查条件并返回成功如果
                 * 我们已经被唤醒,我们不能错过事件,因为
                 * 唤醒锁定/解锁相同的 wq_head->lock。
                 * 
                 * 但我们需要确保 set-condition + wakeup after that 
                 * 看不到我们,如果
                 我们失败,它应该唤醒另一个独占的服务员。
                 */
                 list_del_init(&wq_entry->entry); 
                ret = -ERESTARTSYS; 
        } else { 
                if (list_empty(&wq_entry->entry)) { 
                        if (wq_entry->flags & WQ_FLAG_EXCLUSIVE) 
                                __add_wait_queue_entry_tail(wq_head, wq_entry); 
                        别的
                                __add_wait_queue(wq_head, wq_entry); 
                } 
                set_current_state(state); 
        } 
        spin_unlock_irqrestore(&wq_head->lock, flags); 

        返还;
} 
EXPORT_SYMBOL(prepare_to_wait_event);

看起来我们可以使用非零退出代码打破这种情况的唯一方法是 ifsignal_pending_state()为真。因为我们的调用站点是 just wait_event(),所以我们知道这里的状态是TASK_UNINTERRUPTIBLE;的定义signal_pending_state()看起来像:

static  inline  int  signal_pending_state ( unsigned  int state, struct task_struct *p)
 { 
        if (!(state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL)))
                返回 0 ; 
        如果(!signal_pending(p))
                返回 0;

        返回(状态和 TASK_INTERRUPTIBLE)|| __fatal_signal_pending(p); 
}

我们的任务是不可中断的,所以第一个 if 失败了。不过,我们的任务应该有一个待处理的信号,对吗?

static  inline  int  signal_pending ( struct task_struct *p)
 { 
        /* 
         * TIF_NOTIFY_SIGNAL 并不是真正的信号,但它需要相同的
         * 行为来确保我们跳出等待循环
         * 以便可以处理通知信号回调。
         */ 
        if (unlikely(test_tsk_thread_flag(p, TIF_NOTIFY_SIGNAL))) 
                return  1 ; 
        返回task_sigpending(p); 
}

正如评论指出的那样,TIF_NOTIFY_SIGNAL尽管它的名字在这里并不相关,但让我们看看task_sigpending():

static  inline  int  task_sigpending ( struct task_struct *p)
 { 
        return unlikely(test_tsk_thread_flag(p,TIF_SIGPENDING)); 
}

唔。看起来我们应该设置那个标志,对吧?为了弄清楚这一点,让我们看看信号传递是如何工作的。当我们关闭 中的 pid 命名空间时zap_pid_ns_processes(),它会:

group_send_sig_info(SIGKILL,SEND_SIG_PRIV,任务,PIDTYPE_MAX);

最终到达__send_signal_locked(),其中有:

挂起=(类型!= PIDTYPE_PID)?&t->signal->shared_pending : &t->pending; 
... 
sigaddset(&pending->signal, sig); 
... 
complete_signal(sig, t, type);

使用PIDTYPE_MAX这里作为类型有点奇怪,但它大致表示“这是发送此信号的非常特权的内核内容,你绝对应该传递它”。不过,这里有一些意想不到的后果,因为__send_signal_locked()最终将 发送SIGKILL到共享集,而不是单个任务集。如果我们查看代码__fatal_signal_pending(),我们会看到:

static  inline  int __fatal_signal_pending( struct task_struct *p) 
{ 
        return unlikely(sigismember(&p->pending.signal, SIGKILL)); 
}

但事实证明这有点转移注意力(尽管我 花 了 一段 时间才明白这一点)。

信号实际上是如何传递给进程的

要了解这里到底发生了什么,我们需要查看complete_signal(),因为它无条件地将 a 添加SIGKILL到任务的待处理集:

sigaddset(&t->pending.signal, SIGKILL);

但为什么它不起作用?在函数的顶部,我们有:

/* 
* 现在找到一个我们可以唤醒的线程,从队列中取出信号。
* 
* 如果主线程需要信号,它会首先破解。
* 对普通熊而言,这可能是最不令人惊讶的。
*/ 
if (wants_signal(sig, p)) 
        t = p; 
else  if ((type == PIDTYPE_PID) || thread_group_empty(p)) 
        /* 
         * 只有一个线程,不需要被唤醒。
         * 它会在再次运行之前使未阻塞的信号出队。
         */
        返回;

但正如Eric Biederman 所描述的SIGKILL,基本上每个线程都可以随时处理一个。这是wants_signal():

static  inline  bool  wants_signal ( int sig, struct task_struct *p)
 { 
        if (sigismember(&p->blocked, sig))
                返回 false ; 

        如果(p->flags & PF_EXITING)
                返回 false;

        如果(sig == SIGKILL)
                返回 true;

        如果(task_is_stopped_or_traced(p))
                返回 false;

        返回task_curr(p) || !task_sigpending(p); 
}

所以……如果一个线程已经退出(即它有PF_EXITING),它不需要信号。考虑以下事件序列:

1. 任务打开一个 FUSE 文件,但没有关闭它,然后退出。在退出期间,内核尽职地调用do_exit(),它执行以下操作:

退出信号(tsk);/* 设置 PF_EXITING */

2.do_exit()继续执行exit_files(tsk);,这会刷新所有仍打开的文件,从而产生上面的堆栈跟踪。

3. pid 命名空间退出,进入zap_pid_ns_processes(),向所有人发送一个SIGKILL(它预计是致命的),然后等待所有人退出。

4. 这会杀死 pid ns 中的 FUSE 守护进程,因此它永远无法响应。

5.complete_signal()对于已经退出的 FUSE 任务忽略信号,因为它有PF_EXITING.

6.死锁。如果不手动中止 FUSE 连接,事情将永远挂起。

解决方案:不要等待!

在这种情况下等待刷新真的没有意义:任务快结束了,所以没有人可以告诉flush()to 的返回码。事实证明,这个错误可能发生在几个文件系统上(任何调用内核等待代码的东西flush(),即基本上任何与本地内核之外的东西对话的东西)。

同时需要为单个文件系统打补丁,例如 FUSE 的修复程序在这里,它于 4 月 23 日在 Linux 6.3 中发布。

虽然这篇博文解决了 FUSE 死锁问题,但 nfs 代码和其他地方肯定存在问题,我们尚未在生产中遇到这些问题,但几乎肯定会遇到。您还可以将其视为其他文件系统错误的症状。如果您有一个不会退出的 pid 名称空间,则需要注意一些事项。

 

出处
:https://netflixtechblog.com/debugging-a-fuse-deadlock-in-the-linux-kernel-c75cd7989b6d



Tags: Linux   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08  Search: Linux  点击:(6)  评论:(0)  加入收藏
2024 年 Linux 和开源的六大趋势预测
让我们尝试预测未来吧!新的一年快乐,朋友们 ✨2024 年的钟声已经敲过,我们有必要去预见一下将塑造本年度的各种潮流。我们不能预见未来,所以无法精确预知将会发生什么,但根据目前...【详细内容】
2024-01-09  Search: Linux  点击:(92)  评论:(0)  加入收藏
对 Bash 感到厌倦?教你如何在 Linux 中更改默认 Shell
Bash 并不是唯一可供选择的 Shell。还存在数量众多的 Shell,它们都有一些独特的特性,例如 Zsh、Fish、Ksh 和 Xonsh。在你的系统中,你可以同时安装多个 Shell。要想将另一个 Sh...【详细内容】
2023-12-14  Search: Linux  点击:(199)  评论:(0)  加入收藏
适用于 Linux 的 LibreOffice 替代品
LibreOffice 是一个出色的开源文档套件。它预装在许多 Linux 发行版上,应该足以满足大多数用户的需求。然而,有些人可能不喜欢它的用户界面和功能集。某些用户可能想尝试其他...【详细内容】
2023-11-30  Search: Linux  点击:(122)  评论:(0)  加入收藏
十个最佳免费 Linux 防火墙工具
概述防火墙是保护网络边界的关键。防火墙会阻止敏感端口并过滤传入和传出流量,以阻止恶意连接并确保不会发生未经请求的数据交换。在自由开源软件的世界里,有很多防火墙解决方...【详细内容】
2023-11-28  Search: Linux  点击:(173)  评论:(0)  加入收藏
如何使用 Linux Xargs 命令,看这篇就够了
一、xargs 命令是什么?该xargs命令构建并执行通过标准输入提供的命令。它接受输入并将其转换为另一个命令的命令参数。此功能在文件管理中特别有用,可与rm、cp、mkdir和其他类...【详细内容】
2023-11-23  Search: Linux  点击:(176)  评论:(0)  加入收藏
揭秘 Linux 调度策略与 CFS 调度算法:解锁内核的奥秘
引言在当今计算机领域,Linux操作系统扮演着至关重要的角色,而其中的调度策略和内核结构体更是它多任务处理的核心。本文将引领你深入探索Linux中的调度策略,理解不同策略如何影...【详细内容】
2023-11-23  Search: Linux  点击:(216)  评论:(0)  加入收藏
使用 dialog 和 jq 在 Linux 上编写高效终端 TUI
为何选择文字用户界面(TUI)?许多人每日都在使用终端,因此,文字用户界面Text User Interface(TUI)逐渐显示出其价值。它能减少用户输入命令时的误差,让终端操作更高效,提高生产力。以...【详细内容】
2023-11-19  Search: Linux  点击:(117)  评论:(0)  加入收藏
这些 Linux 基础命令你总得掌握吧
很多深度学习/机器学习/数据分析等领域(或者说大多数在Python环境下进行操作的领域)的初学者入门时是在Windows上进行学习,也得益于如Anaconda等工具把环境管理做的如此友善。...【详细内容】
2023-11-10  Search: Linux  点击:(169)  评论:(0)  加入收藏
如何在 Linux 下使用 WebP 图像
WebP 图像格式的定义2010 年 9 月,谷歌提出了 WebP 图像格式,其愿景是完全替代 JPEG、PNG 和 GIF 文件格式。就如你所见,WebP 是一个全能型的格式,继承了先前压缩算法的所有特性...【详细内容】
2023-09-25  Search: Linux  点击:(315)  评论:(0)  加入收藏
▌简易百科推荐
微软 Win11 Linux 子系统(WSL)发布 2.2.2 版本
IT之家 4 月 8 日消息,微软近日更新 Windows Subsystem for Linux(WSL),最新 2.2.2 版本中带来了诸多改进,重点更新了 nft 规则,可以让 IPv6 流量通过 Linux 容器。图源: dev.to,AI...【详细内容】
2024-04-08    IT之家  Tags:Linux   点击:(6)  评论:(0)  加入收藏
从原理到实践:深入探索Linux安全机制
Linux 是一种开源的类Unix操作系统内核,由Linus Torvalds在1991年首次发布,其后又衍生出许多不同的发行版(如Ubuntu、Debian、CentOS等)。前言本文将从用户和权限管理、文件系统...【详细内容】
2024-03-27  凡夫编程  微信公众号  Tags:Linux安全   点击:(16)  评论:(0)  加入收藏
在Linux系统中,如何处理内存管理和优化的问题?
本文对 Linux 内存管理和优化的一些高级技巧的详细介绍,通过高级的内存管理技巧,可以帮助系统管理员和开发人员更好地优化 Linux 系统的内存使用情况,提高系统性能和稳定性。在...【详细内容】
2024-03-26  编程技术汇  微信公众号  Tags:Linux   点击:(10)  评论:(0)  加入收藏
Linux 6.9-rc1 内核发布:AMD P-State 首选核心、BH 工作队列
IT之家 3 月 25 日消息,Linus Torvalds 宣布,Linux 6.9 内核的首个 RC(候选发布)版 Linux 6.9-rc1 发布。▲ Linux 6.9-rc1Linus 表示,Linux 内核 6.9 看起来是一个“相当正常”...【详细内容】
2024-03-25    IT之家  Tags:Linux   点击:(11)  评论:(0)  加入收藏
轻松实现Centos系统的软件包安装管理:yum指令实战详解
yum 是一种用于在 CentOS、Red Hat Enterprise Linux (RHEL) 等基于 RPM 的 Linux 发行版上安装、更新和管理软件包的命令行工具。它可以自动解决软件包依赖关系,自动下载并...【详细内容】
2024-02-27  凡夫贬夫  微信公众号  Tags:Centos   点击:(54)  评论:(0)  加入收藏
Win + Ubuntu 缝合怪:第三方开发者推出“Wubuntu”Linux 发行版
IT之家 2 月 26 日消息,一位第三方开发者推出了一款名为“Wubuntu”的缝合怪 Linux 发行版,系统本身基于 Ubuntu,但界面为微软 Windows 11 风格,甚至存在微软 Windows 徽标。据...【详细内容】
2024-02-27    IT之家  Tags:Ubuntu   点击:(49)  评论:(0)  加入收藏
Linux中磁盘和文件系统工作原理解析
在Linux系统中,一切皆文件的概念意味着所有的资源,包括普通文件、目录以及设备文件等,都以文件的形式存在。这种统一的文件系统管理方式使得Linux系统具有高度的灵活性和可扩展...【详细内容】
2024-02-20  王建立    Tags:Linux   点击:(51)  评论:(0)  加入收藏
Linux子系统概览
inux操作系统是一个模块化的系统,由多个子系统组成。这些子系统协同工作,使Linux能够执行各种任务。了解Linux的子系统有助于更好地理解整个操作系统的运作机制。以下是Linux...【详细内容】
2024-02-01    简易百科  Tags:Linux   点击:(76)  评论:(0)  加入收藏
Linux内核:系统之魂与交互之源
内核,作为任何基于Linux的操作系统的心脏,扮演着至关重要的角色。它不仅是计算机系统软件与硬件之间的桥梁,更是确保系统稳定、高效运行的关键。内核提供了一系列核心功能,为上...【详细内容】
2024-02-01  松鼠宝贝    Tags:Linux内核   点击:(68)  评论:(0)  加入收藏
如何确保Linux进程稳定与持久
在Linux系统中,进程的稳定性与持久性对于维持系统的持续运行至关重要。然而,由于各种原因,进程可能会面临崩溃或系统重启的情况。为了确保关键进程能够持续运行,我们必须采取一...【详细内容】
2024-01-19  松鼠宝贝    Tags:Linux进程   点击:(85)  评论:(0)  加入收藏
站内最新
站内热门
站内头条