分类 技术 下的文章

今天的帖子来自于最近的 Go 语言的一次小测试,观察下面的测试基础片段 [1]

func BenchmarkSortStrings(b *testing.B) {
        s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"}
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
                sort.Strings(s)
        }
}

sort.Stringssort.StringSlice(s) 的便捷包装器,sort.Strings 在原地对输入进行排序,因此不会分配内存(或至少 43% 回答此问题的 Twitter 用户是这么认为的)。然而,至少在 Go 的最近版本中,基准测试的每次迭代都会导致一次堆分配。为什么会是这种情况?

正如所有 Go 程序员应该知道的那样,接口是以 双词结构 实现的。每个接口值包含一个字段,其中保存接口内容的类型,以及指向接口内容的指针。 [2]

在 Go 语言伪代码中,一个接口可能是这样的:

type interface struct {
        // the ordinal number for the type of the value
        // assigned to the interface 
        type uintptr

        // (usually) a pointer to the value assigned to
        // the interface
        data uintptr
}

interface.data 可以容纳一个机器字(在大多数情况下为 8 个字节),但一个 []string 却需要 24 个字节:一个字用于指向切片的底层数组;一个字用于存储切片的长度;另一个字用于存储底层数组的剩余容量。那么,Go 是如何将 24 个字节装入个 8 个字节的呢?通过编程中最古老的技巧,即间接引用。一个 []string,即 s,需要 24 个字节;但 *[]string —— 即指向字符串切片的指针,只需要 8 个字节。

逃逸到堆

为了让示例更加明确,以下是重新编写的基准测试,不使用 sort.Strings 辅助函数:

func BenchmarkSortStrings(b *testing.B) {
        s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"}
        b.ReportAllocs()
        for i := 0; i < b.N; i++ {
                var ss sort.StringSlice = s
                var si sort.Interface = ss // allocation
                sort.Sort(si)
        }
}

为了让接口正常运行,编译器将赋值重写为 var si sort.Interface = &ss,即 ss 的地址分配给接口值。 [3] 我们现在有这么一种情况:出现一个持有指向 ss 的指针的接口值。它指向哪里?还有 ss 存储在哪个内存位置?

似乎 ss 被移动到了堆上,这也同时导致了基准测试报告中的分配:

Total:    296.01MB   296.01MB (flat, cum) 99.66%
      8            .          .           func BenchmarkSortStrings(b *testing.B) { 
      9            .          .               s := []string{"heart", "lungs", "brain", "kidneys", "pancreas"} 
     10            .          .               b.ReportAllocs() 
     11            .          .               for i := 0; i < b.N; i++ { 
     12            .          .                   var ss sort.StringSlice = s 
     13     296.01MB   296.01MB                   var si sort.Interface = ss // allocation 
     14            .          .                   sort.Sort(si) 
     15            .          .               } 
     16            .          .           } 

发生这种分配是因为编译器当前无法确认 sssi 生存期更长。Go 编译器开发人员对此的普遍态度是,觉得 这个问题改进的余地,不过我们另找时间再议。事实上,ss 就是被分配到了堆上。因此,问题变成了:每次迭代会分配多少个字节?为什么不去询问 testing 包呢?

% go test -bench=. sort_test.go
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i7-5650U CPU @ 2.20GHz
BenchmarkSortStrings-4          12591951                91.36 ns/op           24 B/op          1 allocs/op
PASS
ok      command-line-arguments  1.260s

可以看到,在 amd 64 平台的 Go 1.16 beta1 版本上,每次操作会分配 24 字节。 [4] 然而,在同一平台先前的 Go 版本中,每次操作则消耗了 32 字节。

% go1.15 test -bench=. sort_test.go
goos: darwin
goarch: amd64
BenchmarkSortStrings-4          11453016                96.4 ns/op            32 B/op          1 allocs/op
PASS
ok      command-line-arguments  1.225s

这引出了本文的主题,即 Go 1.16 版本中即将推出的一项便利改进。不过在讨论这个内容之前,我需要聊聊 “ 尺寸类别 size class ”。

尺寸类别

在解释什么是 “ 尺寸类别 size class ” 之前,我们先考虑个问题,理论上的 Go 语言在运行时是如何在其堆上分配 24 字节的。有一个简单的方法:追踪目前为止已分配到的所有内存的动向——利用指向堆上最后分配的字节的指针。分配 24 字节,堆指针就会增加 24,然后将前一个值返回给调用函数。只要写入的请求 24 字节的代码不超出该标记的范围,这种机制就没有额外开销。不过,现实情况下,内存分配器不仅要分配内存,有时还得释放内存。

最终,Go 语言程序在运行时将释放这些 24 字节,但从运行的视角来看,它只知道它给调用者的开始地址。它不知道从该地址起始之后又分配了多少字节。为了允许释放内存,我们假设的 Go 语言程序运行时分配器必须记录堆上每个分配的长度值。那么这些长度值的分配存储在何处?当然是在堆上。

在我们的设想中,当程序运行需要分配内存的时候,它可以请求稍微多一点,并把它用来存储请求的数量。而对于我们的切片示例而言,当我们请求 24 字节时,实际上会消耗 24 字节加上存储数字 24 的一些开销。这些开销有多大?事实上,实际上的最小开销量是一个字。 [5]

用来记录 24 字节分配的开销将是 8 字节。25% 不是很大,但也不算糟糕,随着分配的大小增加,开销将变得微不足道。然而,如果我们只想在堆上存储一个字节,会发生什么?开销将是请求数据量的 8 倍!是否有一种更高效的方式在堆上分配少量内存?

与其在每个分配旁边存储长度,不如将相同大小的内容存储在一起,这个主意如何?如果所有的 24 字节的内容都存储在一起,那么运行时会自动获取它们的大小。运行时所需要的是一个单一的位,指示 24 字节区域是否在使用中。在 Go 语言中,这些区域被称为 Size Classes,因为相同大小的所有内容都会存储在一起(类似学校班级,所有学生都按同一年级分班,而不是 C++ 中的类)。当运行时需要分配少量内存时,它会使用能够容纳该分配的最小的尺寸类别。

无限制的尺寸类别

现在我们知道尺寸类别是如何工作的了,那么问题又来了,它们存储在哪里?和我们想的一样,尺寸类别的内存来自堆。为了最小化开销,运行时会从堆上分配较大的内存块(通常是系统页面大小的倍数),然后将该空间用于单个大小的分配。不过,这里存在一个问题————

将大块区域用于存储同一大小的事物的模式很好用 [6] ,如果分配大小的数量是固定的,最好是少数几个。那么在通用语言中,程序可以要求运行时以任何大小分配内存 [7]

例如,想象一下向运行时请求 9 字节。9 字节是一个不常见的大小,因此可能需要一个新的尺寸类别来存储 9 字节大小的物品。因为 9 字节大小的物品不常见,所以分配的其余部分(通常为 4KB 或更多)可能会被浪费。由于尺寸类别的集合是固定的,如果没有精确匹配的 size class 可用,分配将并入到下一个尺寸类别。在我们的示例中,9 字节可能会在 12 字节的尺寸类别中分配。未使用的 3 字节的开销要比几乎未使用的整个尺寸类别分配好。

