検索
ホームページ運用・保守Linuxの運用と保守Linux の copy_{to, from}_user() についての深い理解 (コード付き)

Linux の copy_{to, from}_user() についての深い理解 (コード付き)

Linux の copy_{to, from}_user() についての深い理解 (コード付き)

はじめに

copy_{to,from}_user() インターフェースの使用法をよく理解しておく必要があります。 Linux の基本書籍ではその機能が紹介されています。結局のところ、それはカーネル空間とユーザー空間の間の架け橋です。すべてのデータ対話では、このようなインターフェイスを使用する必要があります。したがって、インターフェースの役割を知らなくてはいけない理由はありません。しかしながら、以下のような疑問も感じました。

  1. copy_{to,from}_user() が必要なのはなぜですか?これは舞台裏で何をするのでしょうか?

  2. copy_{to,from}_user() と memcpy() の違いは何ですか? memcpy() を直接使用できますか?

  3. memcpy() が copy_{to,from}_user() を置き換えた場合、問題はありますか?

当時混乱していた自分に、ふと気づきました。私がこれまでに尋ねた質問はすべて、以前に考えたことのあるものです。何度も考えましたが、そのたびに違う考えが浮かびました。もちろん、最初からよく理解していなかったからです。さて、私は再びこの重いテーマに戻り、この過去の問題について考え続けます。

100 の思想派が争う

上記の問題に対応するには、もちろん、Baidu が最初の対応となるべきです。 Baidu にはこの問題に関する多くのブログもあり、この問題が多くの Linux 愛好家を混乱させているに違いないことがわかります。私のレビュー結果については、 意見は主に次の 2 つのタイプに分類されます:

1. copy_{to,from}_user() は、memcpy() テストよりも受信アドレスの正当性検証が機能します。

たとえば、ユーザー空間のアドレス範囲に属するかどうかなどです。理論的には、カーネル空間はユーザー空間から渡されたポインタを直接使用できます。データをコピーしたい場合でも、memcpy() を直接使用することもできます。実際、MMU のないアーキテクチャでは、copy_{to,from}_user()最終的な実装では mencpy() を使用します。

しかし、MMU を備えたほとんどのプラットフォームでは状況が変わりました。ユーザー空間から渡されたポインターは仮想アドレス空間内にあり、それが指す仮想アドレス空間は実際には実際の物理アドレス空間にマッピングされていない可能性があります。ページ。でも、だから何?ページ フォールトによって発生した例外はカーネルによって透過的に修復され (フォールトが発生したページのアドレス空間に新しい物理ページを送信します)、フォールトが発生したページにアクセスする命令は何事もなかったかのように実行を継続します。ただし、これはユーザー空間でのページ欠落例外の動作にすぎません。カーネル空間では、このページ欠落例外は明示的に修復する必要があります。これは、カーネルが提供するページ欠落例外処理関数の設計パターンによって決まります。

その背後にある考え方は次のとおりです。カーネル モードでは、物理ページをまだコミットしていないユーザー空間アドレスにプログラムがアクセスしようとした場合、カーネルはこれを警戒する必要があり、ユーザーのようにそれを認識することはできません。空間。

2. ユーザー モードで渡されるポインターの正確性を確認すれば、copy_{to,from}_user() の代わりに memcpy() 関数を完全に使用できます。いくつかの実験とテストの結果、memcpy() を使用して実行するプログラムには問題がないことがわかりました。したがって、ユーザー モード ポインターの安全性を確保しながら、この 2 つを置き換えることができます。

さまざまなブログから、意見は主に最初の点に集中しています。最初の点は誰もが広く認識しているようです。しかし、実践に注意を払う人々は、結局のところ、実践が真の知識をもたらすという 2 番目の観点に到達しました。真実は少数の人々の手に握られているのでしょうか?それとも大衆の目は鋭いのでしょうか?もちろん、私は上記の意見を否定するものではありません。どの見解が正しいかは保証できません。かつては完璧だった理論でも、時間が経ったり、特定の状況が変化したりすると、もはや真実ではなくなる可能性があると私は信じているからです。たとえば、ニュートンの古典力学理論(少し遠いようですが)。私に人間の言葉を話してもらいたいなら、これは次のとおりです。時間の経過とともに、Linux コードは常に変化しています。おそらく、上記の見方はかつては正しかったのでしょう。もちろん、今もそうなのかもしれません。以下の分析は私の見解です。同様に、誰もが懐疑的であり続ける必要があります。ここでいくつかのアイデアを紹介します。

