分类 技术 下的文章

在刚刚结束的 openEuler Summit 2022 上,华为服务器 OS 首席架构师、openEuler 社区技术委员会委员熊伟为广大开发者介绍了 openEuler 在过去 3 年的成就和成果。而其中最值得关注的是,openEuler 在云、服务器、边缘计算、嵌入式等四大场景下的八个技术方向的创新。

用熊伟博士的话来说,“在过去的三年里,openEuler 深入行业和产业,用八个技术方向的纵深创新,引领全场景的产业领先”。接下来,作为一个一直关注开源操作系统的技术人,我分享一下我的理解。

可编程内核:系统调度的极限追求

首先介绍一下可编程内核。

我们知道,内核是一个操作系统的核心。对于我们所熟悉的 PC 和服务器,由于对资源的消耗和性能的需求没有那么的敏感,无需毫厘必争,所以往往不太需要在内核层面做删减,正常使用即可。删减内核功能的成本可能远高于增加内存的成本。

但对于资源消耗和成本更加敏感的边缘计算、嵌入式设备来说,每一丝的资源占有,都是难以估量的成本。对于资源集群量级极大的云计算场景而言,每 0.1% 的性能优化,对于整个集群而言,都是十分可观的收益。一个可编程的内核相比于模块化的内核,为生产环境下的优化提供了更多的可能性 —— 工程师可以根据业务的实际变化,随时调整内核的实现和策略,从而优化系统的性能。

在这种情况下,对于内核进行组装,支持可编程能力,就变得极其重要。openEuler 创新性地借鉴了社区当中 eBPF 的思想,并在此基础之上,将内核的机制和框架进行了分离,将框架内置到内核,实现的功能和策略则可以在开发完成后,注入到内核中即可实现对内核二次编程的效果。

说起来简单,但在具体落实层面,openEuler 做了不少的工作来达成这个目标:

可编程内核

openEuler 在系统内核层面,提供了编写用户态策略的基础库函数和可配置的调度策略模板,可以帮助用户快速理解内核的编程方式。支持用户快速编排和扩展,降低用户对内核实现调整时的成本,降低上手的门槛。在具体的管理机制方面,则提供了对任务 / 进程 / 组 / 用户等对象的自定义拓展标签,从而承载了用户态与内核态、内核态组件之间协同调度的语义,实现了整个内核的统一调度,确保力往一处使。此外,openEuler 还在内核层面提供了丰富的钩子点位和辅助函数,从而支持对 CFS 调度类的选核、选任务、抢占流程的自定义策略注入。

对于普通的开发人员来说,也可以基于 openEuler 提供的可编程框架,来实现针对不同的应用场景来开发自定义策略,动态加载到内核执行,提升内核的执行效率,为整个业务系统降本增效。

除了内核级别的资源调度,在 openEuler 操作系统中,还实现了两种不同层面的混合部署:在线/离线业务混部和软硬实时混部。从这个角度看,openEuler 志在实现更多更大场景的统一管理和运筹。我们先来看一下在线/离线业务混部:

在线/离线业务混部:资源利用率和系统性能的极限平衡追求

对于任何规模化的业务来说,必然会存在波峰和波谷。如何平衡不同业务之间分配的资源从而实现资源的最大化利用,是一个极为重要的课题。

在 openEuler 当中,你可以使用可编程的内核来完成内核级别的资源调度;而在更上一层,openEuler 提供了完整的在线/离线业务混部能力。openEuler 为开发者提供了三层不同的层次的资源调度能力

在内核层,基于内核优先级隔离调度技术,对于 CPU、内存、IO/NET 等资源维度实现干扰隔离,从根源上优化资源调度和隔离的能力。在用户态,为用户提供了基于 Rubik 的动态配置和拓扑编排能力,同时配合新一代 QoS 感知资源调度器 Skylark 所提供的资源调度能力,从而实现为不同的 QoS 要求的混部业务提供合适的资源调度。

在线/离线业务混部

Rubik 为开发者提供了基于应用画像的应用调度机制,来实现自动的资源调度能力。通过自动注入技术,来实现业务的自动画像和自动分析,得出不同业务负载对于资源的敏感度和压力度。再基于画像和标记,对各节点的资源进行调度(如 CPU、内存带宽、缓存带宽、磁盘带宽、网络带宽等)和数据收集,并基于历史数据二次调度资源,均衡各不同业务对于资源的平均利用水平。

在应用调度之上,Rubik 还会基于业务指标进行一定程度上的节点资源超卖。通过对业务资源维度的采样,预测可压缩资源的使用情况,从而实现基于预测情况的超卖。在为在线业务准确预留所需资源,保障其 QoS 的同时,将未使用资源尽可能多地分配给离线业务,最大化离线业务的吞吐率,提升节点的资源利用率。