总结一下

这是谜题的最后一块拼图。Go 1.15 版本没有 24 字节的尺寸类别,因此 ss 的堆分配是在 32 字节的尺寸类别中分配的。由于 Martin Möhrmann 的工作,Go 1.16 版本有一个 24 字节的尺寸类别,非常适合分配给接口的切片值。

相关文章

  1. 我在 Devfest 2017年西伯利亚大会谈 Go 语言
  2. 如果对齐的内存写操作是原子性的,为什么我们还需要 sync/atomic 包呢?
  3. 为你的树莓派创建一个真实的串行控制台
  4. 为什么 Go 语言线程的栈是无限制的?

(题图:MJ/01d5fe46-778f-48fe-9481-162f4d0289dc)


  1. 这不是正确的对排序函数进行基准测试的方式,因为在第一次迭代之后,输入已经排序。但这又是另外一个话题了。 ↩︎
  2. 此语句的准确性取决于所使用的 Go 版本。例如,Go 1.15 版本添加了直接将一些 整数存储在接口值 中的功能,从而节省了分配和间接性。然而,对于大多数值来说,如果它不是指针类型,它的地址将被取出并存储在接口值中。 ↩︎
  3. 编译器在接口值的类型字段中跟踪了这种手法,因此它记住了分配给 si 的类型是 sort.StringSlice 而不是 *sort.StringSlice↩︎
  4. 在 32 位平台上,这个数字减半,但我们不再关注它↩︎
  5. 如果你准备限制分配为 4G 或者可能是 64KB,你可以使用较少内存来存储分配的尺寸,但实际上使用小于一个字来存储长度标头的节省会受到填充的影响。 ↩︎
  6. 将相同大小的物品存储在一起也是一种有效的对抗碎片化的策略。 ↩︎
  7. 这并不是一个不切实际的设想,字符串有各种形状和大小,生成以前未见过的大小的字符串可能就像附加空格一样简单。 ↩︎

via: https://dave.cheney.net/2021/01/05/a-few-bytes-here-a-few-there-pretty-soon-youre-talking-real-memory

作者:Dave Cheney 选题:lujun9972 译者:Drwhooooo 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!几天前,我尝试向其他人解释 Git 遴选(git cherry-pick)的工作原理,结果发现自己反而更混淆了。

我原先以为 Git 遴选是简单地应用一个补丁,但当我真正这样尝试时,却未能成功!

因此,接下来我们将谈论我原来以为的遴选操作(即应用一个补丁),这个理解为何不准确,以及实际上它是如何执行的(进行“三路合并”)。

尽管本文的内容有些深入,但你并不需要全部理解才能有效地使用 Git。不过,如果你(和我一样)对 Git 的内部运作感到好奇,那就跟我一起深入探讨一下吧!

遴选操作并不只是应用一个补丁

我先前理解的 git cherry-pick COMMIT_ID 的步骤如下:

  • 首先是计算 COMMIT_ID 的差异,就如同执行 git show COMMIT_ID --patch > out.patch 这个命令
  • 然后是将补丁应用到当前分支,就如同执行 git apply out.patch 这个命令

在我们详细讨论之前,我想指出的是,虽然大部分情况下这个模型是正确的,如果这是你的认知模型,那就没有问题。但是在一些细微的地方,它可能会错,我觉得这个疑惑挺有意思的,所以我们来看看它究竟是如何运作的。

如果我在存在合并冲突的情况下尝试进行“计算差异并应用补丁”的操作,下面我们就看看具体会发生什么情况:

$ git show 10e96e46 --patch > out.patch
$ git apply out.patch
error: patch failed: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown:17
error: content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown: patch does not apply

这一过程无法成功完成,它并未提供任何解决冲突或处理问题的方案。

而真正运行 git cherry-pick 时的实际情况却大为不同,我遭遇到了一处合并冲突:

$ git cherry-pick 10e96e46
error: could not apply 10e96e46... wip
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run
hint: "git cherry-pick --continue".

因此,看起来 “Git 正在应用一个补丁”这样的理解方式并不十分准确。但这里的错误信息确实标明了 “无法应用 10e96e46”,这么看来,这种理解又不完全是错的。这到底是怎么回事呢?

那么,遴选到底是怎么执行的呢?

我深入研究了 Git 的源代码,主要是想了解 cherry-pick 是如何工作的,最终我找到了 这一行代码

res = do_recursive_merge(r, base, next, base_label, next_label, &head, &msgbuf, opts);

所以,遴选实际上就是一种……合并操作?这有些出乎意料。那具体都合并了什么内容?如何执行这个合并操作的呢?

我意识到我对 Git 的合并操作并不是特别理解,于是我上网搜索了一下。结果发现 Git 实际上采用了一种被称为 “三路合并” 的合并方式。那这到底是什么含义呢?

Git 的合并策略:三路合并

假设我要合并下面两个文件,我们将其分别命名为 v1.pyv2.py

def greet():
    greeting = "hello"
    name = "julia"
    return greeting + " " + name
def say_hello():
    greeting = "hello"
    name = "aanya"
    return greeting + " " + name

在这两个文件间,存在两处不同:

  • def greet()def say_hello
  • name = "julia"name = "aanya"

我们应该选择哪个呢?看起来好像不可能有答案!

不过,如果我告诉你,原始的函数(我们称之为 base.py)是这样的:

def say_hello():
    greeting = "hello"
    name = "julia"
    return greeting + " " + name

一切似乎变得清晰许多!在这个基础上,v1 将函数的名字更改为 greetv2name = "aanya"。因此,合并时,我们应该同时做出这两处改变:

def greet():
    greeting = "hello"
    name = "aanya"
    return greeting + " " + name

我们可以命令 Git 使用 git merge-file 来完成这次合并,结果正是我们预期的:它选择了 def greet()name = "aanya"

$ git merge-file v1.py base.py v2.py -p
def greet():
    greeting = "hello"
    name = "aanya"
    return greeting + " " + name⏎

这种将两个文件与其原始版本进行合并的方式,被称为 三路合并

如果你想在线上试一试,我在 jvns.ca/3-way-merge/ 创建了一个小实验场。不过我只是草草制作,所以可能对移动端并不友好。

Git 合并的是更改,而非文件

我对三路合并的理解是 —— Git 合并的是更改,而不是文件。我们对同一个文件做出两种不同的更改,Git 试图以合理的方式将这两种更改结合到一起。当两个更改都对同一行进行操作时,Git 可能会遇到困难,此时就会产生合并冲突。

Git 也可以合并超过两处的更改:你可以对同一文件有多达 8 处不同的更改,Git 会尝试将所有更改协调一致。这被称为八爪鱼合并,但除此之外我对其并不了解,因为我从未执行过这样的操作。

Git 如何使用三路合并来应用补丁

接下来,让我们进入到一个有些出乎意料的情境!当我们讨论 Git “应用补丁”(如在变基 —— rebase、撤销 —— revert 或遴选 —— cherry-pick 中所做的)时,其实并非是生成一个补丁文件并应用它。相反,实际执行的是一次三路合并。