賛成します

まず、memcpy() と copy_{to,from}_user() の関数定義を見てみましょう。パラメータにほとんど違いはなく、すべてに宛先アドレス、送信元アドレス、コピーされるバイトのサイズが含まれます。

static __always_inline unsigned long __must_check	
copy_to_user(void __user *to, const void *from, unsigned long n);	
static __always_inline unsigned long __must_check	
copy_from_user(void *to, const void __user *from, unsigned long n);	
void *memcpy(void *dest, const void *src, size_t len);

ただし、確かにわかっていることが 1 つあります。つまり、memcpy() は受信アドレスの有効性をチェックしません。そして、copy_{to,from}_user() は、受信アドレスに対して次のような正当性チェックを実行します (簡単に言えば、検証の詳細についてはコードを参照してください)。

  1. ユーザー空間からカーネル空間にデータをコピーする場合、ユーザー空間アドレス to と to にコピーされたバイト長 n を加えたものがユーザー空間アドレス空間に存在する必要があります。

  2. データをカーネル空間からユーザー空間にコピーする場合は、当然ながら、アドレスの正当性もチェックする必要があります。たとえば、範囲外アクセスであるかどうか、コードセグメント内のデータであるかどうかなどです。つまり、すべての違法な操作は直ちに停止される必要があります。

经过简单的对比之后,我们再看看其他的差异以及一起探讨下上面提出的2个观点。我们先从第2个观点说起。涉及实践,我还是有点相信实践出真知。从我测试的结果来说,实现结果分成两种情况。

第一种情况的结果是:使用memcpy()测试,没有出现问题,代码正常运行。测试代码如下(仅仅展示proc文件系统下file_operations对应的read接口函数):

static ssize_t test_read(struct file *file, char __user *buf,	
                         size_t len, loff_t *offset)	
{	
        memcpy(buf, "test\n", 5);    /* copy_to_user(buf, "test\n", 5) */	
        return 5;	
}

我们使用cat命令读取文件内容,cat会通过系统调用read调用test_read,并且传递的buf大小是4k。

测试很顺利,结果很喜人。成功地读到了“test”字符串。看起来,第2点观点是没毛病的。但是,我们还需要继续验证和探究下去。因为第1个观点提到,“在内核空间这种缺页异常必须被显式地修复”。

因此我们还需要验证的情况是:如果buf在用户空间已经分配虚拟地址空间,但是并没有建立和物理内存的具体映射关系,这种情况下会出现内核态page fault。我们首先需要创建这种条件,找到符合的buf,然后测试。这里我当然没测啦。因为有测试结论(主要是因为我懒,构造这个条件我觉得比较麻烦)。

这个测试是我的一个朋友,人称宋老师的“阿助教”阿克曼大牛。他曾经做个这个实验,并且得到的结论是:即使是没有建立和物理内存的具体映射关系的buf,代码也可以正常运行。在内核态发生page fault,并被其修复(分配具体物理内存,填充页表,建立映射关系)。同时,我从代码的角度分析,结论也是如此。

经过上面的分析,看起来好像是memcpy()也可以正常使用,鉴于安全地考虑建议使用copy_{to,from}_user()等接口。

第二种情况的结果是:以上的测试代码并没有正常运行,并且会触发kernel oops。当然本次测试和上次测试的kernel配置选项是不一样的。这个配置项是 <span class="pln">CONFIG_ARM64_SW_TTBR0_PAN</span>或者 <span class="pln">CONFIG_ARM64_PAN</span>(针对ARM64平台)。两个配置选项的功能都是阻止内核态直接访问用户地址空间。只不过CONFIG_ARM64_SW_TTBR0_PAN是软件仿真实现这种功能,而CONFIG_ARM64_PAN是硬件实现功能(ARMv8.1扩展功能)。我们以CONFIG_ARM64_SW_TTBR0_PAN作为分析对象(软件仿真才有代码提供分析)。BTW,如果硬件不支持,即使配置CONFIG_ARM64_PAN也没用,只能使用软件仿真的方法。如果需要访问用户空间地址需要通过类似copy_{to,from}_user()的接口,否则会导致kernel oops。