通过引入 Rubik 在离线混部解决方案,在保证业务 SLA 不下降的情况下,资源利用率从业界平均的 15% 提升到 35%。

Rubik 混部解决方案

除了在线/离线业务混部之外,openEuler 还支持软硬件层面的实时混部:

软硬实时混部:多层次确定性时延需求满足

在 openEuler 当中,集成了一个新的硬实时内核 uniProton,帮助开发者磨平底层的硬件平台的差异,提供一套标准统一的操作系统平台。uniProton 支持任务管理、事件管理、队列管理、硬中断管理等管理方式,兼容 POSIX 标准接口的开发,降低了开发者的开发成本。

而对于开发者而言,更重要的是 uniProton 是鸿蒙系统和 openEuler 共同支持的内核。开发者可以开发一套应用,同时运行在 openEuler 计算设备和鸿蒙设备上,降低了开发者的开发成本。

对于需要硬实时方案的开发者来说,配合 uniProton 和 openEuler 的系统镜像裁剪能力,可以实现 KB 级的系统镜像调度时延 < 3us;而可以接受软实时方案的开发者则可以选择基于内核自旋锁和信号量优先级继承机制,配合周期性、Workqueue 延时、负载均衡等任务驱逐机制,实现 20us 中断响应。

通过软硬实时方案的混部能力,openEuler 实现了 CPU 、内存等全域资源的隔离分区,满足了数控机床、传统工业场景下对于多层次确定性时延的需求,帮助 openEuler 开发者可以进行传统工业场景的开发。

openEuler 除了和鸿蒙系统共建 uniProton,还提供了一个更有价值的特性 —— 多芯片架构的支持。

异构互联:泛架构算力存储统一调度

openEuler 全版本支持 x86、ARM、申威、龙芯、RISC-V 等五大架构,并支持英特尔、AMD、兆芯等多款 CPU 芯片,支持多个硬件厂商发布的多款整机型号、板卡型号,对于开发者来说,可以轻松完成多个不同型号的设备之间的互联和统一调度。

异构融合统一调度

配合分布式软总线,可以实现鸿蒙设备和 openEuler 系统设备之间的即插即用、高效传输。开发者可以无需关注设备的发现机制,借助分布式软总线提供的通信机制,快速完成设备的发现、组网、连接和传输能力。开发者可以通过使用分布式软总线提供的 API 实现设备间的高速通信,无需关心通信细节,进而实现业务平台的高效部署与运行能力。

SysMaster: 安全可靠机制可靠的服务管理系统

除了上面介绍的各种内核、硬件方面的技术突破以外,openEuler 还在开发者最熟悉的初始化系统上做了一些探索和改进。

SysMaster

我们过去熟悉的初始化系统(比如 sysVinit、systemd、upstart),大多是使用 C 写的,且往往因为设计复杂,功能大一统等有违 UNIX 传统思维的做法而广受诟病。openEuler 社区为社区提供了一个全新的、采用 Rust 编写的初始化系统 —— SysMaster。

和 systemd 相比,由于 SysMaster 采用 Rust 语言编写,原生地规避了内存泄漏问题,开发者无需担心内存泄漏导致的 1 号进程挂掉。而从零构建的 SysMaster,也摒弃了之前的初始化系统中存在问题,为开发者提供了新一代的初始化系统

SysMaster

相比于过去的初始化系统,SysMaster 提供了全新的架构设计,分为 SysMaster Core 和 SysMaster Extend 两类。SysMaster Core 提供了极度轻量的调度方式,占用更少的资源,以及更快的启动速度。拆分的架构则可以支持拓展多种服务类型,实现 1+1+N 的架构,满足初始化系统的多样化诉求。而它的生态兼容工具,则可以让开发者可以自由选择 systemd 和 SysMaster,无需担心被生态绑定。

总结

openEuler 的技术创新覆盖的场景相当地丰富和深入,以至于我无法在一篇文章中逐一分析和披露所有细分场景和技术创新点。在撰写这篇文章时,给我的最大感受是“他们居然实现了!

对于一个产业的研发人员来说,毫无疑问,openEuler 的这些技术创新,将帮助产业和行业的开发者节省大量的时间,用更短的时间完成应用的建设,将精力投放在产业和业务当中,产生价值。

从过去的“要打造根社区”,到如今的“成为根社区,并从根出发,进入纵深领域创新”,openEuler 给我了太多的惊喜。假以时日,我相信,openEuler 还给我带来更多的震撼。

了解 MySQL 如何存储和显示你的字符串变量,以便你能更好地控制你的数据。

