分类 技术 下的文章

在桌面上拥抱 Java 应用程序,然后在所有桌面上运行它们。

无论你运行的是哪种操作系统,通常都有几种安装应用程序的方法。有时你可能会在应用程序商店中找到一个应用程序,或者使用 Fedora 上的 DNF 或 Mac 上的 Brew 这样的软件包管理器进行安装,而有时你可能会从网站上下载可执行文件或安装程序。因为 Java 是这么多流行的应用程序的后端,所以最好了解安装它的不同方法。好消息是你有很多选择,本文涵盖了所有这些内容。

坏消息是 Java 太大,我说的不仅仅是文件大小。Java 是一种开放源代码语言和规范,这意味着从理论上讲,任何人都可以创建它的实现版本。这意味着,在安装任何东西之前,必须确定要安装的 Java 发行版。

我需要 JVM 还是 JRE 或者 JDK?

Java 大致分为两个下载类别。 Java 虚拟机 Java Virtual Machine (JVM)是运行时组件;它是使 Java 应用程序能够在计算机上启动和运行的“引擎”。它包含在 Java 运行时环境 Java Runtime Environment (JRE)中。

Java 开发工具包 Java Development Kit (JDK)是一个开发工具包:你可以将其视为一个车库,修理工可以坐在那里进行调整、修理和改进。JDK 包含 Java 运行时环境(JRE)。

以下载来说,这意味着:

  • 如果你是希望运行 Java 应用程序的用户,则只需 JRE(包括了 JVM)。
  • 如果你是希望使用 Java 进行编程的开发人员,则需要 JDK(包括 JRE 库,而 JRE 库又包括 JVM)。 ### OpenJDK、IcedTea 和 OracleJDK 有什么不同?

太阳微系统 Sun Microsystems 被 Oracle 收购时,Java 是该交易的主要部分。幸运的是,Java 是一种开源技术,因此,如果你对 Oracle 维护该项目的方式不满意,则可以选择其他方法。Oracle 将专有组件与 Java 下载捆绑在一起,而 OpenJDK 项目是完全开源的。

IcedTea 项目本质上是 OpenJDK,但其目标是使用户在使用完全自由开源的工具时更容易构建和部署 OpenJDK。

(LCTT 译注:阿里巴巴也有一个它自己维护的 Open JDK 发行版“ 龙井 Dragonwell ”。以下引自其官网:“Alibaba Dragonwell 是一款免费的,生产就绪型 Open JDK 发行版,提供长期支持,包括性能增强和安全修复。……Alibaba Dragonwell 作为 Java 应用的基石,支撑了阿里经济体内所有的 Java 业务。Alibaba Dragonwell 完全兼容 Java SE 标准,……”)

我应该安装哪个 Java?

如果你对这些选择感到不知所措,那么简单的答案就是你应该安装的 Java 实现应该是最容易安装的那个。当应用程序告诉你需要 Java 12,但你的存储库中只有 Java 8 时,可以安装可以从可靠来源中找到的 Java 12 的任何实现。在 Linux 上,你可以一次安装几个不同版本的 Java,它们不会互相干扰。

如果你是需要选择使用哪个版本的开发人员,则应考虑所需的组件。如果选择 Oracle 的版本,请注意,软件包中包含专有的插件和字体,可能会影响你分发你的应用程序。在 IcedTea 或 OpenJDK 上进行开发是最安全的。

从存储库安装 OpenJDK?

现在,你已经知道要选择什么了,你可以使用软件包管理器搜索 OpenJDK 或 IcedTea,然后安装所需的版本。有些发行版使用关键字 latest 来指示最新版本,这通常是你要运行的应用程序所需要的。根据你使用的软件包管理器,你甚至可以考虑使用 grep 过滤搜索结果以仅包括最新版本。例如,在 Fedora 上:

$ sudo dnf search openjdk | grep latest | cut -f1 -d':'

java-latest-openjdk-demo.x86_64
java-openjdk.i686
java-openjdk.x86_64
java-latest-openjdk-jmods.x86_64
java-latest-openjdk-src.x86_64
java-latest-openjdk.x86_64
[...]

只有当你尝试运行的应用程序坚持要求你使用 Java 的旧版本时,你才应该看看 latest 之前的版本。

在 Fedora 或类似系统上安装 Java:

$ sudo dnf install java-latest-openjdk

如果你的发行版不使用 latest 标签,则可以使用其他关键字,例如 default。以下是在 Debian 上搜索 OpenJDK 的信息:

$ sudo apt search openjdk | less
default-jdk
  Standard Java development kit

default-jre
  Standard Java runtime

openjdk-11-jdk
  OpenJDK development kit (JDK)

[...]

在这种情况下,default-jre 软件包适合用户,而 default-jdk 则适合开发人员。

例如,要在 Debian 上安装 JRE:

$ sudo apt install default-jre

现在已安装好 Java。

你的存储库中可能有许多与 Java 相关的软件包。要搜索 OpenJDK,如果你是用户,则查找最新的 JRE 或 JVM,如果你是开发人员,则查找最新的 JDK。

从互联网上安装 Java

如果在存储库中找不到 JRE 或 JDK,或者找不到满足你需求的 JRE 或 JDK,则可以从互联网上下载开源的 Java 软件包。你可以在 openjdk.java.net 中找到需要手动安装的 tar 形式的 OpenJDK 下载文件,或者可以从 Azul 下载 tar 形式的 Zulu 社区版或其可安装的 RPM 或 DEB 软件包。

从 TAR 文件安装 Java

如果从 Java.net 或 Azul 下载 TAR 文件,则必须手动安装。这通常称为“本地”安装,因为你没有将 Java 安装到“全局”位置。你可以在 PATH 中选择一个合适的位置。

如果你不知道 PATH 中包含什么,请查看一下以找出:

$ echo $PATH
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/home/seth/bin

在此示例 PATH 中,位置 /usr/local/bin/home/seth/bin 是不错的选择。如果你是计算机上的唯一用户,那么你自己的家目录就很有意义。如果你的计算机上有很多用户,则最好选择一个通用位置,例如 /usr/local/opt

如果你无权访问需要 sudo 权限的 /usr/local 之类的系统级目录,则可以在你自己的家目录中创建一个本地 bin(意思是 “ 二进制 binary ”,而不是“ 垃圾箱 waste bin ”)或 Applications 文件夹:

$ mkdir ~/bin

如果它不在你的 PATH 中,请将其添加到其中:

$ echo PATH=$PATH:$HOME/bin >> ~/.bashrc
$ source ~/.bashrc

最后,将压缩包解压缩到你选择的目录中。

$ tar --extract --file openjdk*linux-x64_bin.tar.gz --directory=$HOME/bin

Java 现在安装好了。

从 RPM 或 DEB 安装 Java

如果从 Azul.com 下载 RPM 或 DEB 文件,则可以使用软件包管理器进行安装。

对于 Fedora、CentOS、RHEL 等,请下载 RPM 并使用 DNF 进行安装:

$ sudo dnf install zulu*linux.x86_64.rpm

对于 Debian、Ubuntu、Pop\_OS 和类似发行版,请下载 DEB 软件包并使用 Apt 安装它:

$ sudo dpkg -i zulu*linux_amd64.deb

Java 现在安装好了。

用 alternatives 安装你的 Java 版本

一些应用程序是为特定版本的 Java 开发的,不能与其他任何版本一起使用。这种情况很少见,但确实会发生,在 Linux 上,你可以使用本地安装方法(请参阅上面“从 TAR 文件安装 Java”一节)或使用 alternatives 应用程序来解决此冲突。

alternatives 命令会查找 Linux 系统上安装的应用程序,并让你选择要使用的版本。有些发行版,例如 Slackware,不提供 alternatives 命令,因此你必须使用本地安装方法。在 Fedora、CentOS 和类似的发行版上,该命令是 alternatives。在 Debian、Ubuntu 和类似的系统上,该命令是 update-alternatives

要获取当前已安装在 Fedora 系统上的应用程序的可用版本列表:

$ alternatives --list

在 Debian 上,你必须指定可供替代的应用程序:

$ update-alternatives --list java

在 Fedora 上选择要使系统将哪个版本作为默认版本:

$ sudo alternatives --config java

在 Debian 上:

$ sudo updates-alternatives --config java

你可以根据需要运行的应用程序,根据需要更改默认的 Java 版本。

运行 Java 应用

Java 应用程序通常以 JAR 文件的形式分发。根据你安装 Java 的方式,你的系统可能已经为运行 Java 应用程序配置好了,这使你只需双击应用程序图标(或从应用程序菜单中选择它)即可运行。如果必须执行未与系统其余部分集成的本地 Java 安装,则可以直接从终端启动 Java 应用程序:

$ java -jar ~/bin/example.jar &

Java 是个好东西

Java 是少数将跨平台开发放在首位的编程环境之一。没有什么比问一个应用程序是否能在你的平台上运行然后发现该应用程序是用 Java 编写要让人感到松一口气的了。它是如此简单,无论你是开发人员还是用户,你都可以摆脱任何平台上的焦虑。在桌面上拥抱 Java 应用程序,然后在所有桌面上运行它们吧。


via: https://opensource.com/article/19/11/install-java-linux

作者:Seth Kenlon 选题:lujun9972 译者:wxy 校对:wxy

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

NetBSD 的软件包管理器通用、灵活又容易。下面是如何使用它。