在打开CONFIG_ARM64_SW_TTBR0_PAN的选项后,测试以上代码就会导致kernel oops。原因就是内核态直接访问了用户空间地址。因此,在这种情况我们就不可以使用memcpy()。我们别无选择,只能使用copy_{to,from}_user()。

为什么我们需要PAN(Privileged Access Never)功能呢?原因可能是用户空间和内核空间数据交互上容易引入安全问题,所以我们就不让内核空间轻易访问用户空间,如果非要这么做,就必须通过特定的接口关闭PAN。另一方面,PAN功能可以更加规范化内核态和用户态数据交互的接口使用。在使能PAN功能的情况下,可以迫使内核或者驱动开发者使用copy_{to,from}_user()等安全接口,提升系统的安全性。类似memcpy()非规范操作,kernel就oops给你看。

由于编程的不规范而引入安全漏洞。例如:Linux内核漏洞CVE-2017-5123可以提升权限。该漏洞的引入原因就是是缺少access_ok()检查用户传递地址的合法性。因此,为了避免自己编写的代码引入安全问题,针对内核空间和用户空间数据交互上,我们要格外当心。

刨根问底

既然提到了CONFIG_ARM64_SW_TTBR0_PAN的配置选项。当然我也希望了解其背后设计的原理。由于ARM64的硬件特殊设计,我们使用两个页表基地址寄存器ttbr0_el1和ttbr1_el1。处理器根据64 bit地址的高16 bit判断访问的地址属于用户空间还是内核空间。如果是用户空间地址则使用ttbr0_el1,反之使用ttbr1_el1。因此,ARM64进程切换的时候,只需要改变ttbr0_el1的值即可。ttbr1_el1可以选择不需要改变,因为所有的进程共享相同的内核空间地址。

当进程切换到内核态(中断,异常,系统调用等)后,如何才能避免内核态访问用户态地址空间呢?其实不难想出,改变ttbr0_el1的值即可,指向一段非法的映射即可。因此,我们为此准备了一份特殊的页表,该页表大小4k内存,其值全是0。当进程切换到内核态后,修改ttbr0_el1的值为该页表的地址即可保证访问用户空间地址是非法访问。因为页表的值是非法的。这个特殊的页表内存通过链接脚本分配。

#define RESERVED_TTBR0_SIZE    (PAGE_SIZE)	
SECTIONS	
{	
        reserved_ttbr0 = .;	
        . += RESERVED_TTBR0_SIZE;	
        swapper_pg_dir = .;	
        . += SWAPPER_DIR_SIZE;	
        swapper_pg_end = .;	
}

这个特殊的页表和内核页表在一起。和swapper_pg_dir仅仅差4k大小。reserved_ttbr0地址开始的4k内存空间的内容会被清零。

当我们进入内核态后会通过__uaccess_ttbr0_disable切换ttbr0_el1以关闭用户空间地址访问,在需要访问的时候通过_uaccess_ttbr0_enable打开用户空间地址访问。这两个宏定义也不复杂,就以_uaccess_ttbr0_disable为例说明原理。其定义如下:

.macro    __uaccess_ttbr0_disable, tmp1	
    mrs    \tmp1, ttbr1_el1                        // swapper_pg_dir (1)	
    bic    \tmp1, \tmp1, #TTBR_ASID_MASK	
    sub    \tmp1, \tmp1, #RESERVED_TTBR0_SIZE      // reserved_ttbr0 just before	
                                                // swapper_pg_dir (2)	
    msr    ttbr0_el1, \tmp1                        // set reserved TTBR0_EL1 (3)	
    isb	
    add    \tmp1, \tmp1, #RESERVED_TTBR0_SIZE	
    msr    ttbr1_el1, \tmp1                       // set reserved ASID	
    isb	
.endm
  1. ttbr1_el1存储的是内核页表基地址,因此其值就是swapper_pg_dir。

  2. swapper_pg_dir减去RESERVED_TTBR0_SIZE就是上面描述的特殊页表。

  3. 将ttbr0_el1修改指向这个特殊的页表基地址,当然可以保证后续访问用户地址都是非法的。

__uaccess_ttbr0_disable对应的C语言实现可以参考这里。

如何允许内核态访问用户空间地址呢?也很简单,就是__uaccess_ttbr0_disable的反操作,给ttbr0_el1赋予合法的页表基地址。这里就不必重复了。