字符串是你在 MySQL 中使用的最常见的数据类型之一。许多用户在他们的数据库中插入和读取字符串,而没有认真地了解过它们。本文旨在让你深入了解 MySQL 如何存储和显示你的字符串变量,以便你能更好地控制你的数据。

你可以把字符串分成两类:二进制和非二进制。你可能在大多数时候想到的是非二进制字符串。非二进制字符串有字符集和排序的不同。另一方面,二进制字符串存储诸如 MP3 文件或图像等东西。即使你在二进制字符串中存储了一个词,比如“歌曲”,它的存储方式也与非二进制字符串不同。

我将重点讨论非二进制字符串。MySQL 中的所有非二进制字符串都与字符集和排序相关。字符串的字符集控制哪些字符可以存储在字符串中,而它的排序方式控制当你显示字符串时如何排序。

字符集

要查看你系统中的字符集,请运行以下命令:

SHOW CHARACTER SET;

这个命令将输出四列数据,包括字符集:

  • 名称
  • 简要描述
  • 默认的排序方式
  • 字符集中每个字符的最大尺寸

MySQL 过去默认为 latin1 字符集,但自 8.0 版以来,默认为 utf8mb4。现在的默认排序方式是 utf8mb4_0900_ai_ciai 表示该排序对音调不敏感( á = a),而 ci 则指定它对大小写不敏感(a = A)。

不同的字符集将其字符存储在内存中不同大小的块中。例如,从上面的命令可以看出,存储在 utf8mb4 的字符被存储在 1 到 4 个字节大小的内存中。如果你想看看一个字符串是否包含多字节的字符,你可以使用 CHAR_LENGTH()LENGTH() 函数。CHAR_LENGTH() 显示一个字符串包含多少个字符,而 LENGTH() 显示一个字符串有多少个字节,根据字符集的不同,它可能与一个字符串的字符长度相同,也可能不相同。下面是一个例子:

SET @a = CONVERT('data' USING latin1);

SELECT LENGTH(@a), CHAR_LENGTH(@a);

+------------+-----------------+
| LENGTH(@a) | CHAR_LENGTH(@a) |
+------------+-----------------+
|     4      |       4         |
+------------+-----------------+

这个例子表明,latin1 字符集以单字节为单位存储字符。其他字符集,如 utf16,允许多字节的字符:

SET @b = CONVERT('data' USING utf16);

SELECT LENGTH(@b), CHAR_LENGTH(@b);

+------------+------------------+
| LENGTH(@b) | CHAR_LENGTH(@b)  |
+------------+------------------+
|       8    |        4         |
+------------+------------------+

排序

当你运行带有 ORDER BY 子句的 SQL 语句时,字符串排序方式将决定值的显示方式。你对排序方式的选择是由你选择的字符集决定的。当你运行上面的 SHOW CHARACTER SET 命令时,你看到了每个字符集的默认排序方式。你可以很容易地看到某个特定字符集的所有排序方式。例如,如果你想查看 utf8mb4 字符集允许哪些排序,请运行:

SHOW COLLATION LIKE 'utf8mb4%';

排序方式可以是不区分大小写的,也可以是区分大小写的,或者是二进制的。让我们建立一个简单的表,向其中插入一些值,然后用不同的排序方式查看数据,看看输出结果有什么不同:

CREATE TABLE sample (s CHAR(5));

INSERT INTO sample (s) VALUES 
 ('AAAAA'), ('ccccc'),  ('bbbbb'), ('BBBBB'), ('aaaaa'), ('CCCCC');

SELECT * FROM sample;

+-----------+
| s         |
+-----------+
| AAAAA     |
| ccccc     |
| bbbbb     |
| BBBBB     |
| aaaaa     |
| CCCCC     |
+-----------+

在不区分大小写的情况下,你的数据会按字母顺序返回,但不能保证大写的单词会排在小写的单词之前,如下图所示:

SELECT * FROM sample ORDER BY s COLLATE utf8mb4_turkish_ci;

+-----------+
| s         |
+-----------+
| AAAAA     |
| aaaaa     |
| bbbbb     |
| BBBBB     |
| ccccc     |
| CCCCC     |
+-----------+

另一方面,当 MySQL 运行大小写敏感的搜索时,每个字母的小写将排在大写之前:

SELECT * FROM sample ORDER BY s COLLATE utf8mb4_0900_as_cs;

+-----------+
| s         |
+-----------+
| aaaaa     |
| AAAAA     |
| bbbbb     |
| BBBBB     |
| ccccc     |
| CCCCC     |
+-----------+

而按二进制排序方式将返回所有大写的值,然后再返回小写的值:

SELECT * FROM sample ORDER BY s COLLATE utf8mb4_0900_bin;

+-----------+
| s         |
+-----------+
| AAAAA     |
| ccccc     |
| bbbbb     |
| BBBBB     |
| aaaaa     |
| CCCCC     |
+-----------+

如果你想知道一个字符串使用哪种字符集和排序,你可以使用被恰当命名的 charsetcollation 函数。运行 MySQL 8.0 或更高版本的服务器将默认使用 utf8mb4 字符集和 utf8mb4_0900_ai_ci 排序:

SELECT charset('data');

+-------------------+
| charset('data')   |
+-------------------+
| utf8mb4           |
+-------------------+

SELECT collation('data');

+--------------------+
| collation('data')  |
+--------------------+
| utf8mb4_0900_ai_ci |
+--------------------+

你可以使用 SET NAMES 命令来改变所使用的字符集或排序方式。

要从 utf8mb4 字符集改为 utf16,运行这个命令:

SET NAMES 'utf16';

如果你想选择默认以外的排序方式,你可以在 SET NAMES 命令中添加一个 COLLATE 子句。

例如,假设你的数据库存储西班牙语的单词。MySQL 的默认排序(utf8mb4_0900_ai_ci)将 chll 视为两个不同的字符,并将它们排序。但在西班牙语中,chll 是单独的字母,所以如果你想让它们按正确的顺序排序(分别排在 cl 之后),你需要使用不同的排序。一个选择是使用 utf8mb4_spanish2_ci 排序方式:

SET NAMES 'utf8mb4' COLLATE 'utf8mb4_spanish2_ci';

储存字符串

MySQL 允许你为你的字符串值选择不同的数据类型。(甚至比其他流行的数据库,如 PostgreSQL 和 MongoDB 更多。)

下面是 MySQL 的二进制字符串数据类型的列表、它们的非二进制对应物,以及它们的最大长度:

  • binarychar(255)
  • varbinaryvarchar(65,535)
  • tinyblobtinytext(255)
  • blobtext(65,535)
  • mediumblobmediumtext(16,777,215)
  • longbloblongtext(4,294,967,295)

要记住的一件重要事情是,与被存储在可变长度的字段中的 varbinaryvarchartextblob 类型不同(也就是说,只使用需要的空间),MySQL 将二进制(binary)和字符(char)类型存储在固定长度的字段。因此,像 char(20)binary(20) 这样的值将总是占用 20 个字节,即使你在其中存储了少于 20 个字符。对于二进制类型,MySQL用 ASCII NUL 值(0x00)填充这些值,对于 字符类型,用空格填充。

在选择数据类型时要考虑的另一件事是,你是否希望在字符串后面的空格被保留或剥离。在显示数据时,MySQL 会从以字符数据类型存储的数据中剥离空格,但不会剥离 varchar 的空格。

CREATE TABLE sample2 (s1 CHAR(10), s2 VARCHAR(10));

INSERT INTO sample2 (s1, s2) VALUES ('cat       ', 'cat       ');

SELECT s1, s2, CHAR_LENGTH(s1), CHAR_LENGTH(s2) FROM sample2;

+---------+---------+-----------------------------------+
| s1      | s2      | CHAR_LENGTH(s1) | CHAR_LENGTH(s2) |
+---------+---------+-----------------------------------+
| cat     | cat     |        3        |       10        |
+---------+---------+-----------------------------------+

总结

字符串是数据库中最常用的数据类型之一,而 MySQL 仍然是当今最流行的数据库系统之一。我希望你能从这篇文章中学到一些新的东西,并能用你的新知识来提高你的数据库技能。


via: https://opensource.com/article/23/1/strings-mysql

作者:Hunter Coleman 选题:lkxed 译者:wxy 校对:wxy

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

这是一篇快速而简单的指南,用 Oh My Zsh 和 Powerlevel10k 主题改造你的 Zsh 终端 Shell,使其在 Ubuntu 和其他 Linux 发行版中看起来很酷。

大多数 Linux 发行版中的默认 Shell 是 Bash。Bash 是一个可靠的和传统的工具。然而,它缺乏一些自定义功能,比如漂亮的颜色、光标支持等等。

你可以使用另一个 Shell,即 Zsh 来得到更多的设置调整,并帮助你扩展你的 Bash Shell 体验。

这个简单的指南解释了如何安装 Zsh、Oh My Zsh 并应用 Powerlevel10k 主题。

Oh My Zsh 和 Powerlevel10k 安装和配置指南

1、安装 Zsh 和改变 Shell

打开一个终端,使用以下适用于你的发行版的命令安装 Zsh。

Ubuntu、Debian、Linux Mint 和所有相关的发行版:

sudo apt install zsh

Fedora:

sudo dnf install zsh

Arch:

pacman -S zsh

安装完成后,找出 Zsh 的安装路径:

whereis zsh

然后使用当前用户的 Zsh 可执行路径改变 Shell。

chsh -s /usr/bin/zsh <用户名 >

改变当前用户的 Shell

关闭并再次打开终端。然后你应该看到 Zsh 的首次设置。选择选项 2。它将用一个默认的主题改变你的 Shell 提示符的外观,如下图所示:

Zsh 的首次设置

2、安装 Oh My Zsh

Oh My Zsh 是一套可以进一步定制 Zsh 的脚本。

首先,我们将从 GitHub 上下载 Oh My Zsh 脚本来安装它。如果你有 wgetgit 软件包,那就最好了。如果还没有安装,请使用以下命令 安装 wget & git:

sudo apt install wget
sudo apt install git

然后用下面的命令安装 Oh My Zsh:

sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

然后你应该看到 Oh My Zsh 及默认主题 Robbyrussell 应用到了你的终端。

安装 Oh My Zsh 和默认主题

Oh My Zsh 还附带了其他的主题,你可以 使用这篇指南 安装它们。然而,在本教程中,我将谈论一个特定的主题,即 Powerlevel10k。

3、为 Oh My Zsh 安装 Powerlevel10k 主题

打开终端,运行以下命令,从 GitHub 上克隆 Powerlevel10k 代码库,并将文件放到 Oh My Zsh 的配置文件夹中。

git clone https://github.com/romkatv/powerlevel10k.git $ZSH_CUSTOM/themes/powerlevel10k

用文本编辑器打开 ~/.zshrc 文件,将 ZSH_THEME 变量设为 "powerlevel10k/powerlevel10k"

cd ~
nano .zshrc

默认情况下,它应该是 Robbyrussell。删除 ”robbyrussell",添加下面的 "powerlevel10k/powerlevel10k"

更改后,你的 ~/.zshrc 文件应该是这样的:

ZSH_THEME="powerlevel10k/powerlevel10k”

保存并关闭该文件(CTRL+O、回车和 CTRL+X)。

改变 Oh My Zsh 主题为 Powerlevel10k

重新启动你的终端,启动首次向导来设置 Powerlevel10k 主题。

4、Powerleve10k 的首次设置

安装后启动终端时,Powerlevel10k 会提示你各种问题以了解你的 Linux 发行版设置。所以,根据你的需要按下键,按照你的口味来定制你的终端。下面是一些问题的例子截图,可以给你一些启发。

Powerlevel10k - wizard1

Powerlevel10k - wizard2

最后,你可以保存文件,享受你的终端的新面貌。

应用 Powerlevel10k Zsh 主题设置后

如果你想再次重启配置向导,运行以下程序。你可以随心所欲地做,次数不限。

p10k configure

基本设置就这样结束了。如果你想了解更多,请继续阅读。

更多配置(高级用法)

5、安装 Dracula GNOME 终端主题

如果你使用的是带有原生终端应用的 GNOME 桌面,你可以试试令人惊叹的 Drakula 主题。要做到这一点,打开一个终端,运行下面的命令来下载该主题:

git clone https://github.com/dracula/gnome-terminalcd gnome-terminal

打开 GNOME “终端”应用,进入偏好设置。通过点击 “+” 添加一个新的配置文件,并命名为 “drakula”。然后进入颜色标签,取消勾选 “ 使用系统主题的颜色 use colors from system theme ” 选项。

为终端创建一个新的配置文件

回到终端,运行以下程序。当出现提示时,选择你刚才创建的配置文件名称,如上所述。

./install.sh

为 GNOME “终端”应用 Drakula 主题

一旦安装完成,回到偏好设置中,将 Drakula 配置文件标记为默认。

6、Zsh 的自动补完和语法高亮

你可能想试试由社区开发的两个可用于 Zsh 的插件。它们是 zsh-autosuggestions 和 zsh-syntax-highlighting。

打开终端,运行以下程序,下载 zsh-autosuggestions,并将其放在插件文件夹中:

git clone https://github.com/zsh-users/zsh-autosuggestions.git $ZSH_CUSTOM/plugins/zsh-autosuggestions

同样地,为语法高亮插件运行以下程序:

git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $ZSH_CUSTOM/plugins/zsh-syntax-highlighting

通过文本编辑器打开 ~/.zshrc文件(使用以下命令),并找到 plugins=(git) 一行。并将其替换为以下内容:

nano ~/.zshrc
plugins=(git zsh-autosuggestions zsh-syntax-highlighting)

使用 CTRL+O、回车和 CTRL+X 保存并关闭该文件。

关闭并打开你的终端。现在,你应该可以使用自动建议和语法高亮了。

总结

这样就好了!你现在应该已经在你的系统上安装了 Oh My Zsh 和 Powerlevel10k 主题。你可以根据自己的需要,进一步定制 Powerlevel10k 主题的外观和行为。

干杯。


via: https://www.debugpoint.com/oh-my-zsh-powerlevel10k/

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

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

这里是一个关于理解 Linux 中 who 命令的初学者指南,并带有几个例子。

这篇文章是 Linux 命令学习系列的一部分。

who 命令

Linux 中的 who 命令用于显示当前登录到系统中的用户的信息。它显示用户的登录名,用户登录的终端,用户登录的时间,以及远程主机名(如果有)。

语法

下面是 who 命令的基本语法:

who [OPTION]... [ FILE | ARG1 ARG2 ]

各种 who 命令和开关的例子

默认情况下,who 读取文件 /var/run/utmp,其中包含当前登录的用户的信息。如果没有指定选项,它会显示每个用户的登录名、终端和登录时间。

who

它给出了以下输出。你可以看到它显示了登录名是 debugpoint,终端 ID tty2 和登录的日期和时间。

debugpoint tty2 2023-01-01 11:22 (tty2)

who 命令 - 默认示例

然而,如果你在虚拟机中运行上述命令,你应该看到同样的情况,但终端 ID 将是 x11 服务器的显示名称,即 :0

❯ who
debugpoint :0 2023-01-01 23:36 (:0)

要显示当前用户的用户名和信息,使用下面的方法:

whoami

使用 -b 选项查看最后一次系统启动时间:

❯ who -b
system boot 2023-01-01 23:36

显示当前系统中登录的用户数:

❯ who -q
debugpoint
users=1

所有上述命令与 -H 选项配对时,你会有一个更好的含标题行的信息,如下所示:

who -H

NAME LINE TIME COMMENT
debugpoint tty2 2023-01-01 11:22 (tty2)

如果你想在 Linux 中显示与 who 命令有关的所有信息,请使用选项 -a

who -aH

NAME LINE TIME IDLE PID COMMENT EXIT
system boot 2023-01-01 11:19
run-level 5 2023-01-01 11:19
debugpoint + tty2 2023-01-01 11:22 13:26 2042 (tty2)

像往常一样,你可以使用下面的重定向将 who 命令的输出保存到任何文件:

who > user_details.txt

who 命令选项的例子总结

下面是一些 who 命令的例子和它们的解释:

下面是一些可以与 who 命令一起使用的选项:

  • -a: 显示每个用户的主机名、登录时间和进程
  • -b: 显示上次系统启动的时间
  • -d: 显示死进程(已终止但未从 utmp 文件中删除的进程)
  • -H: 显示标题行
  • -l: 显示长格式的登录进程
  • -m: 只显示在 ARG1 ARG2 指定的终端上登录的用户的名字和行。
  • -q: 显示已登录用户的数量
  • -u: 显示拥有未脱离进程的用户的信息
  • -w: 显示已经登录的用户信息,格式与 utmp 文件相同

总结

我希望这篇文章能够帮助你了解 who 命令及其基本原理。你也可以阅读 who 手册页来了解更多。如果你有任何问题,请告诉我。


via: https://www.debugpoint.com/who-command-linux/

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

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

这是一个演示如何创建 POSIX 兼容的间隔定时器的教程。

 title=

对开发人员来说,定时某些事件是一项常见任务。定时器的常见场景是看门狗、任务的循环执行,或在特定时间安排事件。在这篇文章中,我将演示如何使用 timer\_create(...) 创建一个 POSIX 兼容的间隔定时器。

你可以从 GitHub 下载下面样例的源代码。

准备 Qt Creator

我使用 Qt Creator 作为该样例的 IDE。为了在 Qt Creator 运行和调试样例代码,请克隆 GitHub 上的仓库,打开 Qt Creator,在 “ 文件 File -> 打开文件或项目…… Open File or Project... ” 并选择 “CMakeLists.txt”:

Qt Creator open project

在 Qt Creator 中打开项目

选择工具链之后,点击 “ 配置项目 Configure Project ”。这个项目包括三个独立的样例(我们在这篇文章中将只会用到其中的两个)。使用绿色标记出来的菜单,可以在每个样例的配置之间切换,并为每个样例激活在终端运行 “ 在终端中运行 Run in terminal ”(用黄色标记)。当前用于构建和调试的活动示例可以通过左下角的“ 调试 Debug ” 按钮进行选择(参见下面的橙色标记)。

Project configuration

项目配置

线程定时器

让我们看看 simple_threading_timer.c 样例。这是最简单的一个。它展示了一个调用了超时函数 expired 的间隔定时器是如何被创建的。在每次过期时,都会创建一个新的线程,在其中调用函数 expired

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>

void expired(union sigval timer_data);

pid_t gettid(void);

struct t_eventData{
    int myData;
};

int main()
{
    int res = 0;
    timer_t timerId = 0;

    struct t_eventData eventData = { .myData = 0 };

    /*  sigevent 指定了过期时要执行的操作  */
    struct sigevent sev = { 0 };

    /* 指定启动延时时间和间隔时间 
    * it_value和it_interval 不能为零 */

    struct itimerspec its = {   .it_value.tv_sec  = 1,
                                .it_value.tv_nsec = 0,
                                .it_interval.tv_sec  = 1,
                                .it_interval.tv_nsec = 0
                            };

    printf("Simple Threading Timer - thread-id: %d\n", gettid());

    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = &amp;expired;
    sev.sigev_value.sival_ptr = &amp;eventData;

    /* 创建定时器 */
    res = timer_create(CLOCK_REALTIME, &amp;sev, &amp;timerId);

    if (res != 0){
        fprintf(stderr, "Error timer_create: %s\n", strerror(errno));
        exit(-1);
    }

    /* 启动定时器 */
    res = timer_settime(timerId, 0, &amp;its, NULL);

    if (res != 0){
        fprintf(stderr, "Error timer_settime: %s\n", strerror(errno));
        exit(-1);
    }

    printf("Press ETNER Key to Exit\n");
    while(getchar()!='\n'){}
    return 0;
}

void expired(union sigval timer_data){
    struct t_eventData *data = timer_data.sival_ptr;
    printf("Timer fired %d - thread-id: %d\n", ++data->myData, gettid());
}

这种方法的优点是在代码和简单调试方面用量小。缺点是由于到期时创建新线程而增加额外的开销,因此行为不太确定。

中断信号定时器

超时定时器通知的另一种可能性是基于 内核信号。内核不是在每次定时器过期时创建一个新线程,而是向进程发送一个信号,进程被中断,并调用相应的信号处理程序。

由于接收信号时的默认操作是终止进程(参考 signal 手册页),我们必须要提前设置好 Qt Creator,以便进行正确的调试。

当被调试对象接收到一个信号时,Qt Creator 的默认行为是:

  • 中断执行并切换到调试器上下文。
  • 显示一个弹出窗口,通知用户接收到信号。

这两种操作都不需要,因为信号的接收是我们应用程序的一部分。

Qt Creator 在后台使用 GDB。为了防止 GDB 在进程接收到信号时停止执行,进入 “ 工具 Tools -> 选项 Options ” 菜单,选择 “ 调试器 Debugger ”,并导航到 “ 本地变量和表达式 Locals & Expressions ”。添加下面的表达式到 “ 定制调试助手 Debugging Helper Customization ”:

handle SIG34 nostop pass

Signal no stop with error

Sig 34 时不停止

你可以在 GDB 文档 中找到更多关于 GDB 信号处理的信息。

接下来,当我们在信号处理程序中停止时,我们要抑制每次接收到信号时通知我们的弹出窗口:

Signal 34 pop up box

Signal 34 弹出窗口

为此,导航到 “GDB” 标签并取消勾选标记的复选框:

Timer signal windows

定时器信号窗口

现在你可以正确的调试 signal_interrupt_timer。真正的信号定时器的实施会更复杂一些:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#define UNUSED(x) (void)(x)

static void handler(int sig, siginfo_t *si, void *uc);
pid_t gettid(void);

struct t_eventData{
    int myData;
};

int main()
{
    int res = 0;
    timer_t timerId = 0;

    struct sigevent sev = { 0 };
    struct t_eventData eventData = { .myData = 0 };

    /* 指定收到信号时的操作 */
    struct sigaction sa = { 0 };

    /* 指定启动延时的时间和间隔时间 */
    struct itimerspec its = {   .it_value.tv_sec  = 1,
                                .it_value.tv_nsec = 0,
                                .it_interval.tv_sec  = 1,
                                .it_interval.tv_nsec = 0
                            };

    printf("Signal Interrupt Timer - thread-id: %d\n", gettid());

    sev.sigev_notify = SIGEV_SIGNAL; // Linux-specific
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_value.sival_ptr = &amp;eventData;

    /* 创建定时器 */
    res = timer_create(CLOCK_REALTIME, &amp;sev, &amp;timerId);

    if ( res != 0){
        fprintf(stderr, "Error timer_create: %s\n", strerror(errno));
        exit(-1);
    }

    /* 指定信号和处理程序 */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;

    /* 初始化信号 */
    sigemptyset(&amp;sa.sa_mask);

    printf("Establishing handler for signal %d\n", SIGRTMIN);

    /* 注册信号处理程序 */
    if (sigaction(SIGRTMIN, &amp;sa, NULL) == -1){
        fprintf(stderr, "Error sigaction: %s\n", strerror(errno));
        exit(-1);
    }

    /* 启动定时器 */
    res = timer_settime(timerId, 0, &amp;its, NULL);

    if ( res != 0){
        fprintf(stderr, "Error timer_settime: %s\n", strerror(errno));
        exit(-1);
    }

    printf("Press ENTER to Exit\n");
    while(getchar()!='\n'){}
    return 0;
}

static void
handler(int sig, siginfo_t *si, void *uc)
{
    UNUSED(sig);
    UNUSED(uc);
    struct t_eventData *data = (struct t_eventData *) si->_sifields._rt.si_sigval.sival_ptr;
    printf("Timer fired %d - thread-id: %d\n", ++data->myData, gettid());
}

与线程定时器相比,我们必须初始化信号并注册一个信号处理程序。这种方法性能更好,因为它不会导致创建额外的线程。因此,信号处理程序的执行也更加确定。缺点显然是正确调试需要额外的配置工作。

总结

本文中描述的两种方法都是接近内核的定时器的实现。不过,即使 timer\_create(...) 函数是 POSIX 规范的一部分,由于数据结构的细微差别,也不可能在 FreeBSD 系统上编译样例代码。除了这个缺点之外,这种实现还为通用计时应用程序提供了细粒度控制。


via: https://opensource.com/article/21/10/linux-timers

作者:Stephan Avenwedde 选题:lujun9972 译者:FigaroCao 校对:wxy

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

一个开源助手可以取代谷歌助理、Alexa 和 Siri?

An Open-Source Alternative to Google, Alexa, and Siri in Works for Home Assistant Platform

Home Assistant 是一个开源的智能家居平台,专注于为用户提供本地控制和隐私。它可以从树莓派或甚至本地服务器上运行。

他们还有一个订阅服务,可以获得额外的功能,如支持 Alexa 和谷歌助理,它由一家名为 “Nabu Casa” 的公司管理。

? 该公司由 Home Assistant 的创始人 Paulus Schoutsen 领导。

在上周的 博客 中,Paulus 宣布了一个新的开源项目,旨在提供一个没有主动互联网连接的语音助手,也无需任何其他大型科技公司的语音助手。

这是 一个对谷歌助理、Alexa 和 Siri 的开源挑战者? ?

让我们看看这到底是怎么回事。

它是什么? 这将是 Home Assistant 应用的一部分,将提供在本地运行语音命令的能力,以控制连接的智能设备。

Paulus 还断言,他们最重要的优先事项是支持不同的语言,他说:

人们需要能够用自己的语言说话,因为对于智能家居的语音助手来说,这是最容易接受和唯一可以接受的语言。

为了推动这一努力,Rhasspy 的创造者 Mike Hansen 已经被拉来实现这一目标。

对于那些不知道的人来说,Rhasspy 是另一个开源软件,专门提供一个由其用户社区支持的完全离线的语音助手。

如果你问我,我觉得 Home Assistant 的这个功能将由 Rhasspy 提供,这是一件好事。

为什么要重新发明已经存在的东西?最好是在它的基础上进行改进。

可以期待什么? 最初,这个语音助手做不到做你可能期待的事情。因此,像进行网络搜索、打电话、玩语音游戏等,都是不可能的。

它所关注的反而是语音助手应该有的基本功能。这样做是为了确保他们面前的工作是可控的。

他们的目标是从几个动作开始,然后围绕它们建立语言模型。

在目前的状态下,Home Assistant 在其用户界面上支持 62 种不同的语言。他们计划用他们的语音助手增加对所有这些语言的支持。

何时期待? 他们已经开始了这方面的工作,为每种语言建立一个 意图匹配句子集合

这意味着社区可以通过将智能设备的命令改编成各自的母语,来为语音助手的发展做出贡献。

他们的目标是在 2023 年的某个时候发布,并提到这将是“语音年”。

我认为一个可以离线工作的开源语音助手可以是一个非常有用的东西。它可以让你不受大科技公司的任何追踪。

? 还有一个额外的好处是,它的开发背后有一个庞大的社区,有什么理由不喜欢呢?


via: https://news.itsfoss.com/open-source-assistant/

作者:Sourav Rudra 选题:lkxed 译者:geekpi 校对:wxy

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