NetBSD 以能在几乎所有平台上运行而闻名,但你知道它第二有名的 pkgsrc 包管理器吗?像 NetBSD 一样,pkgsrc 基本上可以在任何系统上运行,或者至少在任意 Unix 和类 Unix 的系统上上运行。你可以在 BSD、Linux、Illumos、Solaris 和 Mac 上安装 pkgsrc。它总共支持 20 多种操作系统。

为什么使用 pkgsrc?

除了 MacOS 之外,所有 Unix 操作系统均自带包管理器。你不一定需要 pkgsrc,但这可能是你想尝试的三个重要原因:

  • 打包。如果你对打包感到好奇,但尚未尝试自己创建一个软件包,那么 pkgsrc 是一个相对简单的系统,尤其是如果你已经熟悉 Makefile 和类似 GNU Autotools 之类的构建系统时。
  • 通用。如果你使用多个操作系统或发行版,那么可能会遇到每个系统的包管理器。你可以在不同的系统上使用 pkgsrc,以便你为一个系统打包了程序,就为所有系统打包了。
  • 灵活。在许多打包系统中,如何选择二进制包或源码包并不总是很明显。使用 pkgsrc,区别很明显,两种安装方法都一样容易,并且都可以为你解决依赖关系。

如何安装 pkgsrc

无论你使用的是 BSD、Linux、Illumos、Solaris 还是 MacOS,安装过程都基本相同:

  1. 使用 CVS 检出 pkgsrc 树
  2. 引导 pkgsrc 系统
  3. 安装软件包

使用 CVS 检出 pkgsrc 树

在 Git 和 Subversion 之前,就有了 CVS。要检出代码你无需了解 CVS 太多,如果你习惯 Git,那么可以将 检出 checkout 称为 克隆 clone 。当你用 CVS 检出 pkgsrc 时,你就下载了详细说明如何构建每个软件包的“ 配方 recipes ”。它有很多文件,但是它们都很小,因为你实际上并没有拉取每个包的源码,而只有按需构建时需要的构建基础架构和 Makefile。使用 CVS,你可以轻松地在新版本发布时更新 pkgsrc 检出。

pkgsrc 文档建议将其源码树放在 /usr 目录下,因此你必须使用 sudo(或成为 root)运行此命令:

$ cd /usr
$ sudo cvs -q -z2 -d [email protected]:/cvsroot checkout -r pkgsrc-2019Q3 -P pkgsrc

在我撰写本文时,最新版本是 2019Q3。请检查 pkgsrc.org 主页的新闻部分或 NetBSD文档,以确定最新版本。

引导 pkgsrc

pkgsrc 树复制到你的计算机后,你会看到一个充满构建脚本的 /usr/pkgsrc 目录。在使用之前,你必须引导 pkgsrc,以便你可以轻松地访问构建和安装软件所需的相关命令。

引导 pkgsrc 的方式取决于你所使用操作系统。

对于 NetBSD,你只需使用捆绑的引导器:

# cd pkgsrc/bootstrap
# ./bootstrap

在其他系统上,还有更好的方法,包括一些自定义功能,它是由 Joyent 提供的。要了解运行的确切命令,请访问 pkgsrc.joyent.com。比如,在 Linux(Fedora、Debian、Slackware 等)上:

$ curl -O https://pkgsrc.joyent.com/packages/Linux/el7/bootstrap/bootstrap-trunk-x86_64-20170127.tar.gz
$ BOOTSTRAP_SHA="eb0d6911489579ca893f67f8a528ecd02137d43a"

尽管路径暗示文件适用于 RHEL 7,但二进制文件往往与所有(最前沿的 Linux 发行版)兼容。如果你发现二进制文件与你的发行版不兼容,你可以选择从源码构建。

验证 SHA1 校验和:

$ echo "${BOOTSTRAP_SHA}" bootstrap-trunk*gz > check-shasum
sha1sum -c check-shasum

你还可以验证 PGP 签名:

$ curl -O https://pkgsrc.joyent.com/packages/Linux/el7/bootstrap/bootstrap-trunk-x86_64-20170127.tar.gz.asc
$ curl -sS https://pkgsrc.joyent.com/pgp/56AAACAF.asc | gpg --import
$ gpg --verify ${BOOTSTRAP_TAR}{.asc,}

当你确认你已有正确的引导套件,将其安装到 /usr/pkg

sudo tar -zxpf ${BOOTSTRAP_TAR} -C /

它为你提供了通常的 pkgsrc 命令。将这些位置添加到你的 PATH 环境变量中

$ echo "PATH=/usr/pkg/sbin:/usr/pkg/bin:$PATH" >> ~/.bashrc
$ echo "MANPATH=/usr/pkg/man:$MANPATH" >> ~/.bashrc

如果你宁愿使用 pkgsrc 而不依赖于 Joyent 的构建,那么只需运行 pkgsrc 源码树的引导脚本即可。在运行特定于系统的脚本之前,请先阅读 bootstrap 目录中相关 README 文件。

 title=

如何使用 pkgsrc 安装软件

使用 pkgsrc 安装预编译的二进制文件(就像使用 DNF 或 Apt 一样)是很容易的。二进制安装的命令是 pgkin,它有自己的专门网站 pkgin.net。对于任何用过 Linux 的人来说,这个过程应该感觉相当熟悉。

要搜索 tmux 包:

$ pkgin search tmux

要安装 tmux 包:

$ sudo pkgin install tmux

pkgin 命令的目的是模仿典型的 Linux 包管理器的行为,因此有选项可以列出可用的包、查找包提供的特定可执行文件,等等。

如何使用 pkgsrc 从源码构建

然而,pkgsrc 真正强大的地方是方便地从源码构建包。你在第一步中检出了所有 20000 多个构建脚本,你可以直接进入 pkgsrc 源码树来访问这些脚本。

例如,要从源码构建 tcsh,首先找到构建脚本:

$ find /usr/pkgsrc -type d -name "tcsh"
/usr/pkgsrc/shells/tcsh

接下来,进入源码目录:

$ cd /usr/pgksrc/shells/tcsh

构建脚本目录包含许多文件来帮助在你的系统上构建应用,但值得注意的是,这里面有包含了软件说明的 DESCR 文件,以及触发构建的 Makefile

$ ls
CVS    DESCR     Makefile
PLIST  distinfo  patches
$ cat DESCR
TCSH is an extended C-shell with many useful features like
filename completion, history editing, etc.
$

准备就绪后,构建并安装:

$ sudo bmake install

pkgsrc 系统使用 bmake 命令(在第一步检出 pkgsrc 后提供),因此请务必使用 bmake(而不是出于习惯使用 make)。

如果要为多个系统构建,那么你可以创建一个包,而不是立即安装:

$ cd /usr/pgksrc/shells/tcsh
$ sudo bmake package
[...]
=> Creating binary package in /usr/pkgsrc/packages/All/tcsh-X.Y.Z.tgz

pkgsrc 创建的包是标准的 tarball,但它可以方便地通过 pkg_add 安装:

$ sudo pkg_add /usr/pkgsrc/packages/All/tcsh-X.Y.Z.tgz
tcsh-X.Y.Z: adding /usr/pkg/bin/tcsh to /etc/shells
$ tcsh
localhost%

pkgsrc 的 pkgtools 集合提供 pkg_addpkg_infopkg_adminpkg_createpkg_delete 命令,来帮助管理你在系统上构建和维护软件包。

pkgsrc,易于管理

pkgsrc 系统提供了直接,容易上手的软件包管理方法。如果你正在寻找一个不妨碍你并且可以定制的包管理器,请在任何运行 Unix 或类 Unix 的系统上试试 pkgsrc。


via: https://opensource.com/article/19/11/pkgsrc-netbsd-linux

作者:Seth Kenlon 选题:lujun9972 译者:geekpi 校对:wxy

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

这个系列的第二篇,我们会学习字段,记录和一些非常有用的 Awk 变量。

Awk 有好几个变种:最早的 awk,是 1977 年 AT&T 贝尔实验室所创。它还有一些重构版本,例如 mawknawk。在大多数 Linux 发行版中能见到的,是 GNU awk,也叫 gawk。在大多数 Linux 发行版中,awkgawk 都是指向 GNU awk 的软链接。输入 awk,调用的是同一个命令。GNU awk 用户手册中,能看到 awkgawk 的全部历史。

这一系列的第一篇文章 介绍了 awk 命令的基本格式:

$ awk [选项] '模式 {动作}' 输入文件

awk 是一个命令,后面要接选项 (比如用 -F 来定义字段分隔符)。想让 awk 执行的部分需要写在两个单引号之间,至少在终端中需要这么做。在 awk 命令中,为了进一步强调你想要执行的部分,可以用 -e 选项来突出显示(但这不是必须的):

$ awk -F, -e '{print $2;}' colours.txt
yellow
blue
green
[...]

记录和字段

awk 将输入数据视为一系列记录,通常是按行分割的。换句话说,awk 将文本中的每一行视作一个记录。每一记录包含多个字段。一个字段由字段分隔符分隔开来,字段是记录的一部分。

默认情况下,awk 将各种空白符,如空格、制表符、换行符等视为分隔符。值得注意的是,在 awk 中,多个空格将被视为一个分隔符。所以下面这行文本有两个字段:

raspberry red

这行也是:

tuxedo                  black

其他分隔符,在程序中不是这么处理的。假设字段分隔符是逗号,如下所示的记录,就有三个字段。其中一个字段可能会是 0 个字节(假设这一字段中不包含隐藏字符)

a,,b

awk 程序

awk 命令的程序部分是由一系列规则组成的。通常来说,程序中每个规则占一行(尽管这不是必须的)。每个规则由一个模式,或一个或多个动作组成:

模式 { 动作 }

在一个规则中,你可以通过定义模式,来确定动作是否会在记录中执行。模式可以是简单的比较条件、正则表达式,甚至两者结合等等。

这个例子中,程序只会显示包含单词 “raspberry” 的记录:

$ awk '/raspberry/ { print $0 }' colours.txt
raspberry red 99

如果没有文本符合模式,该动作将会应用到所有记录上。

并且,在一条规则只包含模式时,相当于对整个记录执行 { print },全部打印出来。

Awk 程序本质上是数据驱动的,命令执行结果取决于数据。所以,与其他编程语言中的程序相比,它还是有些区别的。

NF 变量

每个字段都有指定变量,但针对字段和记录,也存在一些特殊变量。NF 变量,能存储 awk 在当前记录中找到的字段数量。其内容可在屏幕上显示,也可用于测试。下面例子中的数据,来自上篇文章文本

$ awk '{ print $0 " (" NF ")" }' colours.txt
name       color  amount (3)
apple      red    4 (3)
banana     yellow 6 (3)
[...]

awkprint 函数会接受一系列参数(可以是变量或者字符串),并将它们拼接起来。这就是为什么在这个例子里,每行结尾处,awk 会以一个被括号括起来的整数表示字段数量。

NR 变量

另外,除了统计每个记录中的字段数,awk 也统计输入记录数。记录数被存储在变量 NR 中,它的使用方法和其他变量没有任何区别。例如,为了在每一行开头显示行号:

$ awk '{ print NR ": " $0 }' colours.txt
1: name       color  amount
2: apple      red    4
3: banana     yellow 6
4: raspberry  red    3
5: grape      purple 10
[...]

注意,写这个命令时可以不在 print 后的多个参数间添加空格,尽管这样会降低可读性:

$ awk '{print NR": "$0}' colours.txt

printf() 函数

为了让输出结果时格式更灵活,你可以使用 awkprintf() 函数。 它与 C、Lua、Bash 和其他语言中的 printf 相类似。它也接受以逗号分隔的格式参数。参数列表需要写在括号里。

$ printf 格式, 项目1, 项目2, ...

格式这一参数(也叫格式符)定义了其他参数如何显示。这一功能是用格式修饰符实现的。%s 输出字符,%d 输出十进制数字。下面的 printf 语句,会在括号内显示字段数量:

$ awk 'printf "%s (%d)\n",$0,NF}' colours.txt
name       color  amount (3)
raspberry  red    4 (3)
banana     yellow 6 (3)
[...]

在这个例子里,%s (%d) 确定了每一行的输出格式,$0,NF 定义了插入 %s%d 位置的数据。注意,和 print 函数不同,在没有明确指令时,输出不会转到下一行。出现转义字符 \n 时才会换行。

Awk 脚本编程

这篇文章中出现的所有 awk 代码,都在 Bash 终端中执行过。面对更复杂的程序,将命令放在文件(脚本)中会更容易。-f FILE 选项(不要和 -F 弄混了,那个选项用于字段分隔符),可用于指明包含可执行程序的文件。

举个例子,下面是一个简单的 awk 脚本。创建一个名为 example1.awk 的文件,包含以下内容:

/^a/ {print "A: " $0}
/^b/ {print "B: " $0}

如果一个文件包含 awk 程序,那么在给文件命名时,最好写上 .awk 的扩展名。 这样命名不是强制的,但这么做,会给文件管理器、编辑器(和你)一个关于文件内容的很有用的提示。

执行这一脚本:

$ awk -f example1.awk colours.txt
A: raspberry  red    4
B: banana     yellow 6
A: apple      green  8

一个包含 awk 命令的文件,在最开头一行加上释伴 #!,就能变成可执行脚本。创建一个名为 example2.awk 的文件,包含以下内容:

#!/usr/bin/awk -f
#
# 除了第一行,在其他行前显示行号
#

NR > 1 {
    printf "%d: %s\n",NR,$0
}

可以说,脚本中只有一行,大多数情况下没什么用。但在某些情况下,执行一个脚本,比记住,然后打一条命令要容易的多。一个脚本文件,也提供了一个记录命令具体作用的好机会。以 # 号开头的行是注释,awk 会忽略它们。

给文件可执行权限:

$ chmod u+x example2.awk

执行脚本:

$ ./example2.awk colours.txt
2: apple      red    4
2: banana     yellow 6
4: raspberry red    3
5: grape      purple 10
[...]

awk 命令放在脚本文件中,有一个好处就是,修改和格式化输出会更容易。在终端中,如果能用一行执行多条 awk 命令,那么输入多行,才能达到同样效果,就显得有些多余了。

试一试

你现在已经足够了解,awk 是如何执行指令的了。现在你应该能编写复杂的 awk 程序了。试着编写一个 awk 脚本,它需要: 至少包括一个条件模式,以及多个规则。如果你想使用除 printprintf 以外的函数,可以参考在线 gawk 手册

下面这个例子是个很好的切入点:

#!/usr/bin/awk -f
#
#  显示所有记录 除了出现以下情况
#  如果第一个记录 包含 “raspberry”
#  将 “red” 替换成 “pi”

$1 == "raspberry" {
        gsub(/red/,"pi")
}

{ print }

试着执行这个脚本,看看输出是什么。接下来就看你自己的了。

这一系列的下一篇文章,将会介绍更多,能在更复杂(更有用!) 脚本中使用的函数。

这篇文章改编自 Hacker Public Radio 系列,一个技术社区博客。


via: https://opensource.com/article/19/11/fields-records-variables-awk

作者:Seth Kenlon 选题:lujun9972 译者:wenwensnow 校对:wxy

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

Oracle Linux 内核开发人员 Steve Sistare 参与了这场有关内核调度程序改进的讨论。

通过可扩展的任务窃取进行负载平衡

Linux 任务调度程序通过将唤醒的任务推送到空闲的 CPU,以及在 CPU 空闲时从繁忙的 CPU 中拉取任务来平衡整个系统的负载。在大型系统上的推送侧和拉取侧,有效的伸缩都是挑战。对于拉取,调度程序搜索连续的更大范围中的所有 CPU,直到找到过载的 CPU,然后从最繁忙的组中拉取任务。这代价非常昂贵,在大型系统上要花费 10 到 100 微秒,因此搜索时间受到平均空闲时间的限制,并且某些范围不会被搜索。并非总能达到平衡,而且闲置的 CPU 依旧闲置。

我实现了一种备用机制,该机制在 idle_balance() 中的现有搜索中自身受限并且没有找到之后被调用。我维护了一个过载的 CPU 的位图,当可运行的 CFS 任务计数超过 1 时,CPU 会设置该位。这个位图是稀疏的,每个高速缓存线的有效位数量有限。当许多线程同时设置、清除和访问元素时,这可以减少缓存争用。每个末级缓存都有一个位图。当 CPU 空闲时,它将搜索该位图以查找第一个具有可迁移任务的过载 CPU,然后将其窃取。这种简单的窃取会比单独的 idle_balance() 产生更高的 CPU 利用率,因为该搜索的成本很便宜,花费 1 到 2 微秒,因此每次 CPU 即将空闲时都可以调用它。窃取不会减轻全局最繁忙的队列的负担,但是它比根本不执行任何操作要好得多。

结果

偷窃仅在调度程序代码中占用少量 CPU 开销即可提高利用率。在以下实验中,以不同数量的组(每个组 40 个任务)运行 hackbench,并对每次运行结果显示 /proc/schedstat 中的增量(按 CPU 平均),并增加了这些非标准的统计信息:

  • %find:在旧函数和新函数中花费的时间百分比,这些函数用于搜索空闲的 CPU 和任务以窃取并设置过载的 CPU 位图。
  • steal:任务从另一个 CPU 窃取的次数。经过的时间增加了 8% 到 36%,最多增加了 0.4% 的发现时间。

 title=

​​如下图的绿色曲线所示,新内核的 CPU 繁忙利用率接近 100%,作为比较的基线内核是橙色曲线: ​​

根据负载的不同,窃取可将 Oracle 数据库 OLTP 性能提高多达 9%,并且我们已经看到 MySQL、Pgsql、gcc、Java 和网络方面有了一些不错的改进。通常,窃取对上下文切换率高的工作负载最有帮助。

代码

截至撰写本文时,这项工作尚未完成,但最新的修补程序系列位于 https://lkml.org/lkml/2018/12/6/1253。如果你的内核是使用 CONFIG_SCHED_DEBUG=y 构建的,则可以使用以下命令验证其是否包含窃取优化:

# grep -q STEAL /sys/kernel/debug/sched_features && echo Yes
Yes

如果要尝试使用,请注意,对于具有 2 个以上 NUMA 节点的系统,禁用了窃取功能,因为 hackbench 在此类系统上发生了回归,正如我在 https://lkml.org/lkml/2018/12/6/1250 中解释的那样。但是,我怀疑这种影响是特定于 hackbench 的,并且窃取将有助于多节点系统上的其他工作负载。要尝试使用它,请用内核参数 sched_steal_node_limit=8(或更大)重新启动。

进一步工作

在将基本盗用算法推向上游之后,我正在考虑以下增强功能:

  • 如果在末级缓存中进行窃取找不到候选者,在 LLC 和 NUMA 节点之间进行窃取。
  • 维护稀疏位图以标识 RT 调度类中的偷窃候选者。当前 pull_rt_task() 搜索所有运行队列。
  • idle_balance() 中删除核心和套接字级别,因为窃取会处理这些级别。当支持跨 LLC 窃取时,完全删除 idle_balance()
  • 维护位图以标识空闲核心和空闲 CPU,以实现推平衡。

这篇文章最初发布于 Oracle Developers Blog


via: https://www.linux.com/blog/can-better-task-stealing-make-linux-faster

作者:Oracle 选题:lujun9972 译者:wxy 校对:wxy

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

今天早上,我为未来潜在容器杂志画了一幅 OverlayFS 的漫画,我对这个主题感到兴奋,想写一篇关于它的博客来提供更多详细信息。

容器镜像很大

容器镜像可能会很大(尽管有些很小,例如 alpine linux 才 2.5MB)。Ubuntu 16.04 约为 27 MB,Anaconda Python 发行版为 800MB 至 1.5GB

你以镜像启动的每个容器都是原始空白状态,仿佛它只是为使用容器而复制的一份镜像拷贝一样。但是对于大的容器镜像,像 800MB 的 Anaconda 镜像,复制一份拷贝既浪费磁盘空间也很慢。因此 Docker 不会复制,而是采用叠加

叠加如何工作

OverlayFS,也被称为 联合文件系统联合挂载,它可让你使用 2 个目录挂载文件系统:“下层”目录和“上层”目录。

基本上:

  • 文件系统的下层目录是只读的
  • 文件系统的上层目录可以读写

当进程“读取”文件时,OverlayFS 文件系统驱动将在上层目录中查找并从该目录中读取文件(如果存在)。否则,它将在下层目录中查找。

当进程“写入”文件时,OverlayFS 会将其写入上层目录。

让我们使用 mount 制造一个叠加层!

这有点抽象,所以让我们制作一个 OverlayFS 并尝试一下!这将只包含一些文件:我将创建上、下层目录,以及用来挂载合并的文件系统的 merged 目录:

$ mkdir upper lower merged work
$ echo "I'm from lower!" > lower/in_lower.txt
$ echo "I'm from upper!" > upper/in_upper.txt
$ # `in_both` is in both directories
$ echo "I'm from lower!" > lower/in_both.txt
$ echo "I'm from upper!" > upper/in_both.txt

合并上层目录和下层目录非常容易:我们可以通过 mount 来完成!

$ sudo mount -t overlay overlay
    -o lowerdir=/home/bork/test/lower,upperdir=/home/bork/test/upper,workdir=/home/bork/test/work
    /home/bork/test/merged

在执行此操作时,我不断收到一条非常烦人的错误消息,内容为:mount: /home/bork/test/merged: special device overlay does not exist.。这条消息是错误的,实际上只是意味着我指定的一个目录缺失(我写成了 ~/test/merged,但它没有被展开)。

让我们尝试从 OverlayFS 中读取其中一个文件!文件 in_both.txt 同时存在于 lower/upper/ 中,因此应从 upper/ 目录中读取该文件。

$ cat merged/in_both.txt
"I'm from upper!

可以成功!

目录的内容就是我们所期望的:

find lower/ upper/ merged/
lower/
lower/in_lower.txt
lower/in_both.txt
upper/
upper/in_upper.txt
upper/in_both.txt
merged/
merged/in_lower.txt
merged/in_both.txt
merged/in_upper.txt

创建新文件时会发生什么?

$ echo 'new file' > merged/new_file
$ ls -l */new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 merged/new_file
-rw-r--r-- 1 bork bork 9 Nov 18 14:24 upper/new_file

这是有作用的,新文件会在 upper 目录创建。

删除文件时会发生什么?

读写似乎很简单。但是删除会发生什么?开始试试!

$ rm merged/in_both.txt

发生了什么?让我们用 ls 看下:

ls -l upper/in_both.txt  lower/lower1.txt  merged/lower1.txt
ls: cannot access 'merged/in_both.txt': No such file or directory
-rw-r--r-- 1 bork bork    6 Nov 18 14:09 lower/in_both.txt
c--------- 1 root root 0, 0 Nov 18 14:19 upper/in_both.txt

所以:

  • in_both.txt 仍在 lower 目录中,并且保持不变
  • 它不在 merged 目录中。到目前为止,这就是我们所期望的。
  • 但是在 upper 中发生的事情有点奇怪:有一个名为 upper/in_both.txt 的文件,但是它是字符设备?我想这就是 overlayfs 驱动表示删除的文件的方式。

如果我们尝试复制这个奇怪的字符设备文件,会发生什么?

$ sudo cp upper/in_both.txt upper/in_lower.txt
cp: cannot open 'upper/in_both.txt' for reading: No such device or address

好吧,这似乎很合理,复制这个奇怪的删除信号文件并没有任何意义。

你可以挂载多个“下层”目录

Docker 镜像通常由 25 个“层”组成。OverlayFS 支持具有多个下层目录,因此你可以运行:

mount -t overlay overlay
      -o lowerdir:/dir1:/dir2:/dir3:...:/dir25,upperdir=...

因此,我假设这是有多个 Docker 层的容器的工作方式,它只是将每个层解压缩到一个单独的目录中,然后要求 OverlayFS 将它们全部合并在一起,并使用一个空的上层目录,容器将对其进行更改。

Docker 也可以使用 btrfs 快照

现在,我使用的是 ext4,而 Docker 使用 OverlayFS 快照来运行容器。但是我曾经用过 btrfs,接着 Docker 将改为使用 btrfs 的写时复制快照。(这是 Docker 何时使用哪种存储驱动的列表)

以这种方式使用 btrfs 快照会产生一些有趣的结果:去年某个时候,我在笔记本上运行了数百个临时的 Docker 容器,这导致我用尽了 btrfs 元数据空间(像这个人一样)。这真的很令人困惑,因为我以前从未听说过 btrfs 元数据,而且弄清楚如何清理文件系统以便再次运行 Docker 容器非常棘手。(这个 docker github 上的提案描述了 Docker 和 btrfs 的类似问题)

以简单的方式尝试容器功能很有趣!

我认为容器通常看起来像是在做“复杂的”事情,我认为将它们分解成这样很有趣。你可以运行一条 mount 咒语,而实际上并没有做任何与容器相关的其他事情,看看叠加层是如何工作的!


via: https://jvns.ca/blog/2019/11/18/how-containers-work–overlayfs/

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

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

本文指导读者如何使用 Flutter 运行和部署第一个 Web 应用。

Flutter 在 Android 和 iOS 开发方面走了很长一段路之后,已经迈入了一个新的阶段,即 Web 开发。Google 发布了 Flutter 1.5,同时支持 Web 应用开发。

为 Web 开发配置 Flutter

为了使用 Web 包,输入命令 flutter upgrade 更新到 Flutter 1.5.4。

  • 打开终端
  • 输入 flutter upgrade
  • 输入 flutter –version 检查版本

图 1: 升级 Flutter 到最新版

也可以将 Android Studio 3.0 或更高版本用于 Flutter Web 开发,但在本教程中,我们使用 Visual Studio Code。

使用 Flutter Web 创建新项目

打开 Visual Studio Code,然后按 Shift+Ctrl+P 开始一个新项目。输入 flutter 并选择 “New Web Project”。

图 2:在 VSC 中开始一个新的 Flatter 项目

现在,为项目命名。我将其命名为 open_source_for_you

图 3: 给项目命名

在 VSC 中打开终端窗口,然后输入以下命令:

flutter packages pub global activate webdev
flutter packages upgrade

现在,使用以下命令在 localhost 上运行网站,IP 地址是 127.0.0.1。

flutter packages pub global run webdev serve

打开任何浏览器,然后输入 http://127.0.0.1:8080/

图 4:运行于 8080 端口的 Flutter 演示应用

在项目目录中有个 Web 文件夹,其中包含了 index.htmldart 文件被编译成 JavaScript 文件,并使用以下代码包含在 HTML 文件中:

<script defer src="main.dart.js" type="application/javascript"></script>

编码和修改演示页面

让我们创建一个简单的应用,它会在网页上打印 “Welcome to OSFY”。

现在打开 Dart 文件,它位于 lib 文件夹 main.dart(默认名)中(参见图 5)。

图 5:main.dart 文件的位置

现在,我们可以在 MaterialApp 的属性中删除调试标记,如下所示:

debugShowCheckedModeBanner: false

现在,向 Dart 中添加更多内容与用 Dart 编写 Flutter 很类似。为此,我们可以声明一个名为 MyClass 的类,它继承了 StatelessWidget

我们使用 Center 部件将元素定位到中心。我们还可以添加 Padding 部件来添加填充。使用以下代码获得图 5 所示的输出。使用刷新按钮查看更改。

class MyClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.all(20.0),
              child: Text(
                'Welcome to OSFY',
                style: TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

图 6:MyClass 的输出

让我们从互联网中添加一张图片,我已经从一个杂志网站选择了一张 “Open Source for You” 徽标。我们使用 Image.network

Image.network(
  'https://opensourceforu.com/wp-content/uploads/2014/03/OSFY-Logo.jpg',
  height: 100,
  width: 150
),

最终输出如图 7 所示。

图 7:最终输出


via: https://opensourceforu.com/2019/11/developing-a-simple-web-application-using/

作者:Jis Joe Mathew 选题:lujun9972 译者:geekpi 校对:wxy

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