我们现在需要知道的事实就是,在配置CONFIG_ARM64_SW_TTBR0_PAN的情况下,copy_{to,from}_user()接口会在copy之前允许内核态访问用户空间,并在copy结束之后关闭内核态访问用户空间的能力。因此,使用copy_{to,from}_user()才是正统做法。主要体现在安全性检查及安全访问处理。这里是其比memcpy()多的第一个特性,后面还会介绍另一个重要特性。

现在我们可以解答上一节中遗留的问题。怎样才能继续使用memcpy()?现在就很简单了,在memcpy()调用之前通过uaccess_enable_not_uao()允许内核态访问用户空间地址,调用memcpy(),最后通过uaccess_disable_not_uao()关闭内核态访问用户空间的能力。

未雨绸缪

以上的测试用例都是建立在用户空间传递合法地址的基础上测试的,何为合法的用户空间地址?

用户空间通过系统调用申请的虚拟地址空间包含的地址范围,即是合法的地址(不论是否分配物理页面建立映射关系)。既然要写一个接口程序,当然也要考虑程序的健壮性,我们不能假设所有的用户传递的参数都是合法的。我们应该预判非法传参情况的发生,并提前做好准备,这就是未雨绸缪。

我们首先使用memcpy()的测试用例,随机传递一个非法的地址。经过测试发现:会触发kernel oops。继续使用copy_{to,from}_user()替代memcpy()测试。

测试发现:read()仅仅是返回错误,但不会触发kernel oops。这才是我们想要的结果。毕竟,一个应用程序不应该触发kernel oops。这种机制的实现原理是什么呢?

我们以copy_to_user()为例分析。函数调用流程是:

copy_to_user()->_copy_to_user()->raw_copy_to_user()->__arch_copy_to_user()

_arch_copy_to_user()在ARM64平台是汇编代码实现,这部分代码很关键。

end    .req    x5	
ENTRY(__arch_copy_to_user)	
        uaccess_enable_not_uao x3, x4, x5	
        add    end, x0, x2	
#include "copy_template.S"	
        uaccess_disable_not_uao x3, x4	
        mov    x0, #0	
        ret	
ENDPROC(__arch_copy_to_user)	
        .section .fixup,"ax"	
        .align    2	
9998:    sub x0, end, dst            // bytes not copied	
        ret	
        .previous
  1. uaccess_enable_not_uao和uaccess_disable_not_uao是上面说到的内核态访问用户空间的开关。

  2. copy_template.S文件是汇编实现的memcpy()的功能,稍后看看memcpy()的实现代码就清楚了。

  3. <span class="pun">.section.fixup,“ax”</span>定义一个section,名为“.fixup”,权限是ax(‘a’可重定位的段,‘x’可执行段)。 <span class="lit">9998</span>标号处的指令就是“未雨绸缪”的善后处理工作。还记得copy_{to,from}_user()返回值的意义吗?返回0代表copy成功,否则返回剩余没有copy的字节数。这行代码就是计算剩余没有copy的字节数。当我们访问非法的用户空间地址的时候,就一定会触发page fault。这种情况下,内核态发生的page fault并返回的时候并没有修复异常,所以肯定不能返回发生异常的地址继续运行。所以,系统可以有2个选择:第1个选择是kernel oops,并给当前进程发送SIGSEGV信号;第2个选择是不返回出现异常的地址运行,而是选择一个已经修复的地址返回。如果使用的是memcpy()就只有第1个选择。但是copy_{to,from}_user()可以有第2个选择。 <span class="pun">.fixup</span>段就是为了实现这个修复功能。当copy过程中出现访问非法用户空间地址的时候,do_page_fault()返回的地址变成 <span class="lit">9998</span>标号处,此时可以计算剩余未copy的字节长度,程序还可以继续执行。

对比前面分析的结果,其实_arch_copy_to_user()可以近似等效如下关系。

uaccess_enable_not_uao();	
memcpy(ubuf, kbuf, size);      ==     __arch_copy_to_user(ubuf, kbuf, size);	
uaccess_disable_not_uao();

先插播一条消息,解释copy_template.S为何是memcpy()。memcpy()在ARM64平台是由汇编代码实现。其定义在arch/arm64/lib/memcpy.S文件。

.weak memcpy	
ENTRY(__memcpy)	
ENTRY(memcpy)	
#include "copy_template.S"	
        ret	
ENDPIPROC(memcpy)	
ENDPROC(__memcpy)

