2021年6月

在 Linux 系统上管理系统资源的推荐工具是 cgroups。虽然在可以调整的限制方面(CPU、内存、磁盘 I/O、网络等)非常强大,但配置 cgroups 并不简单。nice) 命令从 1973 年起就可以使用了。但它只是调整在一个处理器上竞争时间的进程之间的调度优先级。nice 命令不会限制一个进程在单位时间内所能消耗的 CPU 周期的百分比。cpulimit 命令提供了两个世界的最佳方案。它限制了一个进程在每单位时间内可以分配的 CPU 周期的百分比,而且相对容易调用。

cpulimit 命令主要对长期运行的和 CPU 密集型的进程有用。编译软件和转换视频是长期运行的进程的常见例子,它们可以使计算机的 CPU 使用率达到最大。限制这类进程的 CPU 使用率将释放出处理器时间,供计算机上可能运行的其他任务使用。限制 CPU 密集型进程也将减少功耗及热输出,并可能减少系统的风扇噪音。限制一个进程的 CPU 使用率的代价是,它需要更多的时间来完成运行。

安装 cpulimit

cpulimit 命令在默认的 Fedora Linux 仓库中可用。运行下面的命令,在 Fedora Linux 系统上安装 cpulimit

$ sudo dnf install cpulimit

查看 cpulimit 的文档

cpulimit 软件包并没有附带的手册页。使用下面的命令来查看 cpulimit 的内置文档。输出结果在下面提供。但你可能需要在你自己的系统上运行该命令,以防止自本文编写以来选项发生变化。

$ cpulimit --help
Usage: cpulimit [OPTIONS…] TARGET
   OPTIONS
      -l, --limit=N percentage of cpu allowed from 0 to 800 (required)
      -v, --verbose show control statistics
      -z, --lazy exit if there is no target process, or if it dies
      -i, --include-children limit also the children processes
      -h, --help display this help and exit
   TARGET must be exactly one of these:
      -p, --pid=N pid of the process (implies -z)
      -e, --exe=FILE name of the executable program file or path name
      COMMAND [ARGS] run this command and limit it (implies -z)

演示

为了演示 cpulimit 命令的使用方式,下面提供了一个精心设计的、计算量很大的 Python 脚本。该脚本首先在没有限制的情况下运行,然后在限制为 50% 的情况下运行。它计算的是第 42 个 斐波那契数 的值。该脚本在这两种情况下都作为 time 命令的子进程运行,以显示计算答案所需的总时间。

$ /bin/time -f '(computed in %e seconds)' /bin/python -c 'f = lambda n: n if n<2 else f(n-1)+f(n-2); print(f(42), end=" ")'
267914296 (computed in 51.80 seconds)
$ /bin/cpulimit -i -l 50 /bin/time -f '(computed in %e seconds)' /bin/python -c 'f = lambda n: n if n<2 else f(n-1)+f(n-2); print(f(42), end=" ")'
267914296 (computed in 127.38 seconds)

当运行第一个版本的命令时,你可能会听到电脑上的 CPU 风扇转动起来。但在运行第二个版本时,你应该不会。第一个版本的命令不受 CPU 的限制,但它不应该导致你的电脑陷入瘫痪。它是以这样一种方式编写的:它最多只能使用一个 CPU 核心。大多数现代 PC 都有多个 CPU 核心,当其中一个 CPU 100% 繁忙时,可以毫无困难地同时运行其他任务。为了验证第一条命令是否使你的一个处理器达到最大,在一个单独的终端窗口中运行 top 命令并按下 1 键。要退出 top 命令可以按 Q 键。

设置高于 100% 的限制只对能够进行 任务并行化 的程序有意义。对于这样的程序,高于 100% 的增量代表一个 CPU 的全部利用率(200%=2 个CPU,300%=3 个CPU,等等)。

注意,在上面的例子中,-i 选项已经传递给 cpulimit 命令。这是必要的,因为要限制的命令不是 cpulimit 命令的直接子进程。相反,它是 time 命令的一个子进程,而后者又是 cpulimit 命令的一个子进程。如果没有 -i 选项,cpulimit 将只限制 time 命令。

最后说明

如果你想限制一个从桌面图标启动的图形程序,请将该程序的 .desktop 文件(通常位于 /usr/share/applications 目录下)复制到你的 ~/.local/share/applications 目录下,并相应修改 Exec 行。然后运行下面的命令来应用这些变化:

$ update-desktop-database ~/.local/share/applications

via: https://fedoramagazine.org/use-cpulimit-to-free-up-your-cpu/

作者:Gregory Bartholomew 选题:lujun9972 译者:geekpi 校对:wxy

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

国产的全功能 Linux 平板电脑 JingPad A1 众筹成功

对于动手能力强的个人用户来说,确实可以尝试在手机或平板电脑上安装“适当的” Linux 发行版。但当前正在 Indiegogo 上众筹的 JingPad Linux 平板,或许是一个更切实际的选择。它的众筹价格为 549 美元,首日就达成了超过五倍的众筹目标。

它采用的是之前发布的国产操作系统 JingOS。虽然有着类似苹果 iPadOS 的外观,但 JingOS 的底层还是基于开源的 Linux 桌面。得益于成熟的 Linux 桌面,用户能够轻松体验 LibreOffice、GIMP 等常用软件,以及流行的开发者 / 系统管理工具。

看起来不错,但是感觉价格并不便宜,希望我们可以尽早做个测评分享给大家。

OpenSSL 3.0 候选版本发布

经过了十几个 alpha 版本后,OpenSSL 3.0 今天发布了候选版本,它将在下季度发布正式版本。新的版本号跳过了 2.x,直接从 1.1.x 到了 3.0。OpenSSL 3.0 已经迁移到一个基于提供者的架构,以允许更大的灵活性。完全可插拔的 TLSv1.3组,新的编码器和解码器支持,完整的证书管理协议(CMP)实现,新的 API,以及对内核 TLS 的集成支持是 OpenSSL 3.0 的许多大变化之一。

另外 OpenSSL 3.0 现在是在 Apache 2.0 许可证下授权的,而之前是采用 “OpenSSL 许可证”和 “Original SSLeay 许可证”双许可证授权的。

在得到更多的关注和支持之后,OpenSSL 开始奋起直追,从公布的特性看,应该值得这个大版本跳跃。

联想 ThinkPad 发布驱动支持在 Linux 设置 BIOS