下面是如何将提交 X 作为补丁应用到你当前的提交,并与之前的 v1v2base 设置相对应:

  1. 在你当前提交中,文件的版本是 v1
  2. 在提交 X 之前,文件的版本是 base
  3. 在提交 X 中,文件的版本是 v2
  4. 执行 git merge-file v1 base v2 以合并它们(实际上,Git 并不直接执行 git merge-file,而是运行一个实现这个功能的 C 函数)。

总的来说,你可以将 basev2 视为“补丁”,它们之间的差异就是你想要应用到 v1 上的更改。

遴选如何运作

假设我们有如下提交图,并且我们打算在 main 分支上遴选提交 Y

A - B (main)
  \ 
   \ 
    X - Y - Z

那么,如何将此情景转化为我们前面提过的 v1v2base 组成的三路合并呢?

  • Bv1
  • Xbase,而 Yv2

所以,XY 共同构成了这个“补丁”。

其实,git rebase 无非就是重复多次执行 git cherry-pick 的过程。

撤销如何运作

现在,假如我们希望在如下的提交图上执行 git revert Y

X - Y - Z - A - B
  • Bv1
  • Ybase,而 Xv2

这个过程反映的实际上就是遴选的情况,不过 XY 的位置颠倒了。我们需要这样做因为我们期望生成一个“反向补丁”。在 Git 中,撤销和遴选关系如此的紧密,它们甚至在同一个文件中实现:revert.c

“三路补丁”是一个非常棒的技巧

使用三路合并将提交作为补丁应用的这个技巧非常巧妙且酷炫,我很惊讶之前从未听说过!我并未听过一个特定的名字来描述这种方法,但我更倾向于称之为“三路补丁”。

“三路补丁”的理念在于,你可以通过两个文件来定义补丁:在应用补丁前后的文件(在我们这篇文章中称之为 basev2)。

因此,总体来看有三个文件被涉及到:一个是原文件,另外两个构成了补丁。

最重要的是,与普通补丁相比,三路补丁是一个更加高效的补丁方案,因为在有两个完整文件的情况下,你拥有更丰富的上下文信息来进行合并。

以下是我们例子中的常规补丁的大致情况:

@@ -1,1 +1,1 @@:
- def greet():
+ def say_hello():
    greeting = "hello"

而下面这就是一个三路补丁。不过,需要提醒的是这个“三路补丁”并不是一个真正的文件格式,这只是我自己提出的一种概念。

BEFORE: (the full file)
def greet():
    greeting = "hello"
    name = "julia"
    return greeting + " " + name
AFTER: (the full file)
def say_hello():
    greeting = "hello"
    name = "julia"
    return greeting + " " + name

《Building Git》 中提到了这点

James Coglan 的书籍 《Building Git》 是我在 Git 源码之外唯一找到的地方,他解释了 git cherry-pick 是如何在底层运用三路合并的(我原以为《Pro Git》可能会提及这个,但我并没能找到此话题的内容)。

我购买完这本书后发现,我早在 2019 年时就已经买过了,这对我来说真的是个很好的参考。

Git 中的合并实际上比这更复杂

在 Git 中,合并不限于三路合并 —— 还有一种我不太理解的叫做“递归合并”,还有许多具体处理文件删除和移动的细节,同时也有多种合并算法。

如果想要了解更多相关知识,我最好的建议是阅读《Building Git》,尽管我还未完全阅读这本书。

Git 应用到底做了什么?

我也参阅了 Git 的源代码,试图理解 git apply 的功能。它似乎(不出意外地)在 apply.c 中实现。这段代码解析了一个补丁文件,并通入目标文件来寻找应该在何处应用补丁。核心逻辑似乎在 这里:思路好像是从补丁建议的行数开始,然后向前向后找寻。

    /*
     * There's probably some smart way to do this, but I'll leave
     * that to the smart and beautiful people. I'm simple and stupid.
     */
    backwards = current;
    backwards_lno = line;
    forwards = current;
    forwards_lno = line;
    current_lno = line;
for (i = 0; ; i++) {
     ...

这个处理过程不禁让人觉得非常直白、与之前的期望相符。

Git 三路应用的工作方式

git apply 命令中也有一个 --3way 参数,可以实现三路合并。因此,我们实际上可以通过如下方式,使用 git apply 来大体实现 git cherry-pick 的功能:

$ git show 10e96e46 --patch > out.patch
$ git apply out.patch --3way
Applied patch to 'content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown' with conflicts.
U content/post/2023-07-28-why-is-dns-still-hard-to-learn-.markdown

但要注意,参数 --3way 并不只用到了补丁文件的内容!补丁文件开始的部分是:

index d63ade04..65778fc0 100644

d63ade0465778fc0 是旧/新文件版本在 Git 对象数据库中的 ID,因此 Git 可以用这些 ID 来执行三路补丁操作。但如果有人将补丁文件通过邮件发送给你,而你并没有新/旧版本的文件,就无法执行这个操作:如果你缺少 blob,将会出现如下错误:

$ git apply out.patch
error: repository lacks the necessary blob to perform 3-way merge.

三路合并有点历史了

有一部分人指出,三路合并比 Git 的历史还要久远,它起源于 70 年代末期左右。有一篇 2007 年的 论文 对此进行了讨论。

就说这么多!

我真的对于我对于 Git 内部应用补丁的核心方法其实理解得并不深入这一点感到非常吃惊——学习这一点真的很酷!

虽然我对 Git 用户界面存在 诸多不满,但是这个特定问题并不包含在内。三路合并似乎是统一解决一系列不同问题的优雅方式,它对于人们来说也很直观(“应用一个补丁”这个想法是许多编程者都习以为常的思考模式,而它底层实现为三路合并的细节,实际上没有人真正需要去思考)。

我顺便快速推荐一下:我正在写一部有关 Git 的 zine,如果你对它的发布感兴趣,你可以注册我非常不频繁的 公告邮件列表

(题图:MJ/321bc2c9-4363-4661-802a-c74fb6a721b2)


via: https://jvns.ca/blog/2023/11/10/how-cherry-pick-and-revert-work/

作者:Julia Evans 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Budgie 是一种现代的桌面体验方式。你可以根据自己的喜好,使用这些定制技巧进一步增强它的功能。

如果你要求我将 MATE 桌面现代化,并增加功能和 GTK 支持,我可能会想出像 Budgie 这样的东西!

Budgie,是一款令人惊艳的桌面环境(DE),为你提供熟悉的布局和独特的用户体验。

那么,如何自定义你的 Budgie 桌面才能提升个人体验呢?莫慌;我这里有几条妙计助你改善你的 Budgie 体验。

我们的目标大致如下:

  • 修改壁纸
  • 修改主题,图标以及光标(简易操作)
  • 自定义面板
  • 自定义停靠区

让我们开始吧!?

1、改变 Budgie 桌面环境壁纸

Budgie 桌面预装了一些好看的壁纸。

修改 Budgie 桌面壁纸,你们只需要做这两个小步骤:

  1. 在主界面点击右键,选择 “ 改变桌面背景 Change Desktop Background
  2. 选择图片,然后该图片就会应用在你的主屏幕中

要是你不太中意其中的可用壁纸,你也可以自网络下载相关壁纸。

下好后,打开文件管理器,导航至图片所在位置,右键点击该图片,选择 “ 设置为壁纸…… Set as Wallpaper... ”选项。

2、修改桌面主题、图标以及光标主题

通常情况下,我们从网络上下载了主题,提取下载的文件,然后就会将文件移动到相应目录以便 应用于 Linux 主题

如何在 Ubnutu 上安装主题

但要那么整的话,既需要花费些功夫,又不是最高效的方法。(如果你好奇去试试的话)

那么此处便是 ocs-url 工具发力的地方了。

ocs-url 是一款一键安装所有主题风格(比如说来自 gnome-look.org 门户网站内容)的工具。

? Gnome-look.orgpling.comOpendesktop.org 旗下的两个网络站点,可以帮助你一站式搜索主题风格,壁纸,图标,以及其它好物。

这个工具与任何桌面环境都没有官方隶属关系。

但令人遗憾的是,它没有被任何 Linux 发行版的软件包管理器包含,也就是说你得在你的系统里手动安装它。

那么首先,移动至 ocs-url 的官方下载页面,转到 “ 文件 Files ” 选项卡,将软件包下载至你的系统中:

完成后,打开你的命令提示符,将文件地址改成文件下载的位置。对于大多数用户来说,便是下载(Downloads)文件夹:

cd Downloads

现在,使用你的 Linux 发行版对应的指令:

适用于 Ubuntu:

sudo dpkg -i ocs-url*.deb

适用于 Fedora:

sudo dnf install qt5-qtbase qt5-qtbase-gui qt5-qtsvg qt5-qtdeclarative qt5-qtquickcontrols && sudo rpm -i ocs-url*.rpm

适用于 Arch:

sudo pacman -S qt5-base qt5-svg qt5-declarative qt5-quickcontrols && sudo pacman -U ocs-url*.pkg.tar.xz

适用于 openSUSE:

sudo zypper install libQt5Svg5 libqt5-qtquickcontrols && sudo rpm -i ocs-url*.rpm

现在,让我们看看该如何安装主题风格,图标以及光标风格。

修改系统主题风格

修改系统主题风格的第一步 —— 访问 Gnome Look 门户网站并选择你中意的主题风格(你可以在 GTK 3 和 GTK 4 之间选择)。

选择任意你最喜欢的主题风格;这里我选择 “Kripton” 风格,你可以看到有一个 “ 安装 Install ” 选项。

在这里,你需要遵循两个小步骤:

  • 点击 “ 安装 Install ” 按钮之后,会呈现该主题风格的不同类型。选择其中一个。
  • 然后会跳出一个提示符,你需要再点击一次 “ 安装 Install ” 按钮:

然后它会打开 ocs-url 工具询问你是否安装所选主题风格。

点击 “ 确定 OK ” 按钮安装该主题风格:

现在,进入 Budgie 桌面设置 Budgie Desktop Settings 界面,然后在“ 样式 Style ” 子菜单下的 “ 组件 Widget ” 选项中点击需要安装的主题风格。

修改图标

要修改图标,先访问有关 Gnome-look 门户网站中的图标区,来搜索你中意的图标:

找到图标后,可以看到有两个按钮:“ 下载 Download ” 和 “ 安装 Install ”。点击 “ 安装 Install ” 按钮。

可以看到有多个图标,但大多数例子里只展现了一个。点击选项之一,弹出提示,再次点击“ 安装 Install ”按钮:

然后,可以看到来自 ocs-url 弹出的提示符询问你是否安装所选图标包,点击 “ 确定 OK ” 安装图标包:

下一步,打开 Budgie 桌面设置 Budgie Desktop Settings 的 “ 图标 icons ” 子菜单,你便可以使用最近安装过的图标风格(我用过 elementary-kde 风格):

修改光标主题风格

和之前步骤类似,访问 Gnome Look 门户网站的光标区 搜索最适合你的光标:

选好心仪的光标风格后,点击相应选项,然后你就可以安装相对应的光标风格了。

弹出提示,再次点击“ 安装 Install ”按钮:

弹出来自 ocs-url 工具的提示,点击安装光标主题风格。只需要点个 “ 确定 OK ” 然后开始安装:

完成后,打开 Budgie 桌面设置 Budgie Desktop Settings 界面,在对应子页面的 “ 光标 Cursors ” 面板中选择刚刚安装的光标主题风格:

3、自定义 Budgie 桌面面板

根据 Linux 发行版的不同,面板的位置也可能不同。例如,假设你用的是 Solus 的 Budgie 或者 Ubuntu 的 Budgie。

打开 Budgie 桌面设置 Budgie Desktop Settings ,在那里你可以寻找到对于面板的相关设置:

如你所见,它将启用的小程序分三个部分显示在面板上:

始端 Start (最左侧)、 中间 Center ,以及 末端 End (最右侧)。

? 注意,每个小程序有自己不同的设置选项,所以还能单独设置它们。

你可以在它们之间做些调整。比如,在这个地方我将时钟工具挪到左边,以便它和我的工作流界面看起来更加融洽:

要想移动小程序,只需要选中该小程序然后点击上下箭头按钮(位于小程序列表上方)。

如果你想移除小程序,只需要选中该程序然后点击删除按钮即可:

这里有一个 “+” 图标样式的选项,可以添加更多小程序。点击它,会为你弹出所有可用的小程序。

找到一个有用的小程序,选中该程序并点击 “ 添加 Add ” 按钮:

要是你希望面板变透明、想1添加阴影,想增加或减少小程序之间的距离,那么就访问设置菜单中的这个页面(如下图):

举个例子,这里,我将自己的面板设置了透明和阴影,让它看起来更舒服点:

4、定制 Budgie 桌面的停靠区

在本节,我会向你介绍如何将自己的停靠区设计得更好,功能性更强:

  • 修改停靠区主题风格
  • 添加更多工具应用以赋予停靠区更多特性

修改停靠区主题风格

可惜啦,这次 ocs-url 工具可就帮不上什么忙了,设置的唯一方式就是手动操作!

首先,访问 Gnome Look 的 Plank 主题区,下载你心仪的 Plank 停靠区主题风格。

这里,我选了模仿 macOS 系统的 Monterey 主题风格(dark-inline 版):

现在,打开命令提示符,找到该主题风格下载位置。

对大多数用户来说,即下载(Downloads)文件夹所在位置:

cd ~/Downloads

现在,使用解压指令 将文件释放到 .local/share/plank/themes/

unzip <theme_name.zip> -d .local/share/plank/themes/

在我的环境下是这样:

unzip Monterey-Dark-inline.zip -d ~/.local/share/plank/themes/

然后,打开 “ Plank 偏好设置 Plank preferences ”,改成刚下载的主题。

我将我的风格改成 “Monterey -Dark inline”:

添加 docklet 应用以增加更多特性

可以、把 docklet 应用当作 applet,但与它不同的是,没有太多选项。

想要添加或移除 docklet 应用,首先得需要打开 “ Plank 偏好设置 Plank preferences ”,然后访问 “Docklets” 菜单,列出可用选项:

现在,如果你想添加一些 docklet 应用,只需要把它们拖进停靠区即可:

但如果你想把添加的 docklet 应用移除出去呢?

简单,把它们从停靠区中拖拽出去就行了:

准备好使用 Budgie 了吗?

当然,为了方便起见,你可以安装已内置 Budgie 桌面环境的 Linux 发行版。

或者你还是可以选择在你现有的发行版(比如说 Ubuntu)中 安装 Budgie

如何在 Ubuntu 上安装 Budgie

在本教程中,我使用的是 Ubuntu 版本的 Budgie。无论你使用哪个发行版,这些步骤都适用于 Budgie 桌面。

? 你是怎么自定义你的 Budgie 桌面的呢?你想为这篇文章贡献更多内容吗?在下方评论区分享你的经验吧。

(题图:MJ/44c073b0-b866-4eeb-b3e0-579d7afe0992)


via: https://itsfoss.com/budgie-customization/

作者:Sagar Sharma 选题:lujun9972 译者:Drwhooooo 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

虽然你可能听到不同的看法,但实际上,它并未像一些批评者所想象的那样完全专有。

对 Ubuntu 的 Snap 打包格式最常见的误解之一是它是专有的 —— 但是深入研究其文档后,会发现这个说法并不对。

在上周末拉脱维亚的里加举行的 Ubuntu 峰会上,笔者有幸采访到 Ubuntu 的 开发者大使 developer advocate ,Igor Ljubuncic。期间,他们详细探讨了关于 Snap 的各种误区,包括它被视为完全闭源的、受 Canonical 控制、必须使用 Canonical 的 Snap 商店等众多谬论。

如果说有什么比糟糕的软件更加厌恶的,那一定是谎言。正如我们在 点评 Fedora 39 时所注意到的,即使在 Linux 诞生之前,各种软件的拥趸们就经常爆发各种 圣战。但我们至少希望能坚守事实的公道。毫无根据的恶意指责是没有必要的:生活本身已经足够糟糕。

笔者的立场很明确,我们并不特别偏爱任何 Linux 发行版或其打包工具。像许多资深电脑技术人员一样,在长期和各种软件打交道后,笔者已经对所有的软件厌烦至极。一句广为接受的说法就是:没有一个软件不让人头疼

Linux 就是一个软件,因而它难免让人头疼。承此,所有的 Linux 发行版也都不尽如人意。包管理器也是一个软件,同样也不尽人意。但幸运的是,至少大多数 Linux 发行版都有一个包管理器。这比没有软件包管理器要好,或者更糟糕的是,有不止一个以上的包管理器,这一点 XKCD 927 漫画体现的淋漓尽致。

我们并不特别青睐 Snap,也不特别反对 Flatpak。笔者个人更偏好 AppImage 格式,它不需要其他额外的框架。但虽然有个 AppImageHub,但该格式却并没有提供软件更新的工具,这个问题就留给了应用本身来解决。

鉴于所有的软件都不完美,那唯一重要的区别就在于其问题严重的程度。一段时间以后,你最关注的就是它是否可运行,能否满足你的需要,以及它的可靠性。

我在早年的职业生涯中花了很多时间在技术支持上,修复其他人的软件。因此,我学到了一个经验,那就是降低软件让人厌烦程度的一个重要因素就是它工作的方式是否容易理解。

Btrfs 是复杂的,而修复它则更是如此。Git 属于本质复杂,其 名称 就体现出这一点。(没错,“git” 是一个名词,而非缩写或代号,有实际的意思 —— “饭桶”。)OStree 可以说是针对二进制文件的 Git,这使得它比普通 Git 至少复杂两倍。而 Flatpak 则是 OStree 的封装。

这意味着增加了两层额外的复杂度:首先,对复杂事物的封装只能隐藏其复杂性,而不能消除其复杂性。其次,你不能使用 Flatpak 构建一个操作系统,因此你还需要 OStree。

因此,我们将来逐一揭穿关于 Snap 格式和工具的一些误解。这不是一篇入门指南,而是对那些不那么显而易见,并且对 Snap 有所误解的人的一份快速概览。

无需商店进行分发

Snap 包其实就是一个 Squashfs,类似于大多数 Linux 安装介质上的系统镜像。Snap 包以两个文件传递:其中一个是命名为 <name>_<revision>.snap,该文件包含了软件本身;另一个则是一个伴随的 声明文件,它为 Snap 提供了数字签名。然后,Canonical 还进一步 详细阐明 了版本修订的工作原则。

使用 snap download 的指令(而非 snap install)可以容易获取这些基本文件:

# snap download firefox
Fetching snap "firefox"
Fetching assertions for "firefox"
Install the snap with:
  snap ack firefox_3252.assert
  snap install firefox_3252.snap

然后,这些文件便可以被复制到另一台设备上进行安装,这种操作不需要访问 Snap 商店,仅需使用输出中的指令即可。

如 Igor 所说:

“这样,从 Snap 商店中,你可以选择你想要的 Snap 包(如 Firefox),将其放入你的内部仓库中,或是 FTP,或是 NFS 上。接着你可以使用它作为在内部安装 Snap 的来源,而这不需要去访问商店。此外,你还可以将这个操作与你所使用的任何调度或部署机制结合起来,就如配置管理那样。”

安装无需声明文件的 Snap 包

通常来说,snap ack 命令会首先读取并验证签名,但是你可以选择跳过这个步骤。

snap install "downloaded snap" --dangerous

上述指令会安装该 Snap 包,并不会验证其签名。请注意,这样做虽然操作简单,但也有一个重要的限制:使用 --dangerous 选项安装的 Snap 包不会自动从商店中更新。

所以,实际上,你可以在你的网络内部分发 Snap 包,避免它们试图连接到 Snap 商店,并自主管理更新。

管控 snapd 内置的更新机制

另一方面,你可以在不忽略验证机制的前提下,管理和控制操作系统何时以及如何更新 Snap 包。Igor 则曾撰写过关于如何使 Snap 更新暂停 的文章。

你可以设置暂停 Snap 的更新一段时间,或永久暂停,甚至只选择暂停特定的 Snap 包,同时也能简单取消此设置。例如:

snap refresh --hold
Auto-refresh of all snaps held indefinitely.

另外,你也可以通过以下方式设置防火墙拦截 Snap API:

sudo iptables -A OUTPUT -d api.snapcraft.io -j DROP

在无 snapd 环境下运行 snaps

.snap 文件实际上就是一个压缩的文件系统,它包含着程序文件(以及各种库等),这些都被存放在一个传统的目录结构中,而该目录结构对于打包在 Snap 应用程序内的应用来说,就是它的根目录。Snapd 负责为此设置挂载名空间,并通过 Apparmorseccomp 实现安全隔离。

你可以将其内容解压并直接运行:

unsquashfs firefox_3252.snap  
Parallel unsquashfs: Using 20 processors
565 inodes (5428 blocks) to write
[=====================/] 5428/5428 100%
created 399 files
created 149 directories
created 166 symlinks
created 0 devices
created 0 fifos
created 0 sockets
ll squashfs-root/
total 80
drwxr-xr-x  7 igor igor  4096 lis  10 02:33 ./
drwxr-xr-x 10 igor igor  4096 lis  19 15:32 ../
drwxr-xr-x  5 igor igor  4096 lis  10 02:33 data-dir/
-rw-r--r--  1 igor igor 32441 lis  10 02:33 default256.png
-rw-r--r--  1 igor igor  9146 lis  10 02:33 firefox.desktop
-rwxr-xr-x  1 igor igor  2680 lis  10 02:33 firefox.launcher*
drwxr-xr-x  2 igor igor  4096 lis  10 02:33 gnome-platform/
drwxr-xr-x  4 igor igor  4096 lis  10 02:33 meta/
-rwxr-xr-x  1 igor igor  3716 lis  10 02:33 patch-default-profile.py*
drwxr-xr-x  4 igor igor  4096 lis  10 02:33 snap/
drwxr-xr-x  4 igor igor  4096 sij  19  2022 usr/

如果你查看 Snap 内 Firefox 二进制文件的动态依赖,你会注意到它希望从根文件系统中获取文件:

ldd usr/lib/firefox/firefox-bin
       linux-vdso.so.1 (0x00007fff33cc5000)
       libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6cf2c00000)
       libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6cf2e40000)
       libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6cf2be0000)
       libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6cf2800000)
       /lib64/ld-linux-x86-64.so.2 (0x00007f6cf300e000)

在 Snap 内部,这个“根”就是你的基础系统(比如 core18 或 core20 等)。但是一旦你解压了这个 Snap,没有 snapd 在安装和运行 Snap 时提供的安全隔离,Firefox 将会尝试直接访问你的根目录的库。这可能会导致执行时的不一致性。

举例来说,你的 Snap 内可能包含的是 GNOME 3.38 版的库,但是你的主机上运行的可能是 GNOME 3.32。如果你尝试解压并运行这个应用,它可能会试图从主机中加载库,这可能引起不一致 —— 更甚者,可能会让程序崩溃。

为了避免这种情况发生,你需要做的唯一事情就是设置 LD_LIBRARY_PATH 环境变量,以让程序知道其库在何处,确保它首选这些库,而不是使用可能导致其运行失败的操作系统中的库副本。

LD_LIBRARY_PATH: ${SNAP_LIBRARY_PATH}${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}:$SNAP/usr/lib:$SNAP/usr/lib/x86_64-linux-gnu

通常,你会希望 LD_LIBRARY_PATH 开始于 /snap/<snap name>/,然后是 /lib/usr/lib 和其他常用路径。至于其他内容,firefox.launcher 文件负责准备运行环境,剩余的,比如 firefox.desktop,都用于桌面集成:如图标、全名、文件关联等。这些内容虽然使应用看起来效果更好,但它们并非严格的必需品。

其实,你甚至不需要解压 Snap 的内容,你可以直接将 Snap 文件本身作为一个 回环设备 挂载 —— 你甚至可以设置为只读 —— 但没有挂载命名空间隔离。并且,如果没有设置环境让 Snap 内部的应用在寻找它的库时首先从 Snap 内部开始,你仍然需要正确地设置库路径。

代理和缓存 Snap 包

正如 Igor 所说,如果客户并不打算自行运营一家具备完整品牌属性的 Snap 商店,他们可以选择手动设置一个 Snap 代理。对此,Canonical 也提供了相应的 文档,并描述了所需的 网络访问 权限。

同时,你也可以 配置 一个缓存 Snap 代理 —— 这项任务稍微简单一些,对于希望降低下载带宽的家庭网络来说,可能是个不错的选择。

搭建自己的 Snap 商店

就如我们之前所述,你完全可以忽略所有来自 Canonical 的基础设施,直接运行自己的 Snap 商店。去年,我们写过一篇关于 Ubuntu Unity 维护者 Rudra Saraswat 的文章,他就 做到了这一点,这只是他的众多项目中之一。据悉,好几个在生产环境中使用 Ubuntu Core 的组织都采取了此种做法,而所有所需的工具都存放在 Ubuntu 仓库中。

Canonical 在这方面发布了大量的文档,包括怎样构建你的 第一个 Snap 包,以及如何用 不同的编程语言 构建。今年的峰会上有多场关于如何构建 Snap 的演讲 - 包括 在平板电脑上构建 Snap 包,以及如何 自动化构建更新的 Snap 包,虽然这对笔者来说有点过于复杂。

学习一些新的术语是有必要的,同时也有 官方文档 提供帮助。这段解释我们特别喜欢:

  • 插槽 slots 是指提供方(即 Snap 提供的资源)
  • 插口 plugs 是指消费者(即使用 Snap 提供的资源的用户)
  • 接口 interfaces 是交互的地方(负责将插口和插槽连接起来)

从我们与 Canonical 代表的对话中,他们似乎对 Snap 商店被误解,以及 Snap 被视为封闭、专有系统的争论显得尤为不满。

大约十五年前,有人曾声称 Canonical 的代码托管和项目管理平台 Launchpad 是专有的,所以 Canonical 在整理代码后在 2009 年 公开发布 了代码库。但如我们交谈的人所言:“没人在意。” 它是 Canonical 的内部工具,对其他人来说并没有太大的用处。他们表示,他们不希望再经历一次这样的情况。

我们还注意到,红帽正在朝反方向前进,即从开源的 Bugzilla 迁移 到封闭的、基于云的 Jira —— 这并未引起太大的争议。

snapd 自身的代码已经托管在 GitHub 上,作为 Canonical 的 snapcore 仓库的一部分。这个被大多数发行版使用的打包格式是一个已经存在、有文档记录的格式。用于进行隔离的工具,是已经存在并在其他发行版中使用的第三方工具,比如,Debian 和 SUSE 家族也使用了 AppArmor,这与 Arch 维基中的 描述 相符,而它的主要竞品,SELinux,则更复杂,主要在红帽及其衍生产品中使用。

尽管 Canonical 自家定制的 Snap 商店 的后端仍然 闭源,但 Snap 格式、snapcore 软件、snapcraft.io 前端,以及更多组件都是开放的。我们再次强调,你完全可以自行搭建 自己的 Snap 商店

请不要受到愤怒的论坛喷子们的误导。

最后再说一点...

实际上,撰写这篇文章的作者曾经就职于红帽和 SUSE,但他主要还是使用 Ubuntu,从 2004 年 Ubuntu 刚刚发布起就开始一直使用。Ubuntu 不但运行顺畅,使用起来也十分便捷。然而,早在多年前他就已经从他的主要工作电脑上删除了 snapd 和相关的一切工具,取而代之的是 deb-get —— 最初这是 Ubuntu MATE 的创造者 Martin Wimpress 编写的。为了更加迅速,他还选择使用 Nala 包管理器 而不是 Apt。

如果可以的话,笔者很希望可以放弃各种形式的 Unix,除了服务器,其他情况下更倾向于使用 RISC OS 或是经典的 MacOS。但是遗憾的是,这两个操作系统在网络浏览器、网络连接,还有多核支持和整体稳定性上有待改进。

笔者今年参加 Ubuntu 峰会的费用是由 Canonical 承担的,这一点他愿意公开。类似的,Linux 基金会曾资助他参加 今年 在 Bilbao 的开源峰会,而红帽则资助了他在 2016 年在 Kraków 参加 Flock to Fedora 峰会。这类赞助可以让我们将广告预算分配到其他地方,但并不会对我们的报道产生影响:我们总会积极追踪那些 IT 新闻。

(题图:MJ/520ba58f-9e07-4acb-af4a-f4832762311f)


via: https://www.theregister.com/2023/11/10/snap_without_ubuntu_tools/

作者:Liam Proven 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Fedora Silverblue 是 一款基于 Fedora Linux 构建的面向桌面的操作系统。这款操作系统非常适合日常使用、开发和容器化的工作流程。它有 许多优势,例如可以在发生问题时轻松回滚操作。如果你想在 Fedora Silverblue 系统上更新或将系统 重定位 rebase 到 Fedora Linux 39,本文会提供帮助。文章不仅会指导你执行操作,还会教你如何在遇到意外情况时撤销操作。

更新你目前的系统

在实际重定位至 Fedora Linux 39 前,你需要先安装所有待处理的更新。你可以在终端中使用下面的命令:

$ rpm-ostree update

或者你也可以通过 GNOME “软件”应用安装更新,然后重新启动系统。

使用 GNOME “软件” 重定位

在 GNOME “软件”应用的更新页面上,你可以看到 Fedora Linux 的新版本已经可以使用。

首先,你需要点击 “ 下载 Download ” 按钮来下载新的操作系统镜像。这个过程可能需要一些时间。完成后,你会发现更新已经准备好进行安装。

接下来,点击 “ 重新启动 & 升级 Restart & Upgrade ” 按钮。这个过程只需要几分钟,一旦更新完成,计算机将会重新启动。重启后,你会看到崭新的 Fedora Linux 39 的系统环境,看起来很简单,是吧?

使用终端进行重定位

如果你更喜欢在终端操作,那么这部分指南就是为你准备的。

首先你需要确认 39 版本是否已经可以使用:

$ ostree remote refs fedora

在命令输出中你应该看到如下内容:

fedora:fedora/39/x86_64/silverblue

如果你想保留当前的部署(这意味着这个部署将会持续在 GRUB 中显示为一个选项,直到你手动移除它),你可以通过运行下面的命令实现:

# 0 是在 rpm-ostree 状态中的条目位置
$ sudo ostree admin pin 0

如果你想移除已经固定的部署,你可以使用下面的命令:

# 2 是在 rpm-ostree 状态中的条目位置
$ sudo ostree admin pin --unpin 2

然后,将你的系统重定位至 Fedora Linux 39 镜像。

$ rpm-ostree rebase fedora:fedora/39/x86_64/silverblue

最后,重启你的电脑,启动进入 Fedora Linux 39 版本。

如何进行回滚

如果遇到任何问题(例如,如果你无法启动 Fedora Linux 39),回滚是非常容易的。在系统启动时,在 GRUB 菜单中选择 Fedora Linux 39 之前的版本,然后你的系统就会启动这个更早的版本而非 Fedora Linux 39。如果你在启动过程中看不到 GRUB 菜单,那么在启动时尝试按下 ESC 键。如果你想让更早版本的选择永久生效,你可以使用下面的命令:

$ rpm-ostree rollback

现在,你已经了解如何将 Fedora Silverblue 系统重定位到 Fedora Linux 39,以及如何进行系统回滚了。那么何不今天就试试看呢?

常见问题解答

在每篇关于重定位 Silverblue 到新版本的文章的评论中,总会有相似的问题,因此我会在这个部分尝试解答这些问题。

问题:在 Fedora 的重定位过程中我能跳过某些版本吗?例如直接从 Fedora 37 Silverblue 更新到 Fedora 39 Silverblue。

答案:虽然有时可能可以在重定位过程中跳过某些版本,但并不推荐这样操作。你应当始终更新到紧邻的新版本(例如从 38 更新到 39),以避免不必要的错误。

问题:我安装了 rpm-fusion ,在重定位过程中出现错误,我应当怎样进行重定位?

答案:如果你在 Silverblue 安装上加入了 rpm-fusion,你在重定位前应当执行以下操作:

rpm-ostree update \
    --uninstall rpmfusion-free-release \
    --uninstall rpmfusion-nonfree-release \
    --install rpmfusion-free-release \
    --install rpmfusion-nonfree-release

执行完上述操作后,你可以按照本篇博文的步骤完成重定位过程。

问题:这个指南是否适用于其他的 ostree 版本(例如 Kinoite, Sericea)?

答案:是的,你可以照着本指南的 使用终端进行重定位 部分的操作来完成所有的 Fedora ostree 版本的重定位过程。只需要使用对应的分支即可。例如对于 Kinoite,你可以使用

fedora:fedora/39/x86_64/kinoite

而非

fedora:fedora/39/x86_64/silverblue

(题图:MJ/71150afc-ae44-48f3-8689-e86758e07b1e)


via: https://fedoramagazine.org/how-to-rebase-to-fedora-linux-39-on-silverblue/

作者:Michal Konečný 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

本文翻译自 GIMP 官网,是 GIMP 教程的一部分。

目的

恭喜你!你在电脑上安装了 GIMP!GIMP 是一个非常强大的图像处理软件,但是不要被它吓到。即使你没有时间学习高级的电脑图形处理技能,GIMP 仍然可以是一个非常有用和方便的快速修改图像的工具。

我希望这些例子能帮助你解决那些需要对图像应用进行快速修改的小需求。希望这也能让你学习到 GIMP 更强大的图像编辑能力。

为了便于快速查看,我将在这篇快速教程中涵盖以下四个要点:

  • 更改图像的大小(尺寸),即缩放
  • 更改 JPEG 的大小(文件大小)
  • 剪裁图像
  • 旋转或翻转图像

为了与这个页面之前的版本保持保持一致,我将使用 NASA 提供的天文学家每日图像(APOD)中的一张图片。

为了跟随这些快速示例,你只需要知道如何找到你的图片并打开它:“ 文件 File 打开 Open ”。

调整图像的大小(尺寸),即缩放

你可能会遇到一个图像太大,不适合特定用途的问题(例如,嵌入网页、在线发布或包含在电子邮件中)。在这种情况下,你通常会希望将图像缩小到更小的尺寸,以便更好地满足你的需求。

在 GIMP 中轻松完成这个任务非常简单。

我们使用的图片是哈勃望远镜拍摄的马头星云红外成像图。

当你第一次在 GIMP 中打开图像时,很可能会发现图像被缩放,以便整个图像都能适合你的画布。对于这个示例,需要注意的是,GIMP 窗口顶部的窗口装饰会显示一些关于图像的信息。

GIMP画布的视图,顶部显示窗口信息

请注意,窗口顶部的信息显示了当前图像的像素尺寸(在这个例子中,像素尺寸为 1225×1280)。

要调整图像的大小到新的尺寸,我们只需要调用“ 缩放图像 Scale Image ”对话框:“ 图像 Image 缩放图像... Scale Image ... ”。

这将打开“缩放图像”对话框:

“缩放图像”对话框

在“缩放图像”对话框中,你会发现一个可以输入新宽度和高度的地方。如果你知道所需图的新尺寸,可以在这里填写相应的值。

宽度 Width ”和“ 高度 Height ”输入框右侧,你也会注意到一个小链。这个图标显示了宽度和高度值被相互锁定,这意味着改变一个值会导致另一个值的变化,以保持相同的宽高比(图像中不会出现奇怪的压缩或拉伸)。

例如,如果你想要将图像宽度调整到 600 像素,你可以在这个宽度输入框中输入这个值,高度将自动更改以保持图像的宽高比:

调整大小到 600 像素

如你所见,在宽度一栏输入 600 像素后会自动将高度更改为 627 像素。

此外,我还展示了 “ 质量 Quality ” 选项下的不同选项。默认值是“ 立方 Cubic ”,但为了保持最佳质量,最好使用 “Sinc(Lanczos3)”。

如果你想使用不同类型的值(而不是像素大小)指定一个新的尺寸,可以通过点击“px”下拉菜单来更改输入值的类型:

更改输入值类型

这种情况的一个常见用途是,如果你想要以原始尺寸的百分比指定一个新的尺寸。在这种情况下,你可以更改为“ 百分比 percent ”,然后在任何字段中输入 50 来将图像缩小一半。

一旦你缩放了图像,别忘了保存你所做的更改:选中 “ 文件 File 导出... Exprert ... ” 以新的文件名导出,或者 “ 文件 File 覆盖 {文件名} Overwrite {FILENAME} ” 覆盖原始文件(谨慎使用)。

有关使用缩放图像的更多信息,你可以查看文档。

修改 JPEG 文件的大小

你也可以在导出为 JPEG 等格式时修改图像的文件大小。JPEG 是一种有损压缩算法,这意味着在将图像保存为 JPEG 格式时,你将牺牲一些图像质量来获得较小的文件大小。

我使用已经将其调整为 200 像素宽(请参见上方)的“马头星云”图像,并使用不同级别的 JPEG 压缩将其导出,以比较 JPEG 压缩的效果:

比较不同级别的 JPEG 压缩

如你所见,即使在质量设置为 80 的情况下,图像的文件大小显著减少了 77%,而图像质量仍然相当合理。

当你完成任何正在进行的图像修改,并准备导出时,只需通过以下方式调用导出对话框:“文件 → 导出 …” 这将调用“ 导出图像 Export Image ”对话框:

导出图像对话框

你可以在此输入新的文件名。如果文件名里包含扩展名(此时为 “.jpg”),GIMP 会尝试为你导出对应的文件格式。此处将图像导出为 JPEG 格式。

如果你需要将文件导出到不同的位置,也可以通过位置窗格导航到计算机上的新位置。当你准备好导出图像时,只需按“ 导出 Expert ”按钮。

这将调用“ 导出图像为 JPEG Export Image as JPEG ”对话框,你可以在其中更改导出的质量:

从“导出为JPG”对话框中更改导出的质量

现在你可以在此对话框中更改导出质量。如果你还勾选了“ 在图像窗口中显示预览 Show preview in image window ”选项,画布上的图像将更新以反映你输入的质量值。这也将启用“ 文件大小: File size: ”信息,告诉你导出后的文件大小。你可能需要移动一些窗口才能在背景中查看画布上的预览。

当你对结果满意时,按“导出”按钮进行导出。

要查看有关导出不同图像格式的更多详细信息,请参阅手册中的“从 GIMP 中获取图像”。

裁剪图像

有很多原因可能会使你想要裁剪图像。你可能想要删除无用的边框或信息,或者你可能希望最终图像的焦点集中在某些特定的细节上。

简而言之,裁剪就是一个将图像缩小到比你开始时小的操作:

原始图像(左),裁剪图像(右)

裁剪图像的步骤非常简单。你可以通过工具面板使用裁剪工具:

工具面板上的裁剪工具

或者通过菜单访问裁剪工具:“ 工具 Tools 变换工具 Transform Tools 裁剪 Crop ”。

GIMP裁剪工具光标

一旦激活该工具,画布上的鼠标光标会改变,以表示正在使用裁剪工具。

现在你可以在画布上的任何位置单击鼠标,然后拖动鼠标到新位置以高亮显示初始选择区域以进行裁剪。在这个阶段,你不必担心精确度,因为在实际裁剪之前,你可以修改最终选区。

使用裁剪工具的第一步

在选择要裁剪的区域后,你会发现选区仍然处于活动状态。在这一点上,将鼠标光标悬停在选区的任何四个角或边缘上都会改变鼠标光标,并高亮显示该区域,以对裁剪进行精调。

你可以点击并拖动任何一侧或一角来移动该部分的选区。

一旦你对裁剪区域满意,只需按键盘上的回车键即可提交裁剪。在你想从头开始或决定不裁剪时,可以按键盘上的 Esc 键退出操作。

有关在GIMP中裁剪的更多信息,请参阅文档。

另一种方法

另一种裁剪图像的方法是首先使用矩形选择工具进行选择:

裁剪选区 矩形选择工具

或者通过菜单:“ 工具 Tools 选择工具 Selection Tools 矩形选择 Rectangle Select ”,然后你可以以与裁剪工具相同的方式高亮显示选区,并调整选区。一旦你有一个喜欢的选区,你就可以通过以下方式将图像裁剪到该选区的大小:“ 图像 Image 裁剪到选区 Crop to Selection ”。

旋转或翻转图像

可能有时你需要旋转图像。例如,你可能使用相机在垂直方向拍摄了图像,但是 GIMP 并没有自动旋转(GIMP 通常会为你自动处理,但并非总是如此)。

有时你也可能想翻转图像。这些命令都位于同一个菜单项下:“ 图像 Image 变换 Transform ”。

翻转图像

如果你想翻转你的图像,变换菜单提供了两种选项:水平翻转或垂直翻转。此操作将沿着指定的轴翻转(镜像)图像。例如,这里显示了在单个图像上应用的所有翻转操作:

应用到基图像(左上角)的所有翻转

旋转图像

变换菜单中的图像旋转限制为 90° 顺时针/逆时针或 180°。 不要误解这意味著 GIMP 不能执行任意角度旋转。任意旋转是针对每个图层进行处理的,而这里的图像旋转适用于整个图像。

旋转示例:原始(左上角),顺时针旋转90°(右上角),逆时针旋转90°(左下角),180°(右下角)。

总结

这里展示的简单示例只是冰山一角。然而,这些是许多没有学习太多图像处理知识的人经常进行的常见修改。希望这个教程对你有所帮助。 我鼓励你阅读其他教程,了解更高级的图像处理方法!

(题图:MJ/9bbe01ba-7cc1-49b1-91a6-2b3d13594503)


via: https://www.gimp.org/tutorials/GIMP_Quickies/

作者:Pat David 译者:TimXiedada 编辑:wxy

本文由贡献者投稿至 Linux 中国公开投稿计划,采用 CC-BY-SA 协议 发布,Linux中国 荣誉推出