所以很明显,memcpy()和__memcpy()函数定义是一样的。并且memcpy()函数声明是weak,因此可以重写memcpy()函数(扯得有点远)。再扯一点,为何使用汇编呢?为何不使用lib/string.c文件的memcpy()函数呢?当然是为了优化memcpy() 的执行速度。lib/string.c文件的memcpy()函数是按照字节为单位进行copy(再好的硬件也会被粗糙的代码毁掉)。

但是现在的处理器基本都是32或者64位,完全可以4 bytes或者8 bytes甚至16 bytes copy(考虑地址对齐的情况下)。可以明显提升执行速度。所以,ARM64平台使用汇编实现。这部分知识可以参考这篇博客《ARM64 的 memcpy 优化与实现》。

下面继续进入正题,再重复一遍:内核态访问用户空间地址,如果触发page fault,只要用户空间地址合法,内核态也会像什么也没有发生一样修复异常(分配物理内存,建立页表映射关系)。但是如果访问非法用户空间地址,就选择第2条路,尝试救赎自己。这条路就是利用 <span class="pun">.fixup</span><span class="pln">__ex_table</span>段。

如果无力回天只能给当前进程发送SIGSEGV信号。并且,轻则kernel oops,重则panic(取决于kernel配置选项CONFIG_PANIC_ON_OOPS)。在内核态访问非法用户空间地址的情况下,do_page_fault()最终会跳转 <span class="pln">no_context</span>标号处的do_kernel_fault()。

static void __do_kernel_fault(unsigned long addr, unsigned int esr,	
                              struct pt_regs *regs)	
{	
        /*	
         * Are we prepared to handle this kernel fault?	
         * We are almost certainly not prepared to handle instruction faults.	
         */	
        if (!is_el1_instruction_abort(esr) && fixup_exception(regs))	
                return;	
        /* ... */	
}

fixup_exception()继续调用search_exception_tables(),其通过查找_extable段。__extable段存储exception table,每个entry存储着异常地址及其对应修复的地址。

例如上述的 <span class="lit">9998:subx0,end,dst</span>指令的地址就会被找到并修改do_page_fault()函数的返回地址,以达到跳转修复的功能。其实查找过程是根据出问题的地址addr,查找_extable段(exception table)是否有对应的exception table entry,如果有就代表可以被修复。由于32位处理器和64位处理器实现方式有差别,因此我们先从32位处理器异常表的实现原理说起。