联想发布了一个新的 Think-LMI 驱动,即将进入 Linux 主线内核。该驱动允许在支持 WMI 接口的ThinkPad 及其它联想系统上查看和改变 BIOS/固件设置。支持读取/配置的 BIOS 选项通过 sysfs 的 /sys/class/firmware-attributes/* 暴露给用户空间。该驱动预计会进入 Linux 5.14。

通过 sysfs 公开 BIOS 设置并不是一个全新的概念。从去年开始,戴尔率先为 Linux 开展了固件属性的工作。

虽然不是开源的 BIOS 和固件,但是能在 Linux 内设置感觉是一种进步。

一旦你理解了一般原则,C++ 类成员函数指针不再那么令人生畏。

 title=

如果你正在寻找性能、复杂性或许多可能的解决方法来解决问题,那么在涉及到极端的情况下,C++ 总是一个很好的选择。当然,功能通常伴随着复杂性,但是一些 C++ 的特性几乎难以分辨。根据我的观点,C++ 的 类成员函数指针 也许是我接触过的最复杂的表达式,但是我会先从一些较简单的开始。

文章中的例子可以在我的 Github 仓库 里找到。

C 语言:函数指针

让我们先从一些基础开始:假设你有一个函数接收两个整数作为参数返回一个整数:

int sum(int a, int b) {
    return a+b;
}

在纯 C 语言中,你可以创建一个指向这个函数的指针,将其分配给你的 sum(...) 函数,通过解引用来调用它。函数的签名(参数、返回类型)必须符合指针的签名。除此之外,一个函数指针表现和普通的指针相同:

int (*funcPtrOne)(int, int);

funcPtrOne = &sum;

int resultOne = funcPtrOne(2, 5);

如果你使用指针作为参数并返回一个指针,这会显得很丑陋:

int *next(int *arrayOfInt){
    return ++arrayOfInt;
}

int *(*funcPtrTwo)(int *intPtr);

funcPtrTwo = &next;

int resultTwo = *funcPtrTwo(&array[0]);

C 语言中的函数指针存储着子程序的地址。

指向类成员函数的指针

让我们来进入 C++:好消息是你也许不需要使用类成员函数指针,除非在一个特别罕见的情况下,比如说接下来的例子。首先,你已经知道定义一个类和其中一个成员函数:

class MyClass
{
public:

    int sum(int a, int b) {
        return a+b;
    }

};

1、定义一个指针指向某一个类中一个成员函数

声明一个指针指向 MyClass 类成员函数。在此时,你并不知道想调用的具体函数。你仅仅声明了一个指向 MyClass 类中任意成员函数的指针。当然,签名(参数、返回值类型)需要匹配你接下想要调用的 sum(...) 函数:

int (MyClass::*methodPtrOne)(int, int);

2、赋值给一个具体的函数

为了和 C 语言(或者 静态成员函数#Static_method))对比,类成员函数指针不需要指向绝对地址。在 C++ 中,每一个类中都有一个虚拟函数表(vtable)用来储存每个成员函数的地址偏移量。一个类成员函数指针指向 vtable 中的某个条目,因此它也只存储偏移值。这样的原则使得 多态 变得可行。

因为 sum(...) 函数的签名和你的指针声明匹配,你可以赋值签名给它:

methodPtrOne = &MyClass::sum;

3、调用成员函数

如果你想使用指针调用一个类成员函,你必须提供一个类的实例:

MyClass clsInstance;
int result = (clsInstance.*methodPtrOne)(2,3);

你可以使用 . 操作符来访问,使用 * 对指针解引用,通过提供两个整数作为调用函数时的参数。这是丑陋的,对吧?但是你可以进一步应用。

在类内使用类成员函数指针

假设你正在创建一个带有后端和前端的 客户端/服务器 原理架构的应用程序。你现在并不需要关心后端,相反的,你将基于 C++ 类的前端。前端依赖于后端提供的数据完成初始化,所以你需要一个额外的初始化机制。同时,你希望通用地实现此机制,以便将来可以使用其他初始化函数(可能是动态的)来拓展你的前端。

首先定义一个数据类型用来存储初始化函数(init)的指针,同时描述何时应调用此函数的信息(ticks):

template<typename T>
struct DynamicInitCommand {
    void (T::*init)();     // 指向额外的初始化函数
    unsigned int ticks;    // 在 init() 调用后 ticks 的数量
};

下面一个 Frontend 类示例代码:

class  Frontend
{
public:

    Frontend(){
        DynamicInitCommand<Frontend> init1, init2, init3;

        init1 = { &Frontend::dynamicInit1, 5};
        init2 = { &Frontend::dynamicInit2, 10};
        init3 = { &Frontend::dynamicInit3, 15};

        m_dynamicInit.push_back(init1);
        m_dynamicInit.push_back(init2);
        m_dynamicInit.push_back(init3);
    }
   
    void  tick(){
        std::cout << "tick: " << ++m_ticks << std::endl;
       
        /* 检查延迟初始化 */
        std::vector<DynamicInitCommand<Frontend>>::iterator  it = m_dynamicInit.begin();

        while (it != m_dynamicInit.end()){
            if (it->ticks < m_ticks){
                 
                if(it->init)
                    ((*this).*(it->init))(); // 这里是具体调用

                it = m_dynamicInit.erase(it);

            } else {
                it++;
            }
        }
    }
   
    unsigned  int  m_ticks{0};
   
private:

    void  dynamicInit1(){
        std::cout << "dynamicInit1 called" << std::endl;
    };

    void  dynamicInit2(){
        std::cout << "dynamicInit2 called" << std::endl;
    }

    void  dynamicInit3(){
        std::cout << "dynamicInit3 called" << std::endl;
    }

    unsigned  int  m_initCnt{0};
    std::vector<DynamicInitCommand<Frontend> > m_dynamicInit;
};

Frontend 完成实例化后,tick() 函数会被后端以固定的时间时间调用。例如,你可以每 200 毫秒调用一次:

int  main(int  argc, char*  argv[]){
    Frontend frontendInstance;

    while(true){
        frontendInstance.tick(); // 仅用于模拟目的
        std::this_thread::sleep_for(std::chrono::milliseconds(200));
    }
}

Fronted 有三个额外的初始化函数,它们必须根据 m_ticks 的值来选择调用哪个。在 ticks 等于何值调用哪个初始化函数的信息存储在数组 m_dynamicInit 中。在构造函数(Frontend())中,将此信息附加到数组中,以便在 5、10 和 15 个 tick 后调用其他初始化函数。当后端调用 tick() 函数时,m_ticks 值会递增,同时遍历数组 m_dynamicInit 以检查是否必须调用初始化函数。

如果是这种情况,则必须通过引用 this 指针来取消引用成员函数指针:

((*this).*(it->init))()

总结

如果你并不熟悉类成员函数指针,它们可能会显得有些复杂。我做了很多尝试和经历了很多错误,花了一些时间来找到正确的语法。然而,一旦你理解了一般原理后,方法指针就变得不那么可怕了。

这是迄今为止我在 C++ 中发现的最复杂的语法。 你还知道更糟糕的吗? 在评论中发布你的观点!


via: https://opensource.com/article/21/2/ccc-method-pointers

作者:Stephan Avenwedde 选题:lujun9972 译者:萌新阿岩 校对:wxy

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

开发和实施云原生(容器优先)软件的检查清单。

 title=

许多年来,单体应用是实现业务需求的标准企业架构。但是,当云基础设施开始以规模和速度为业务加速,这种情况就发生了重大变化。应用架构也发生了转变,以适应云原生应用和 微服务无服务器 以及事件驱动的服务,这些服务运行在跨混合云和多云平台的不可变的基础设施上。

云原生与 Kubernetes 的联系

根据 云原生计算基金会 (CNCF) 的说法:

“云原生技术使企业能够在现代动态环境中建立和运行可扩展的应用,如公共云、私有云和混合云。容器、服务网格、微服务、不可变的基础设施和声明式 API 就是这种方法的典范。”

“这些技术使松散耦合的系统具有弹性、可管理和可观察性。与强大的自动化相结合,它们使工程师能够以最小的工作量频繁地、可预测地进行重要的改变。”

Kubernetes 这样的容器编排平台允许 DevOps 团队建立不可变的基础设施,以开发、部署和管理应用服务。现在,快速迭代的速度与业务需求相一致。构建容器以在 Kubernetes 中运行的开发人员需要一个有效的地方来完成。

云原生软件的要求

创建云原生应用架构需要哪些能力,开发人员将从中获得哪些好处?

虽然构建和架构云原生应用的方法有很多,但以下是一些需要考虑的部分:

  • 运行时: 它们更多是以容器优先或/和 Kubernetes 原生语言编写的,这意味着运行时会如 Java、Node.js、Go、Python 和 Ruby。
  • 安全: 在多云或混合云应用环境中部署和维护应用时,安全是最重要的,应该是环境的一部分。
  • 可观察性: 使用 Prometheus、Grafana 和 Kiali 等工具,这些工具可以通过提供实时指标和有关应用在云中的使用和行为的更多信息来增强可观察性。
  • 效率: 专注于极小的内存占用、更小的构件大小和快速启动时间,使应用可跨混合/多云平台移植。
  • 互操作性: 将云原生应用与能够满足上述要求的开源技术相结合,包括 Infinispan、MicroProfile、Hibernate、Kafka、Jaeger、Prometheus 等,以构建标准运行时架构。
  • DevOps/DevSecOps: 这些方法论是为持续部署到生产而设计的,与最小可行产品 (MVP) 一致,并将安全作为工具的一部分。

让云原生具体化

云原生似乎是一个抽象的术语,但回顾一下定义并像开发人员一样思考可以使其更加具体。为了使云原生应用获得成功,它们需要包括一长串定义明确的组成清单。

你是如何规划云原生应用的设计的?在评论中分享你的想法。


via: https://opensource.com/article/20/1/cloud-native-software

作者:Daniel Oh 选题:lujun9972 译者:geekpi 校对:wxy

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

荣耀发布 50 系列:看重鸿蒙,但我选安卓

在此前华为对外放出的鸿蒙 OS 2.0 适配机型中,荣耀独立前的旧机型将在今年第四季度适配;而在刚刚发布的荣耀 50 系列中搭载的仍然是基于 Android 的 Magic UI。荣耀 CEO 在采访中表示,荣耀会关注鸿蒙生态的发展,从消费者体验的角度采用最佳的产品和技术方案。这意味着至少在鸿蒙生态十分成熟之前,荣耀独立后的新机暂时仍不会摆脱 Android 生态。之前,荣耀曾表示其手机通过了 Google 的安全审查,荣耀设备可以选择预装 Google 移动服务(GMS)。

鸿蒙还需要更多手机厂商的支持,但这需要鸿蒙生态更加开放,真正让手机厂商们愿意也放心加入鸿蒙生态才行。

神舟十二号载人飞船发射任务取得圆满成功

6 月 17 日 9 时 22 分,时隔 5 年后中国载人飞船神舟十二号在长征 2F 运载火箭的托举下成功进入预定轨道,并完成太阳翼展开。神舟十二号载人飞船上天后将与“天和”核心舱进行交会对接,而“天和”核心舱也将迎来首批“入住人员”。3 名航天员将在轨驻留 3 个月,计划开展两次出舱活动及舱外作业。

这是中国载人航天史上由中国人书写的又一壮举,标志中国空间站建造任务再次向前迈出了一大步。

为中国航天取得巨大的成就点赞!也期待中国航天可以像 NASA 一样拥抱开源和利用开源来发展航天科技。

80% 支付赎金的企业会遭到二次勒索攻击

在遭遇勒索软件攻击之后,不少企业因为无法等待数据恢复、想要尽快恢复业务,选择向恶意行为者支付赎金。根据一份 Censuswide 的报告显示,大约 80% 选择支付赎金的组织会遭到第二次攻击,46% 的受害者会再次成为同一黑客的目标。

即使受害者支付了赎金以重新获得其加密文件的访问权,也经常出现问题。46% 的支付者发现一些数据被破坏;51% 的人重新获得了访问权,但没有数据损失;3% 的人根本没有拿回他们的数据。

不支付赎金吧,没法恢复;支付了吧,还有可能打水漂。可真难啊。

说到 基于终端的文本编辑器,通常 Vim、Emacs 和 Nano 受到了关注。

这并不意味着没有其他这样的文本编辑器。Vim 的现代增强版 Neovim,是许多这样的例子之一。

按照同样的思路,我想介绍另一个基于终端的文本编辑器,叫做 Helix Editor。

Helix,一个用 Rust 编写的现代文本编辑器

Helix 是用 Rust 编写的,使用 Tree-sitter 进行语法高亮。开发者声称,它比正则表达式高亮更快,因为 Tree-sitter 像编译器一样将代码解析成语法树,从而给出更多的代码结构信息。

你可以跟踪局部变量,计算缩进和操作选择来选择语法节点。它足够强大,即使有语法错误也能产生结果。

Helix 的主要亮点是“多重选择”,这是基于 Kakoune 的。

内置的语言服务器支持提供上下文感知补全、诊断和代码操作。

在 Linux 上安装 Helix

对于 Arch 和 Manjaro 用户来说,Helix 在 AUR 中有两个包:

  • helix-bin: 包含来自 GitHub 发布的预构建二进制文件
  • helix-git: 构建该仓库的主分支

作为一个 Arch 用户,我相信你可能已经知道 如何使用 AUR 安装应用

对于其他 Linux 发行版,你必须使用 Cargo。Cargo 是 Rust 软件包管理器。有了它,你可以安装 Rust 包。可以认为它相当于 Python 的 PIP。

你应该能够使用你的发行版的包管理器来安装 Cargo。在基于 Ubuntu 的发行版上,可以这样安装 Cargo:

sudo apt install cargo

接下来,你要克隆 Helix 仓库:

git clone --recurse-submodules --shallow-submodules -j8 https://github.com/helix-editor/helix

进入克隆的目录中:

cd helix

现在用 cargo 来安装 Helix:

cargo install --path helix-term --features "embed_runtime"

最后一步是将 hx 二进制文件添加到 PATH 变量中,这样你就可以从任何地方运行它。这应该被添加到你的 bashrc 或 bash 配置文件中。

export PATH=”$HOME/.cargo/bin:$PATH”

现在都设置好了,你应该可以通过在终端输入 hx 来使用编辑器。

你可以在 Helix 的文档页上找到使用 Helix 的键盘快捷键:

它与 Vim 或 Neovim 相比如何?我无法说。我可以用 Vim 进行基本的编辑,但我不是 Vim 忍者。如果你是一个信奉 Vim(或 Emacs)的人,请你试试 Helix 并自己判断。


via: https://itsfoss.com/helix-editor/

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

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