分类 技术 下的文章

什么是 KDE Connect?它的主要特性是什么?它应该如何安装?本文提供了基本的使用指南。

科技日新月异。有各种软件、硬件和不同外形的设备。未来,不同设备之间将无缝集成,工作流程会跨越多个设备。每一天,我们都在向跨所有联网设备发送和接收数据的状态迈进。而 KDE Connect 就是引领 Linux 桌面系统向这一天进展的一面旗帜。

什么是 KDE Connect?

KDE Connect 是由 KDE 桌面团队开发的,提供了 Linux 系统与其他系统,如 Windows、MacOS、Android 及 Linux,的无缝连接。

KDE Connect 安装后会使你拥有接收电话的通知、发送或接受短信、浏览文件、发送或接受文件等许多功能。

此外,KDE Connect 在无线网络上遵循安全协议,以防止任何隐私泄露。它是自由开源的软件,出现任何隐患的可能性微乎其微。组合了这些优良特性,KDE Connect 是一款出色的工具。

下文介绍你如何安装和使用它。

安装 KDE Connect

本文将为你展示如何在 Linux 发行版和安卓智能手机之间连接。Windows 与安卓的连接也应是如此。

KDE Connect 的设置是一个双向过程。你必须在你的 Linux 发行版和你的安卓智能手机都安装 KDE Connect。

在 Linux 发行版上安装

在你的 Linux 发行版上安装 KDE Connect 很简单。其在所有的主流 Linux 发行版的官方仓库中都可用。如果你在用 Ubuntu,并且想在终端安装,运行如下指令:

sudo apt install kdeconnect

Fedora:

sudo dnf install kdeconnect

Arch Linux:

pacman -S kdeconnect

或在 “ 软件 Software ” 应用中搜索并安装。

对于 Windows 和其他 Linux 发行版,请参考 这个页面 的其他几种下载安装方式。

在安卓智能手机上安装

在安卓设备中,在谷歌 Play 商店中搜索 “KDE Connect” 并安装。

谷歌 Play 商店中的 KDE Connect

如果你在使用某个自由版本的安卓,你可以用下面的链接来通过 F-droid 安装。(感谢我们的读者提出这一条)。

F-droid 商店中的 KDE Connect

设置 KDE Connect

KDE Connect 可以连接相同网络中的设备。因此,确保你的 Linux 系统和安卓设备都连接到了同一个无线网络中。

打开你手机里的 KDE Connect。你应该可以见到你的 Linux 系统的名称。如果没有看到任何内容,确保你的设备和 Linux 都连接到了同一个网络后点击“ 刷新 Refresh ”。

安卓中的 KDE Connect 显示连接到了 Linux 系统

打开 Linux 中的 KDE Connect,你应当见到你的手机进入了下图展示的样子。

配对前的 KDE Connect

现在,点开你手机的名称然后点击 “ 配对 Pair ”。

紧接着你的手机就会收到一个提醒:是否接受配对。选择 “ 接受 Accept ”。

KDE Connect 的配对请求

代表你的手机的图标应当转为绿色 ,这表示你的手机和 Linux 系统都已经连接且配对。

成功配对后的 KDE Connect

默认情况下,程序会开启以下特性:

  • 多媒体控制
  • 远程输入
  • 远程演示
  • 搜寻设备
  • 分享文件

以下的特性需要你的安卓设备的额外权限。因为它们与隐私相关,这意味着你需要手动启用它们。

  • 短信发送及接收
  • 控制媒体播放器
  • 手机上接受电脑的键盘输入
  • 提醒同步
  • 来电提醒
  • 联系人同步
  • 接受鼠标操作

对于这些特性,你需要去手动打开选项,并在安卓手机中授予权限。然后你就可以在 Linux 设备中享受这些服务了。

示例:提醒同步

我将为你演示提醒同步选项应在何处打开。打开你安卓手机中的 KDE Connect 程序,进入 “ 已连接设备 Connected Device ” 部分。打开 “ 提醒同步 Notification Sync ” 并选择 “ 打开设置应用 OPEN SETTINGS ”。

对 KDE Connect 启用通知访问,然后点击 “ 允许 Allow ”。

打开同步提醒中

之后将展示你手机上的提醒到你的 Linux 设备。例如,下面的提醒是我在我的测试安卓设备中收到的。

手机中的样例提醒

同样的信息也展现在了 Linux 系统中的 KDE Connect。

来自手机的样例信息在 KDE Connect 中

同样地,在给 KDE Connect 权限后,你可以启动对你可用的其他服务。

总结

我希望这个指南可以帮助你在你的 Linux 系统和手机中设置 KDE Connect。

你可以在给与一些主要权限后,轻松地设置几个特性,以使 KDE Connect 应用程序发挥最大优势。配置完成后,你不再需要一直看你的手机。因为你可以在工作时轻易地在你的台式电脑或笔记本上阅读提示、回消息。

你觉得 KDE Connect 怎么样?发在下面的评论框来让我知道吧。

(题图:MJ/5b09a037-14c3-4f62-a15a-dfd9fb2c7b3a)


via: https://www.debugpoint.com/2022/01/kde-connect-guide/

作者:Arindam 选题:lkxed 译者:yjacks 校对:wxy

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

当我第一次知道 DNS 时,我想它应该不会很复杂。不就是一些存储在服务器上的 DNS 记录罢了。有什么大不了的?

但是教科书上只是介绍了 DNS 的原理,并没有告诉你实际使用中 DNS 可能会以多少种方式破坏你的系统。这可不仅仅是缓存问题!

所以我 在 Twitter 上发起了一个提问,征集人们遇到的 DNS 问题,尤其是那些一开始看起来与 DNS 没什么关系的问题。(“总是 DNS 问题”这个梗)

我不打算在这篇文章中讨论如何解决或避免这些问题,但我会放一些讨论这些问题的链接,在那里可以找到解决问题的方法。

问题:网络请求缓慢

如果你的网络比预期的要慢,这是因为某些原因导致 DNS 解析器变慢了。这可能是解析器负载过大或者存在内存泄漏等原因导致的。

我的路由器的 DNS 转发器曾遇到过这个问题,导致我的所有 DNS 请求很慢。我通过重启路由器解决了这个问题。

问题:DNS 超时

一些网友提到由于 DNS 查询超时,他们的网络请求需要耗时 2 秒多甚至 30 秒。这跟“网络请求缓慢”问题类似,但情况要更糟糕,因为 DNS 请求就会消耗掉几秒钟时间。

Sophie Haskins 有一篇关于 Kubernete DNS 超时的博客文章 一次 Kube DNS 踩坑经历

问题:ndots 设置

一些网友提到在 /etc/resolv.conf 中设置 ndots:5 时会出现问题。

下面是从 这篇《Kubernetes 容器荚中 /etc/resolv.conf 里设置 ndots:5 为什么会拖慢你的程序性能》中引用的 /etc/resolv.conf文件。

    nameserver 100.64.0.10
    search namespace.svc.cluster.local svc.cluster.local cluster.local eu-west-1.compute.internal
    options ndots:5

如果你用上面的配置文件,想要查询得域名是 google.com,那么你的程序会调用 getaddrinfo 函数,而它会依次查询以下域名:

  1. google.com.namespace.svc.cluster.local.
  2. google.com.svc.cluster.local.
  3. google.com.cluster.local.
  4. google.com.eu-west-1.compute.internal.
  5. google.com.

简单来说,它会检查 google.com 是不是 search 这一行中的某个子域名。

所以每发起一次 DNS 查询,你都得先等待前 4 次查询失败后才能获取到最终查询结果。

问题:难以判断系统使用的 DNS 解析器

这本身并不是一个问题,但当你遇到 DNS 问题时,一般都会跟 DNS 解析器有关。我没有一种判断 DNS 解析器的万能方法。

下面是我知道的方法:

  • 在 Linux 系统上,最常见的是通过 /etc/resolv.conf 来选择 DNS 解析器。但是也有例外,比如浏览器可能会忽略 /etc/resolv.conf,而是使用 基于 HTTPS 的 DNS DNS-over-HTTPS 服务。
  • 如果你使用的是 UDP DNS,你可以通过 sudo tcpdump port 53 来查看 DNS 请求被发送到了哪里。但如果你使用的是基于 HTTPS 的 DNS 或 基于 TLS 的 DNS DNS over TLS ,这个方法就不行了。

我依稀记得这在 MacOS 系统上会更加令人迷惑,我也不清楚原因。

问题:DNS 服务器返回 NXDOMAIN 而不是 NOERROR

这是我曾经遇到过的一个 Nginx 不能解析域名的问题。

  • 我设置 Nginx 使用一个特定的 DNS 服务器来解析 DNS 查询
  • 当访问这个域名时,Nginx 做了两次查询,第一次是对 A 的,第二次是对 AAAA
  • 对于 A 的查询,DNS 服务器返回 NXDOMAIN
  • Nginx 认为这个域名不存在,然后放弃查询
  • 对于 AAAA 的查询 DNS 服务器返回了成功
  • 但 Nginx 忽略了对 AAAA 返回的查询结果,因为它前面已经放弃查询了

问题出在 DNS 服务器本应该返回 NOERROR 的——那个域名确实存在,只是没有关于 A 的记录罢了。我报告了这个问题,然后他们修复了这个问题。

我自己也写出过这个问题,所以我理解为什么会发生这种情况——很容易想当然地认为“没有要查询的记录,就应该返回 NXDOMAIN 错误码”。

问题:自动生效的 DNS 缓存

如果你在生成一个域名的 DNS 记录之前就访问这个域名,那么这个记录的缺失会被缓存起来。当你第一次遇到这个问题时一定会非常吃惊——我也是去年才知道有这个问题。

缓存的 TTL 就是域名的 起始权限记录 Start of Authority (SOA) 记录的 TTL ——比如对于 jvns.ca ,这个值是一个小时。

问题:Nginx 永久缓存 DNS 记录

如果你在 Nginx 中使用下面的配置:

    location / {
        proxy_pass https://some.domain.com;
    }

Nginx 只会在启动的时候解析一次 some.domain.com,以后不会再对其进行解析。这是非常危险的操作,尤其是对于那些 IP 地址经常变动的域名。它可能平安无事地运行几个月,然后突然在某个凌晨两点把你从床上纠起来。

针对这个问题已经有很多众所周知的方法了,但由于本文不是关于 Nginx 的,所以我不打算深入探讨它。但你第一次遇到它时一定会很惊讶。

这是一篇关于这个问题发生在 AWS 负载均衡器上的 博客文章

问题:Java 永久缓存 DNS 记录

跟上面类似的问题,只是出现在 Java 上:据说 这与你 Java 的配置有关。“JVM 的默认 TTL 设置可能会导致只有 JVM 重启时才会刷新 DNS 记录。”

我还没有遇到过这个问题,不过我那些经常写 Java 的朋友遇到过这个问题。

当然,任何软件都可能存在永久缓存 DNS 的问题,但据我所知它经常出现在 Nginx 和 Java 上。

问题:被遗忘的 /etc/hosts 记录

这是另一种缓存问题:/etc/hosts 中的记录会覆盖你的常规 DNS 设置!

让人迷惑的是 dig 命令会忽略 /etc/hosts 文件。所以当你使用 dig whatever.com 来查询 DNS 信息时,它会告诉你一切正常。

问题:电子邮件未发送 / 将成为垃圾邮件

电子邮件是通过 DNS(MX 记录, SPF 记录, DKIM 记录)来发送和验证的,所以有些电子邮件问题其实是 DNS 问题。

问题:对国际化域名无效

你可以使用非 ASCII 字符甚至是表情符来注册域名,比如 拉屎网 https://?.la

DNS 能够处理国际化域名是因为 ?.la 会被用 punycode 编码将转换为 xn--ls8h.la

尽管已经有了 DNS 处理国际化域名的标准,很多软件并不能很好地处理国际化域名。Julian Squires 的 干掉 Chrome 浏览器的表情符!! 就是一个非常有趣的例子。

问题:TCP DNS 被防火墙拦截

有人提到一些防火墙会允许在 53 端口上使用 UDP 协议,但是禁止 TCP 协议。然而很多 DNS 查询需要在 53 端口上使用 TCP,这可能会导致很难排查的间歇性的问题。

问题:musl 不支持 TCP DNS

很多应用程序使用 libcgetaddrinfo 来做 DNS 查询。musl 是用在 Alpine Docker 容器上的 glibc 替代品。而它不支持 TCP DNS。如果你的 DNS 查询的响应数据超过 DNS UDP 数据包的大小(512 字节)就会出现问题。

我对此仍然不太清楚,我下面我的理解也可能是错的:

  1. muslgetaddrinfo 发起一个 DNS 请求
  2. DNS 服务器发现请求的响应数据太大了,没法放入一个 DNS 数据包中
  3. DNS 服务器返回一个 空截断响应 empty truncated response ,并期望客户端通过 TCP DNS 重新用发起查询
  4. musl 不支持 TCP DNS,所以根本不会重试

关于这个问题的文章:在 Alpine Linux 上的 DNS 解析问题

问题:getaddrinfo 不支持轮询 DNS

轮询 round robin DNS 是一种 负载均衡 load balancing 技术,每次 DNS 查询都会获得一个不同的 IP 地址。显然如果你使用 gethostbyname 做 DNS 查询不会有任何问题,但是用 getaddrinfo 就不行了。因为 getaddrinfo 会对获得的 IP 地址进行排序。

在你从 gethostbyname 切换到 getaddrinfo 时可能完全不会意识到这可能会引起负载均衡问题。

这个问题可能会非常隐蔽,如果你不是用 C 语言编程的话,这些函数调用被隐藏在各种调用库背后,你可能完全意识不到发生了这种改变。所以某次看似人畜无害的升级就可能导致你的 DNS 负载均衡失效。

下面是讨论这个的一些文章:

问题:启动服务时的竞争条件

有人 提到 使用 Kubernete DNS 时遇到的问题:他们有两个同时启动的容器,一旦启动就会立即尝试解析对方的地址。由于 Kubernete DNS 还没有改变,所以 DNS 查询会失败。这个失败会被缓存起来,所以后续的查询会一直失败。

写在最后

我所列举的不过是 DNS 问题的冰山一角,期待大家告诉我那些我没有提到的问题和相关链接。我希望了解这些问题在实际中是如何发生的以及如何被解决的。

(题图:MJ/f512f18e-2e1d-4614-bed1-b0a0c373e14d)


via: https://jvns.ca/blog/2022/01/15/some-ways-dns-can-break/

作者:Julia Evans 选题:lujun9972 译者:toknow-gh 校对:wxy

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

本章将介绍如何在 Bash Shell 脚本中使用数组。学习添加元素、删除元素和获取数组长度。

在本系列的前面部分中,你了解了变量。变量中可以有单个值。

数组内部可以有多个值。当你必须一次处理多个变量时,这会使事情变得更容易。你不必将各个值存储在新变量中。

因此,不要像这样声明五个变量:

distro1=Ubuntu
distro2=Fedora
distro3=SUSE
distro4=Arch Linux
distro5=Nix

你可以在单个数组中初始化它们所有:

distros=(Ubuntu Fedora SUSE "Arch Linux" Nix)

与其他一些编程语言不同,你不使用逗号作为数组元素分隔符。

那挺好的。让我们看看如何访问数组元素。

在 Bash 中访问数组元素

使用索引(数组中的位置)访问数组元素。要访问索引 N 处的数组元素,请使用:

${array_name[N]}
? 与大多数其他编程语言一样,Bash Shell 中的数组从索引 0 开始。这意味着第一个元素的索引为 0,第二个元素的索引为 1,第 n 个元素的索引为 n-1

因此,如果你想打印 SUSE,你将使用:

echo ${distros[2]}

Example of accessing array elements in bash shell

? ${ 之后或 } 之前不能有任何空格。你不能像 ${ array[n] } 那样使用它。

一次访问所有数组元素

假设你要打印数组的所有元素。

你可以一一使用 echo ${array[n]} 但这确实没有必要。有一个更好更简单的方法:

${array[*]}

这将为你提供所有数组元素。

Accessing all array elements at once in bash shell

在 Bash 中获取数组长度

如何知道数组中有多少个元素? 有一个专门的方法 在 Bash 中获取数组长度

${#array_name[@]}

就这么简单,对吧?

Get array length in bash

在 Bash 中添加数组元素

如果必须向数组添加其他元素,请使用 += 运算符 将元素追加到 Bash 中的现有数组

array_name+=("new_value")

这是一个例子:

Append new element to array

? 追加元素时使用 () 很重要。

你还可以使用索引将元素设置在任何位置。

array_name[N]=new_value

但请记住使用正确的索引编号。 如果在现有索引上使用它,新值将替换该元素。

如果你使用“越界”索引,它仍会添加到最后一个元素之后。例如,如果数组长度为 6,并且你尝试在索引 9 处设置新值,则该值仍将作为最后一个元素添加到第 7 个位置(索引 6)。

删除数组元素

你可以使用 Shell 内置的 unset 通过提供索引号来删除数组元素:

unset array_name[N]

这是一个示例,我删除了数组的第四个元素。

Delete array element in bash

你还可以通过 unset 来删除整个数组:

unset array_name
? Bash 中没有严格的数据类型规则。你可以创建一个同时包含整数和字符串的数组。

?️ 练习时间

让我们练习一下你所学到的有关 Bash 数组的知识。

练习 1:创建一个 Bash 脚本,其中包含五个最佳 Linux 发行版的数组。全部打印出来。

现在,用 “Hannah Montanna Linux” 替换中间的选择。

练习 2:创建一个 Bash 脚本,该脚本接受用户提供的三个数字,然后以相反的顺序打印它们。

预期输出:

Enter three numbers and press enter
12 23 44
Numbers in reverse order are: 44 23 12

我希望你喜欢通过本系列学习 Bash Shell 脚本。在下一章中,你将学习如何使用 if-else。敬请关注。

(题图:MJ/09477e2f-2bf9-4fdf-bc1e-c894a068adf2)


via: https://itsfoss.com/bash-arrays/

作者:Abhishek Prakash 选题:lkxed 译者:geekpi 校对:wxy

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

getline() 提供了一种更灵活的方法,可以在不破坏系统的情况下将用户数据读入程序。

在 C 语言中读取字符串是一件非常危险的事情。当读取用户输入时,程序员可能会尝试使用 C 标准库中的 gets 函数。它的用法非常简单:

char *gets(char *string);

gets() 从标准输入读取数据,然后将结果存储在一个字符串变量中。它会返回一个指向字符串的指针,如果没有读取到内容,返回 NULL 值。

举一个简单的例子,我们可能会问用户一个问题,然后将结果读入字符串中:

#include <stdio.h>
#include <string.h>

int main()
{
  char city[10]; // 例如 "Chicago"

  // 这种方法很糟糕 .. 不要使用 gets

  puts("Where do you live?");
  gets(city);

  printf("<%s> is length %ld\n", city, strlen(city));

  return 0;
}

输入一个相对较短的值就可以:

Where do you live?
Chicago
<Chicago> is length 7

然而,gets() 函数非常简单,它会天真地读取数据,直到它认为用户完成为止。但是它不会检查字符串是否足够容纳用户的输入。输入一个非常长的值会导致 gets() 存储的数据超出字符串变量长度,从而导致覆盖其他部分内存。

Where do you live?
Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch
<Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch> is length 58
Segmentation fault (core dumped)

最好的情况是,覆盖部分只会破坏程序。最坏的情况是,这会引入一个严重的安全漏洞,恶意用户可以通过你的程序将任意数据插入计算机的内存中。

这就是为什么在程序中使用 gets() 函数是危险的。使用 gets(),你无法控制程序尝试从用户读取多少数据,这通常会导致缓冲区溢出。

安全的方法

fgets() 函数历来是安全读取字符串的推荐方法。此版本的 gets() 提供了一个安全检查,通过仅读取作为函数参数传递的特定数量的字符:

char *fgets(char *string, int size, FILE *stream);

fgets() 函数会从文件指针读取数据,然后将数据存储到字符串变量中,但最多只能达到 size 指定的长度。我们可以更新示例程序来测试这一点,使用 fgets() 而不是 gets()

#include <stdio.h>
#include <string.h>

int main()
{
    char city[10]; // 例如 "Chicago"

    puts("Where do you live?");

    // fgets 虽好但是并不完美
    fgets(city, 10, stdin);

    printf("<%s> is length %ld\n", city, strlen(city));

    return 0;
}

如果编译运行,你可以在提示符后输入任意长的城市名称。但是,程序只会读取 size = 10 数据存储到字符串变量中。因为 C 语言在字符串末尾会添加一个空(\0)字符,这意味着 fgets() 只会读取 9 个字符到字符串中。

Where do you live?
Minneapolis
<Minneapol> is length 9

虽然这肯定比 fgets() 读取用户输入更安全,但代价是如果用户输入过长,它会“切断”用户输入。

新的安全方法

更灵活的解决方案是,如果用户输入的数据比变量可能容纳的数据多,则允许字符串读取函数为字符串分配更多内存。根据需要调整字符串变量大小,确保程序始终有足够的空间来存储用户输入。

getline() 函数正是这样。它从输入流读取输入,例如键盘或文件,然后将数据存储在字符串变量中。但与 fgets()gets() 不同,getline() 使用 realloc() 调整字符串大小,确保有足够的内存来存储完整输入。

ssize_t getline(char **pstring, size_t *size, FILE *stream);

getline() 实际上是一个名为 getdelim() 的类似函数的装饰器,它会读取数据一直到特殊分隔符停止。本例中,getline() 使用换行符(\n)作为分隔符,因为当从键盘或文件读取用户输入时,数据行由换行符分隔。

结果证明这是一种更安全的方法读取任意数据,一次一行。要使用 getline(),首先定义一个字符串指针并将其设置为 NULL ,表示还没有预留内存,再定义一个 size_t 类型的“字符串大小” 的变量,并给它一个零值。当你调用 getline() 时,你需要传入字符串和字符串大小变量的指针,以及从何处读取数据。对于示例程序,我们可以从标准输入中读取:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
  char *string = NULL;
  size_t size = 0;
  ssize_t chars_read;

  // 使用 getline 读取长字符串

  puts("Enter a really long string:");

  chars_read = getline(&string, &size, stdin);
  printf("getline returned %ld\n", chars_read);

  // 检查错误

  if (chars_read < 0) {
    puts("couldn't read the input");
    free(string);
    return 1;
  }

  // 打印字符串

  printf("<%s> is length %ld\n", string, strlen(string));

  // 释放字符串使用的内存

  free(string);

  return 0;
}

使用 getline() 读取数据时,它将根据需要自动为字符串变量重新分配内存。当函数读取一行的所有数据时,它通过指针更新字符串的大小,并返回读取的字符数,包括分隔符。

Enter a really long string:
Supercalifragilisticexpialidocious
getline returned 35
<Supercalifragilisticexpialidocious
> is length 35

注意,字符串包含分隔符。对于 getline(),分隔符是换行符,这就是为什么输出中有换行符的原因。 如果你不想在字符串值中使用分隔符,可以使用另一个函数将字符串中的分隔符更改为空字符。

通过 getline(),程序员可以安全地避免 C 编程的一个常见陷阱:你永远无法知道用户可能会输入哪些数据。这就是为什么使用 gets() 不安全,而 fgets() 又太笨拙的原因。相反,getline() 提供了一种更灵活的方法,可以在不破坏系统的情况下将用户数据读入程序。

(题图:MJ/4b23132f-8916-42ae-b2da-06fd2812bea8)


via: https://opensource.com/article/22/5/safely-read-user-input-getline

作者:Jim Hall 选题:lkxed 译者:MjSeven 校对:wxy

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

在本系列的第四章,学习在 Bash 中使用基本数学运算。

你可以使用 Bash 脚本做很多事情。对变量执行简单的算术运算就是其中之一。

Bash shell 中算术运算的语法如下:

$((arithmetic_operation))

假设你必须计算两个变量的总和。你这样做:

sum=$(($num1 + $num2))

(()) 内空格的使用没有限制。你可以使用 $(( $num1+ $num2))$(( $num1+ $num2 )) 或者 $(( $num1+ $num2 ))。它们都一样。

在通过示例详细讨论之前,我先分享一下它支持的算术运算符。

Bash 中的基本算术运算符

以下是 Bash shell 中算术运算符的列表。

运算符描述
+加法
-减法
*乘法
/整数除法(不带小数)
%模除法(仅余数)
**求幂(a 的 b 次方)
? Bash 不支持浮点数(小数)。你必须使用其他命令(例如 bc)来处理它们。

Bash 中的加法和减法

让我们通过编写一个脚本来看看它,该脚本从用户那里获取两个数字,然后打印它们的总和和减法。

#!/bin/bash

read -p "Enter first number: " num1
read -p "Enter second number: " num2

sum=$(($num1+$num2))
sub=$(($num1-$num2))
echo "The summation of $num1 and $num2 is $sum"
echo "The substraction of $num2 from $num1 is $sub"

我相信你熟悉上一章中使用 read 命令来 在 Bash 中接受用户输入

你应该关注这两行:

sum=$(($num1+$num2))
sub=$(($num1-$num2))

将此脚本保存为 sum.sh 并运行它。给它一些输入并检查结果。

Example of addition and subtraction in Bash shell script

Bash 中的乘法

现在让我们转向乘法。

这是一个将公里转换为米的示例脚本(这给美国读者带来了麻烦 ?)。作为参考,1 公里等于 1000 米。

#!/bin/bash

read -p "Enter distance in kilometers: " km
meters=$(($km*1000))

echo "$km KM equals to $meters meters"

将脚本保存为 multi.sh,赋予其执行权限并运行它。这是一个示例输出:

Multiplication in bash script

看起来不错,不是吗? 让我们继续进行除法。

Bash 脚本中的除法

让我们用一个非常简单的脚本来看看除法:

#!/bin/bash

num1=50
num2=5

result=$(($num1/$num2))

echo "The result is $result"

你很容易猜到结果:

The result is 10

没关系。但是让我们更改数字并尝试将 50 除以 6。结果如下:

The result is 8

但这不正确。 正确答案应该是 8.33333。

这是因为 Bash 默认情况下只处理整数。你需要额外的命令行工具来处理浮点(小数)。

最流行的工具是 bc,它是一种处理数学运算的非常强大的计算器语言。不过,你现在不需要关注细节。

你必须通过管道将算术运算“回显”给 bc

echo "$num1/$num2" | bc -l

于是,将之前的脚本修改为:

#!/bin/bash

num1=50
num2=6

result=$(echo "$num1/$num2" | bc -l)

echo "The result is $result"

现在你得到结果:

The result is 8.33333333333333333333

请注意 result=$(echo "$num1/$num2" | bc -l),它现在使用你在 本系列第 2 章 中看到的命令替换。

-l 选项加载标准数学库。默认情况下,bc 最多保留 20 位小数。你可以通过以下方式将比例更改为较小的位数:

result=$(echo "scale=3; $num1/$num2" | bc -l)

让我们看看 Bash 中浮点的更多示例。

在 Bash 脚本中处理浮点

让我们修改 sum.sh 脚本来处理浮点。

#!/bin/bash

read -p "Enter first number: " num1
read -p "Enter second number: " num2

sum=$( echo "$num1+$num2" | bc -l)
sub=$( echo "scale=2; $num1-$num2" | bc -l)
echo "The summation of $num1 and $num2 is $sum"
echo "The substraction of $num2 from $num1 is $sub"

现在尝试运行它,看看是否可以正确处理浮点:

Floating points in bash script

?️? 练习时间

是时候一起做一些数学和 Bash 练习了。

练习 1:创建一个脚本,接受以 GB 为单位的输入并以 MB 和 KB 为单位输出其等效值。

练习 2:编写一个带有两个参数并以指数格式输出结果的脚本。因此,如果输入 2 和 3,输出将为 8,即 2 的 3 次方。

提示:使用幂运算符 **

练习 3:编写一个将摄氏度转换为华氏度的脚本。

提示:使用公式 F = C x (9/5) + 32。你必须在此处使用 bc 命令。

你可以在社区中讨论练习及其方案。

在下一章中,你将 了解 Bash 中的数组。敬请关注。

(题图:MJ/8a9dfb90-99a4-4203-bc44-d805d09bc16f)


via: https://itsfoss.com/bash-arithmetic-operation/

作者:Abhishek Prakash 选题:lkxed 译者:geekpi 校对:wxy

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

熟悉 ABI 的概念、ABI 稳定性的重要性以及 Linux 稳定 ABI 中包含的内容。
LCTT 译注:昨天,AlmaLinux 称将 放弃 对 RHEL 的 1:1 兼容性,但将保持对 RHEL 的 ABI 兼容,以便在 RHEL 上运行的软件可以无缝地运行在 AlmaLinux 上。可能有的同学对 ABI 的概念还不是很清楚,因此翻译此文供大家了解。

许多 Linux 爱好者都熟悉 Linus Torvalds 的 著名告诫:“我们不破坏用户空间”,但可能并非每个听到这句话的人都清楚其含义。

这个“第一规则”提醒开发人员关于应用程序的二进制接口(ABI)的稳定性,该接口用于应用程序与内核之间的通信和配置。接下来的内容旨在使读者熟悉 ABI 的概念,阐述为什么 ABI 的稳定性很重要,并讨论 Linux 稳定 ABI 中包含了哪些内容。Linux 的持续增长和演进需要对 ABI 进行变更,其中一些变更引起了争议。

什么是 ABI?

ABI 表示 应用程序二进制接口 Applications Binary Interface 。理解 ABI 概念的一种方式是考虑它与其他概念的区别。对于许多开发人员来说, 应用程序编程接口 Applications Programming Interface (API)更为熟悉。通常,库的头文件和文档被认为是其 API,以及还有像 HTML5 这样的标准文档。调用库或交换字符串格式数据的程序必须遵守 API 中所描述的约定,否则可能得到意外的结果。

ABI 类似于 API,因为它们规定了命令的解释和二进制数据的交换方式。对于 C 程序,ABI 通常包括函数的返回类型和参数列表、结构体的布局,以及枚举类型的含义、顺序和范围。截至 2022 年,Linux 内核仍然几乎完全是 C 程序,因此必须遵守这些规范。

内核系统调用接口” 的描述可以在《Linux 手册第 2 节》中找到,并包括了可从中间件应用程序调用的类似 mountsync 的 C 版本函数。这些函数的二进制布局是 Linux ABI 的第一个重要组成部分。对于问题 “Linux 的稳定 ABI 包括哪些内容?”,许多用户和开发人员的回答是 “sysfs(/sys)和 procfs(/proc)的内容”。而实际上,官方 Linux ABI 文档 确实主要集中在这些 虚拟文件系统 上。

前面着重介绍了 Linux ABI 在程序中的应用方式,但未涵盖同等重要的人为因素。正如下图所示,ABI 的功能需要内核社区、C 编译器(如 GCCclang)、创建用户空间 C 库(通常是 glibc)的开发人员,以及按照 可执行与链接格式(ELF) 布局的二进制应用程序之间的合作努力。

开发社区内的合作

为什么我们关注 ABI?

来自 Torvalds 本人的 Linux ABI 的稳定性保证,使得 Linux 发行版和个人用户能够独立更新内核,而不受操作系统的影响。

如果 Linux 没有稳定的 ABI,那么每次内核需要修补以解决安全问题时,操作系统的大部分甚至全部内容都需要重新安装。显然,二进制接口的稳定性是 Linux 的可用性和广泛采用的重要因素之一。

Terminal output

如上图所示,内核(在 linux-libc-dev 中)和 Glibc(在 libc6-dev 中)都提供了定义文件权限的位掩码。显然,这两个定义集必须一致!apt 软件包管理器会识别软件包提供每个文件。Glibc ABI 的潜在不稳定部分位于 bits/ 目录中。

在大部分情况下,Linux ABI 的稳定性保证运作良好。按照 康韦定律 Conway's Law ,在开发过程中出现的烦人技术问题往往是由于不同软件开发社区之间的误解或分歧所致,而这些社区都为 Linux 做出了贡献。不同社区之间的接口可以通过 Linux 包管理器的元数据轻松地进行想象,如上图所示。

Y2038:一个 ABI 破坏的例子

通过考虑当前正在进行的、缓慢发生 的 “Y2038” ABI 破坏的例子,可以更好地理解 Linux ABI。在 2038 年 1 月,32 位时间计数器将回滚到全零,就像较旧车辆的里程表一样。2038 年 1 月听起来还很遥远,但可以肯定的是,如今销售的许多物联网设备仍将处于运行状态。像今年安装的 智能电表智能停车系统 这样的普通产品可能采用的是 32 位处理器架构,而且也可能不支持软件更新。

Linux 内核已经在内部转向使用 64 位的 time_t 不透明数据类型来表示更晚的时间点。这意味着像 time() 这样的系统调用在 64 位系统上已经变更了它们的函数签名。这些努力的艰难程度可以在内核头文件中(例如 time\_types.h)清楚地看到,在那里放着新的和 _old 版本的数据结构。

里程表翻转

Glibc 项目也 支持 64 位时间,那么就大功告成了,对吗?不幸的是,根据 Debian 邮件列表中的讨论 来看,情况并非如此。发行版面临难以选择的问题,要么为 32 位系统提供所有二进制软件包的两个版本,要么为安装介质提供两个版本。在后一种情况下,32 位时间的用户将不得不重新编译其应用程序并重新安装。正如往常一样,专有应用程序才是一个真正的头疼问题。

Linux 稳定 ABI 里到底包括什么内容?

理解稳定 ABI 有些微妙。需要考虑的是,尽管大部分 sysfs 是稳定 ABI,但调试接口肯定是不稳定的,因为它们将内核内部暴露给用户空间。Linus Torvalds 曾表示,“不要破坏用户空间”,通常情况下,他是指保护那些 “只想它能工作” 的普通用户,而不是系统程序员和内核工程师,后者应该能够阅读内核文档和源代码,以了解不同版本之间发生了什么变化。下图展示了这个区别。

稳定性保证

普通用户不太可能与 Linux ABI 的不稳定部分进行交互,但系统程序员可能无意中这样做。除了 /sys/kernel/debug 以外,sysfs(/sys)和 procfs(/proc)的所有部分都是稳定的。

那么其他对用户空间可见的二进制接口如何呢,包括 /dev 中的设备文件、内核日志文件(可通过 dmesg 命令读取)、文件系统元数据或在内核的 “命令行” 中提供的 “引导参数”(在引导加载程序如 GRUB 或 u-boot 中可见)呢?当然,“这要视情况而定”。

挂载旧文件系统

除了 Linux 系统在引导过程中出现挂起之外,文件系统无法挂载是最令人失望的事情。如果文件系统位于付费客户的固态硬盘上,那么问题确实十分严重。当内核升级时,一个能够在旧内核版本下挂载的 Linux 文件系统应该仍然能够挂载,对吗?实际上,“这要视情况而定”。

在 2020 年,一位受到伤害的 Linux 开发人员在内核的邮件列表上 抱怨道

内核已经接受这个作为一个有效的可挂载文件系统格式,没有任何错误或任何类型的警告,而且已经这样稳定地工作了多年……我一直普遍地以为,挂载现有的根文件系统属于内核<->用户空间或内核<->现有系统边界的范围,由内核接受并被现有用户空间成功使用的内容所定义,升级内核应该与现有用户空间和系统兼容。

但是有一个问题:这些无法挂载的文件系统是使用一种依赖于内核定义,但并未被内核使用的标志的专有工具创建的。该标志未出现在 Linux 的 API 头文件或 procfs/sysfs 中,而是一种 实现细节)。因此,在用户空间代码中解释该标志意味着依赖于“未定义行为”,这是个几乎会让每个软件开发人员都感到战栗的短语。当内核社区改进其内部测试并开始进行新的一致性检查时,“man 2 mount” 系统调用突然开始拒绝具有专有格式的文件系统。由于该格式的创建者明确是一位软件开发人员,因此他未能得到内核文件系统维护者的同情。

施工标志上写着工作人员在树上进行工作

线程化内核的 dmesg 日志

/dev 目录中的文件格式是否保证稳定或不稳定?dmesg 命令 会从文件 /dev/kmsg 中读取内容。2018 年,一位开发人员 为 dmesg 输出实现了线程化,使内核能够“在打印一系列 printk() 消息到控制台时,不会被中断和/或被其他线程的并发 printk() 干扰”。听起来很棒!通过在 /dev/kmsg 输出的每一行添加线程 ID,实现了线程化。密切关注的读者将意识到这个改动改变了 /dev/kmsg 的 ABI,这意味着解析该文件的应用程序也需要进行相应的修改。由于许多发行版没有编译启用新功能的内核,大多数使用 /bin/dmesg 的用户可能没有注意到这件事,但这个改动破坏了 GDB 调试器 读取内核日志的能力。

确实,敏锐的读者会认为 GDB 的用户运气不佳,因为调试器是开发人员工具。实际上并非如此,因为需要更新以支持新的 /dev/kmsg 格式的代码位于内核自己的 Git 源代码库的 “树内” 部分。对于一个正常的项目来说,单个代码库内的程序无法协同工作就是一个明显的错误,因此已经合并了一份 使 GDB 能够与线程化的 /dev/kmsg 一起工作的补丁

那么 BPF 程序呢?

BPF 是一种强大的工具,可以在运行的内核中监控甚至实时进行配置。BPF 最初的目的是通过允许系统管理员即时从命令行修改数据包过滤器,从而支持实时网络配置。Alexei Starovoitov 和其他人极大地扩展了 BPF,使其能够跟踪任意内核函数。跟踪明显是开发人员的领域,而不是普通用户,因此它显然不受任何 ABI 保证的约束(尽管 bpf() 系统调用 具有与其他系统调用相同的稳定性承诺)。另一方面,创建新功能的 BPF 程序为“取代内核模块成为扩展内核的事实标准手段”提供了可能性。内核模块使设备、文件系统、加密、网络等工作正常,因此明显是“只希望它工作”的普通用户所依赖的设施。问题是,与大多数开源内核模块不同,BPF 程序传统上不在内核源代码中。

2022 年春季,一个提案 成为了焦点,该提案提议使用微型 BPF 程序而不是设备驱动程序补丁,对广泛的人机接口设备(如鼠标和键盘)提供支持。

随后进行了一场激烈的讨论,但这个问题显然在 Torvalds 在开源峰会上的评论 中得到解决:

他指出,如果你破坏了“普通(非内核开发人员)用户使用的真实用户空间工具”,那么你需要修复它,无论是否使用了 eBPF。

一致意见似乎正在形成,即希望其 BPF 程序在内核更新后仍能正常工作的开发人员 将需要将其提交到内核源代码库中一个尚未指定的位置。敬请关注后继发展,以了解内核社区对于 BPF 和 ABI 稳定性将采取什么样的政策。

结论

内核的 ABI 稳定性保证适用于 procfs、sysfs 和系统调用接口,但也存在重要的例外情况。当内核变更破坏了“树内”代码或用户空间应用程序时,通常会迅速回滚有问题的补丁。对于依赖内核实现细节的专有代码,尽管这些细节可以从用户空间访问,但它并没有受到保护,并且在出现问题时得到的同情有限。当像 Y2038 这样的问题无法避免 ABI 破坏时,会以尽可能慎重和系统化的方式进行过渡。而像 BPF 程序这样的新功能提出了关于 ABI 稳定性边界的尚未解答的问题。

致谢

感谢 Akkana PeckSarah R. NewmanLuke S. Crawford 对早期版本材料的有益评论。

(题图:MJ/da788385-ca24-4be5-bc27-ad7e7ef75973)


via: https://opensource.com/article/22/12/linux-abi

作者:Alison Chaiken 选题:lkxed 译者:ChatGPT 校对:wxy

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