_extable段的首尾地址分别是 <span class="pln">__start___ex_table</span><span class="pln">__stop___ex_table</span>(定义在include/asm-generic/vmlinux.lds.h。这段内存可以看作是一个数组,数组的每个元素都是 <span class="kwd">struct exception_table_entry</span>类型,其记录着异常发生地址及其对应的修复地址。

                        exception tables	
__start___ex_table --> +---------------+	
                       |     entry     |	
                       +---------------+	
                       |     entry     |	
                       +---------------+	
                       |      ...      |	
                       +---------------+	
                       |     entry     |	
                       +---------------+	
                       |     entry     |	
__stop___ex_table  --> +---------------+

在32位处理器上,struct exception_table_entry定义如下:

struct exception_table_entry {	
        unsigned long insn, fixup;	
};

有一点需要明确,在32位处理器上,unsigned long是4 bytes。insn和fixup分别存储异常发生地址及其对应的修复地址。根据异常地址ex_addr查找对应的修复地址(未找到返回0),其示意代码如下:

unsigned long search_fixup_addr32(unsigned long ex_addr)	
{	
        const struct exception_table_entry *e;	
        for (e = __start___ex_table; e < __stop___ex_table; e++)	
                if (ex_addr == e->insn)	
                        return e->fixup;	
        return 0;	
}

在32位处理器上,创建exception table entry相对简单。针对copy{to,from}user()汇编代码中每一处用户空间地址访问的指令都会创建一个entry,并且insn存储当前指令对应的地址,fixup存储修复指令对应的地址。

当64位处理器开始发展起来,如果我们继续使用这种方式,势必需要2倍于32位处理器的内存存储exception table(因为存储一个地址需要8 bytes)。所以,kernel换用另一种方式实现。在64处理器上,struct exception_table_entry定义如下:

struct exception_table_entry {	
        int insn, fixup;	
};

每个exception table entry占用的内存和32位处理器情况一样,因此内存占用不变。但是insn和fixup的意义发生变化。insn和fixup分别存储着异常发生地址及修复地址相对于当前结构体成员地址的偏移(有点拗口)。例如,根据异常地址ex_addr查找对应的修复地址(未找到返回0),其示意代码如下:

unsigned long search_fixup_addr64(unsigned long ex_addr)	
{	
        const struct exception_table_entry *e;	
        for (e = __start___ex_table; e < __stop___ex_table; e++)	
                if (ex_addr == (unsigned long)&e->insn + e->insn)	
                        return (unsigned long)&e->fixup + e->fixup;	
        return 0;	
}

因此,我们的关注点就是如何去构建exception_table_entry。我们针对每个用户空间地址的内存访问都需要创建一个exception table entry,并插入_extable段。例如下面的汇编指令(汇编指令对应的地址是随意写的,不用纠结对错。理解原理才是王道)。

0xffff000000000000: ldr x1, [x0]	
0xffff000000000004: add x1, x1, #0x10	
0xffff000000000008: ldr x2, [x0, #0x10]	
/* ... */	
0xffff000040000000: mov x0, #0xfffffffffffffff2    // -14	
0xffff000040000004: ret

假设x0寄存器保存着用户空间地址,因此我们需要对0xffff000000000000地址的汇编指令创建一个exception table entry,并且我们期望当x0是非法用户空间地址时,跳转返回的修复地址是0xffff000040000000。为了计算简单,假设这是创建第一个entry, <span class="pln">__start___ex_table</span>值是0xffff000080000000。那么第一个exception table entry的insn和fixup成员的值分别是:0x80000000和0xbffffffc(这两个值都是负数)。因此,针对copy{to,from}user()汇编代码中每一处用户空间地址访问的指令都会创建一个entry。所以0xffff000000000008地址处的汇编指令也需要创建一个exception table entry。

所以,如果内核态访问非法用户空间地址究竟发生了什么?上面的分析流程可以总结如下:

  1. 访问非法用户空间地址:

    0xffff000000000000:ldr x1,[x0]

  2. MMU触发异常

  3. CPU调用do_page_fault()

  4. do_page_fault()调用search_exception_table()(regs->pc == 0xffff000000000000)

  5. 查看_extable段,寻找0xffff000000000000 并且返回修复地址0xffff000040000000

  6. do_page_fault()修改函数返回地址(regs->pc = 0xffff000040000000)并返回

  7. 程序继续执行,处理出错情况

  8. 修改函数返回值x0 = -EFAULT (-14) 并返回(ARM64通过x0传递函数返回值)

总结

到了回顾总结的时候,copy_{to,from}_user()的思考也到此结束。我们来个总结结束此文。

  1. 无论是内核态还是用户态访问合法的用户空间地址,当虚拟地址并未建立物理地址的映射关系的时候,page fault的流程几乎一样,都会帮助我们申请物理内存并创建映射关系。所以这种情况下memcpy()和copy_{to,from}_user()是类似的。

  2. 当内核态访问非法用户空间地址的时候,根据异常地址查找修复地址。这种修复异常的方法并不是建立地址映射关系,而是修改do_page_fault()返回地址。而memcpy()无法做到这点。

  3. 在使能 <span class="pln">CONFIG_ARM64_SW_TTBR0_PAN</span>或者 <span class="pln">CONFIG_ARM64_PAN</span>(硬件支持的情况下才有效)的时候,我们只能使用copy_{to,from}_user()这种接口,直接使用memcpy()是不行的。

最后,我想说,即使在某些情况下memcpy()可以正常工作。但是,这也是不推荐的,不是良好的编程习惯。在用户空间和内核空间数据交互上,我们必须使用类似copy_{to,from}_user()的接口。为什么类似呢?因为还有其他的接口用于内核空间和用户空间数据交互,只是没有copy_{to,from}_user()出名。例如:{get,put}_user()。

感谢您的耐心阅读。

本文转载自蜗窝科技:http://www.wowotech.net/memory_management/454.html

推荐教程:《Linux运维

以上がLinux の copy_{to, from}_user() についての深い理解 (コード付き)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事はCSDNで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
Linux操作のマスター:実用的なガイドLinux操作のマスター:実用的なガイドApr 12, 2025 am 12:10 AM

Linux操作をマスターする理由は、幅広いアプリケーションシナリオと強力な機能です。 1)Linuxは、開発者、システム管理者、テクノロジー愛好家に適しており、サーバー管理、組み込みシステム、コンテナ化テクノロジーで使用されています。 2)Linuxの学習は、ファイルシステム構造、シェルの使用、ユーザー許可管理、プロセス管理から始めることができます。 3)Linuxコマンドラインは、LS、MKDIR、CDなどのシェルを介してコマンドを実行し、リダイレクトおよびパイプライン操作をサポートするコアツールです。 4)高度な使用法には、バックアップスクリプトなどの自動化されたスクリプトの書き込み、TARコマンド、条件付き判断が含まれます。 5)一般的なエラーには、エコー、セットX、$?を介してデバッグできます。 6)パフォーマンス最適化の提案

Linuxの5つの柱:彼らの役割を理解するLinuxの5つの柱:彼らの役割を理解するApr 11, 2025 am 12:07 AM

Linuxシステムの5つの柱は次のとおりです。1。Kernel、2。SystemLibrary、3。Shell、4。FileSystem、5。SystemTools。カーネルはハードウェアリソースを管理し、基本的なサービスを提供します。システムライブラリは、アプリケーション用の事前コンパイルされた機能を提供します。シェルは、ユーザーがシステムと対話するインターフェイスです。ファイルシステムはデータを整理して保存します。また、システムツールはシステム管理とメンテナンスに使用されます。

Linuxメンテナンスモード:ツールとテクニックLinuxメンテナンスモード:ツールとテクニックApr 10, 2025 am 09:42 AM

Linux Systemsでは、起動時に特定のキーを押すか、「sudosystemctlrescue」などのコマンドを使用することにより、メンテナンスモードを入力できます。メンテナンスモードを使用すると、管理者は、ファイルシステムの修復、パスワードのリセット、セキュリティの脆弱性など、干渉なしにシステムメンテナンスとトラブルシューティングを実行できます。

主要なLinux操作:初心者向けガイド主要なLinux操作:初心者向けガイドApr 09, 2025 pm 04:09 PM

Linuxの初心者は、ファイル管理、ユーザー管理、ネットワーク構成などの基本操作をマスターする必要があります。 1)文件管理:使用mkdir、タッチ、ls rm 3)ネットワーク構成:ifconfig、echo、およびufwコマンドを使用します。これらの操作はLinuxシステム管理の基礎であり、それらをマスターすることでシステムを効果的に管理できます。

sudoを使用して、Linuxのユーザーに高い特権を付与するにはどうすればよいですか?sudoを使用して、Linuxのユーザーに高い特権を付与するにはどうすればよいですか?Mar 17, 2025 pm 05:32 PM

この記事では、LinuxのSudo特権を管理する方法について説明します。重要な焦点は、 /etc /sudoersの安全性とアクセスを制限することです。

LinuxでSSHに2要素認証(2FA)を実装するにはどうすればよいですか?LinuxでSSHに2要素認証(2FA)を実装するにはどうすればよいですか?Mar 17, 2025 pm 05:31 PM

この記事では、Google Authenticatorを使用してLinux上のSSH用の2要素認証(2FA)のセットアップ、インストール、構成、およびトラブルシューティング手順の詳細に関するガイドを提供します。 Enhanced Secなど、2FAのセキュリティ利益を強調しています

TOP、HTOP、VMSTATなどのツールを使用してLinuxのシステムパフォーマンスを監視するにはどうすればよいですか?TOP、HTOP、VMSTATなどのツールを使用してLinuxのシステムパフォーマンスを監視するにはどうすればよいですか?Mar 17, 2025 pm 05:28 PM

この記事では、Linuxシステムのパフォーマンスを監視するためにTop、HTOP、およびVMSTATを使用して、効果的なシステム管理のための独自の機能とカスタマイズオプションを詳述することについて説明します。

パッケージマネージャー(apt、yum、dnf)を使用してLinuxのソフトウェアパッケージを管理するにはどうすればよいですか?パッケージマネージャー(apt、yum、dnf)を使用してLinuxのソフトウェアパッケージを管理するにはどうすればよいですか?Mar 17, 2025 pm 05:26 PM

記事では、APT、Yum、およびDNFを使用してLinuxでソフトウェアパッケージの管理を行い、インストール、更新、および削除をカバーしています。さまざまな分布に対する機能と適合性を比較します。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

Dreamweaver Mac版

Dreamweaver Mac版

ビジュアル Web 開発ツール

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

強力な PHP 統合開発環境

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい