2020年3月

Basilisk 是一个 Firefox 复刻,它支持旧版的扩展等更多功能。在这里,我们看一下它的功能并尝试一下。

Basilisk:基于 XUL 的开源 Web 浏览器

尽管最好使用 Linux 上的常规浏览器(如 Firefox 或 Chromium),但了解其他浏览器也没坏处。最近,我偶然发现了一个 Firefox 复刻:Basilisk 浏览器,它有经典的 Firefox 用户界面以及对旧版扩展的支持(就像 Waterfox 一样)。

itsfoss.com homepage on Basilisk

如果你迫切需要使用旧版扩展程序或怀念 Firefox 的经典外观,Basilisk 浏览器可以帮到你。这个浏览器是由 Pale Moon 浏览器背后的团队维护(这是我接下来要介绍的另一个 Firefox 复刻)。

如果你正在寻找开源 Chrome 替代品,那么你可以快速了解一下 Basilisk 提供的功能。

注意:Basilisk 是开发中软件。即使我在使用时没有遇到重大的可用性问题,但你也不应依赖它作为唯一使用的浏览器。

Basilisk 浏览器的特性

Basilisk 开箱即用。但是,在考虑使用之前,可能需要先看一下以下这些特性:

  • 基于 XUL 的 Web 浏览器
  • 它具有 “Australis” Firefox 界面,这在 v29–v56 的 Firefox 版本中非常流行。
  • 支持 NPAPI 插件(Flash、Unity、Java 等)
  • 支持 XUL/Overlay Mozilla 形式的扩展。
  • 使用 Goanna) 开源浏览器引擎,它是 Mozilla Gecko 的复刻
  • 不使用 Rust 或 Photon 用户界面
  • 仅支持 64 位系统

在 Linux 上安装 Basilisk

你可能没有在软件中心中找到它。因此,你必须前往其官方下载页面获得 tarball(tar.xz)文件。

下载后,只需将其解压缩并进入文件夹。接下来,你将在其中找到一个 Basilisk 可执行文件。你只需双击或右键单击并选择 “运行” 即可运行它。

你可以查看它的 GitHub 页面获取更多信息。

你也可以按照下面的步骤使用终端进入下载的文件夹,并运行文件:

cd basilisk-latest.linux64
cd basilisk
./basilisk

使用 Basilisk 浏览器

如果你想要支持旧版扩展,Basilisk 是不错的 Firefox 复刻。它是由 Pale Moon 背后的团队积极开发的,对于希望获得 Mozilla Firefox(在 Quantum 更新之前)经典外观,且不包括现代 Web 支持的用户而言,它可能是一个不错的选择。

浏览网页没有任何问题。但是,我注意到 YouTube 将其检测为过时的浏览器,并警告说它将很快停止支持它。

因此,我不确定 Basilisk 是否适合所有现有的 Web 服务 —— 但是,如果你确实需要使用 Firefox 较早版本中的扩展,那这是一个解决方案。

总结

你认为这个 Firefox 复刻值得尝试吗?你喜欢哪个?在下面的评论中分享你的想法。


via: https://itsfoss.com/basilisk-browser/

作者:Ankush Das 选题:lujun9972 译者:geekpi 校对:wxy

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

我将在本系列的第二篇中深入研究由多个文件组成的 C 程序的结构。

第一篇中,我设计了一个名为喵呜喵呜的多文件 C 程序,该程序实现了一个玩具编解码器。我也提到了程序设计中的 Unix 哲学,即在一开始创建多个空文件,并建立一个好的结构。最后,我创建了一个 Makefile 文件夹并阐述了它的作用。在本文中将另一个方向展开:现在我将介绍简单但具有指导性的喵呜喵呜编解码器的实现。

当读过我的《如何写一个好的 C 语言 main 函数》后,你会觉得喵呜喵呜编解码器的 main.c 文件的结构很熟悉,其主体结构如下:

/* main.c - 喵呜喵呜流式编解码器 */

/* 00 系统包含文件 */
/* 01 项目包含文件 */
/* 02 外部声明 */
/* 03 定义 */
/* 04 类型定义 */
/* 05 全局变量声明(不要用)*/
/* 06 附加的函数原型 */
   
int main(int argc, char *argv[])
{
  /* 07 变量声明 */
  /* 08 检查 argv[0] 以查看该程序是被如何调用的 */
  /* 09 处理来自用户的命令行选项 */
  /* 10 做点有用的事情 */
}
   
/* 11 其它辅助函数 */

包含项目头文件

位于第二部分中的 /* 01 项目包含文件 */ 的源代码如下:

/* main.c - 喵呜喵呜流式编解码器 */
...
/* 01 项目包含文件 */
#include "main.h"
#include "mmecode.h"
#include "mmdecode.h"

#include 是 C 语言的预处理命令,它会将该文件名的文件内容拷贝到当前文件中。如果程序员在头文件名称周围使用双引号(""),编译器将会在当前目录寻找该文件。如果文件被尖括号包围(<>),编译器将在一组预定义的目录中查找该文件。

main.h 文件中包含了 main.c 文件中用到的定义和类型定义。我喜欢尽可能多将声明放在头文件里,以便我在我的程序的其他位置使用这些定义。

头文件 mmencode.hmmdecode.h 几乎相同,因此我以 mmencode.h 为例来分析。

/* mmencode.h - 喵呜喵呜流编解码器 */
  
#ifndef _MMENCODE_H
#define _MMENCODE_H
  
#include <stdio.h>
  
int mm_encode(FILE *src, FILE *dst);
  
#endif /* _MMENCODE_H */

#ifdef#define#endif 指令统称为 “防护” 指令。其可以防止 C 编译器在一个文件中多次包含同一文件。如果编译器在一个文件中发现多个定义/原型/声明,它将会产生警告。因此这些防护措施是必要的。

在这些防护内部,只有两个东西:#include 指令和函数原型声明。我在这里包含了 stdio.h 头文件,以便于能在函数原型中使用 FILE 定义。函数原型也可以被包含在其他 C 文件中,以便于在文件的命名空间中创建它。你可以将每个文件视为一个独立的命名空间,其中的变量和函数不能被另一个文件中的函数或者变量使用。

编写头文件很复杂,并且在大型项目中很难管理它。不要忘记使用防护。

喵呜喵呜编码的最终实现

该程序的功能是按照字节进行 MeowMeow 字符串的编解码,事实上这是该项目中最简单的部分。截止目前我所做的工作便是支持允许在适当的位置调用此函数:解析命令行,确定要使用的操作,并打开将要操作的文件。下面的循环是编码的过程:

/* mmencode.c - 喵呜喵呜流式编解码器 */
...
   while (!feof(src)) {

     if (!fgets(buf, sizeof(buf), src))
       break;

     for(i=0; i<strlen(buf); i++) {
       lo = (buf[i] & 0x000f);
       hi = (buf[i] & 0x00f0) >> 4;
       fputs(tbl[hi], dst);
       fputs(tbl[lo], dst);
     }
   }

简单的说,当文件中还有数据块时( feof(3) ),该循环读取(feof(3) )文件中的一个数据块。然后将读入的内容的每个字节分成两个 hilo 半字节 nibble 。半字节是半个字节,即 4 个位。这里的奥妙之处在于可以用 4 个位来编码 16 个值。我将 hilo 用作 16 个字符串查找表 tbl 的索引,表中包含了用半字节编码的 MeowMeow 字符串。这些字符串使用 fputs(3) 函数写入目标 FILE 流,然后我们继续处理缓存区的下一个字节。

该表使用 table.h 中的宏定义进行初始化,在没有特殊原因(比如:要展示包含了另一个项目的本地头文件)时,我喜欢使用宏来进行初始化。我将在未来的文章中进一步探讨原因。

喵呜喵呜解码的实现

我承认在开始工作前花了一些时间。解码的循环与编码类似:读取 MeowMeow 字符串到缓冲区,将编码从字符串转换为字节

 /* mmdecode.c - 喵呜喵呜流式编解码器 */
 ...
 int mm_decode(FILE *src, FILE *dst)
 {
   if (!src || !dst) {
     errno = EINVAL;
     return -1;
   }
   return stupid_decode(src, dst);
 }

这不符合你的期望吗?

在这里,我通过外部公开的 mm_decode() 函数公开了 stupid_decode() 函数细节。我上面所说的“外部”是指在这个文件之外。因为 stupid_decode() 函数不在该头文件中,因此无法在其他文件中调用它。

当我们想发布一个可靠的公共接口时,有时候会这样做,但是我们还没有完全使用函数解决问题。在本例中,我编写了一个 I/O 密集型函数,该函数每次从源中读取 8 个字节,然后解码获得 1 个字节写入目标流中。较好的实现是一次处理多于 8 个字节的缓冲区。更好的实现还可以通过缓冲区输出字节,进而减少目标流中单字节的写入次数。

/* mmdecode.c - 喵呜喵呜流式编解码器 */
...
int stupid_decode(FILE *src, FILE *dst)
{
  char           buf[9];
  decoded_byte_t byte;
  int            i;
    
  while (!feof(src)) {
    if (!fgets(buf, sizeof(buf), src))
      break;
    byte.field.f0 = isupper(buf[0]);
    byte.field.f1 = isupper(buf[1]);
    byte.field.f2 = isupper(buf[2]);
    byte.field.f3 = isupper(buf[3]);
    byte.field.f4 = isupper(buf[4]);
    byte.field.f5 = isupper(buf[5]);
    byte.field.f6 = isupper(buf[6]);
    byte.field.f7 = isupper(buf[7]);
      
    fputc(byte.value, dst);
  }
  return 0;
}

我并没有使用编码器中使用的位移方法,而是创建了一个名为 decoded_byte_t 的自定义数据结构。

/* mmdecode.c - 喵呜喵呜流式编解码器 */
...

typedef struct {
  unsigned char f7:1;
  unsigned char f6:1;
  unsigned char f5:1;
  unsigned char f4:1;
  unsigned char f3:1;
  unsigned char f2:1;
  unsigned char f1:1;
  unsigned char f0:1;
} fields_t;
  
typedef union {
  fields_t      field;
  unsigned char value;
} decoded_byte_t;

初次看到代码时可能会感到有点儿复杂,但不要放弃。decoded_byte_t 被定义为 fields_tunsigned char联合。可以将联合中的命名成员看作同一内存区域的别名。在这种情况下,valuefield 指向相同的 8 位内存区域。将 field.f0 设置为 1 也将会设置 value 中的最低有效位。

虽然 unsigned char 并不神秘,但是对 fields_t 的类型定义(typedef)也许看起来有些陌生。现代 C 编译器允许程序员在结构体中指定单个位字段的值。字段所在的类型是一个无符号整数类型,并在成员标识符后紧跟一个冒号和一个整数,该整数指定了位字段的长度。

这种数据结构使得按字段名称访问字节中的每个位变得简单,并可以通过联合中的 value 字段访问组合后的值。我们依赖编译器生成正确的移位指令来访问字段,这可以在调试时为你节省不少时间。

最后,因为 stupid_decode() 函数一次仅从源 FILE 流中读取 8 个字节,所以它效率并不高。通常我们尝试最小化读写次数,以提高性能和降低调用系统调用的开销。请记住:少量的读取/写入大的块比大量的读取/写入小的块好得多。

总结

用 C 语言编写一个多文件程序需要程序员要比只是是一个 main.c 做更多的规划。但是当你添加功能或者重构时,只需要多花费一点儿努力便可以节省大量时间以及避免让你头痛的问题。

回顾一下,我更喜欢这样做:多个文件,每个文件仅有简单功能;通过头文件公开那些文件中的小部分功能;把数字常量和字符串常量保存在头文件中;使用 Makefile 而不是 Bash 脚本来自动化处理事务;使用 main() 函数来处理命令行参数解析并作为程序主要功能的框架。

我知道我只是蜻蜓点水般介绍了这个简单的程序,并且我很高兴知道哪些事情对你有所帮助,以及哪些主题需要详细的解释。请在评论中分享你的想法,让我知道。


via: https://opensource.com/article/19/7/structure-multi-file-c-part-2

作者:Erik O'Shaughnessy 选题:lujun9972 译者:萌新阿岩 校对:wxy

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

本文是 Python 之禅特别系列的第二篇,我们将要关注其中第三与第四条原则:简单与复杂。

Il semble que la perfection soit atteinte non quand il n'y a plus rien à ajouter, mais quand il n'y plus rien à retrancher.

It seems that perfection is finally attained not when there is no longer anything to add, but when there is no longer anything to take away.

“完美并非无可增,而是不可减。”

—Antoine de Saint-Exupéry, Terre des Hommes, 1939

编程时最常有的考量是与复杂性的斗争,只想写出让旁人无从下手的繁杂代码,对每个程序员来讲都算不上难事。倘若未能触及代码的简繁取舍,那么 《Python 之禅》 就有了一角残缺。

简单胜过复杂 Simple is better than complex

尚有选择余地时,应该选简单的方案。Python 少有不可为之事,这意味着设计出巴洛克风格(LCTT 译注:即夸张和不理性)的程序只为解决浅显的问题不仅有可能,甚至很简单。

正因如此,编程时应当谨记,代码的简单性是最易丢失,却最难复得的。

这意味着,在可以选用函数来表达时不要去引入额外的类;避免使用强力的第三方库往往有助于你针对迫切的问题场景设计更妥当的简短函数。不过其根本的意图,旨在让你减少对将来的盘算,而去着重解决手头的问题。

以简单和优美作为指导原则的代码相比那些想要囊括将来一切变数的,在日后要容易修改得多。

复杂胜过错综复杂 Complex is better than complicated

把握用词的精确含义对于理解这条令人费解的原则是至关重要的。形容某事 复杂 complex ,是说它由许多部分组成,着重组成成分之多;而形容某事 错综复杂 complicated ,则是指其包含着差异巨大、难以预料的行为,强调的是各组成部分之间的杂乱联系。

解决困难问题时,往往没有可行的简单方案。此时,最 Python 化的策略是“ 自底向上 bottom-up ”地构建出简单的工具,之后将其组合用以解决该问题。

这正是 对象组合 object composition 这类技术的闪耀之处,它避免了错综复杂的继承体系,转而由独立的对象把一些方法调用传递给别的独立对象。这些对象都能独立地测试与部署,最终却可以组成一体。

“自底建造” 的另一例即是 单分派泛函数 singledispatch 的使用,抛弃了错综复杂的对象之后,我们得到是简单、几乎无行为的对象以及独立的行为。


via: https://opensource.com/article/19/12/zen-python-simplicity-complexity

作者:Moshe Zadka 选题:lujun9972 译者:caiichenr 校对:wxy

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

域名系统 Domain Name System ,我们更通常称为 DNS 的系统,可以将域名翻译或转换为与该域关联的 IP 地址。DNS 是能够让你通过名称找到自己喜欢的网站而不是在浏览器中输入 IP 地址的原因。本指南将向你展示如何配置一个主 DNS 系统以及客户端。

以下是本文示例中使用的系统细节:

dns01.fedora.local    (192.168.1.160)- 主 DNS 服务器
client.fedora.local     (192.168.1.136)- 客户端

DNS 服务器配置

使用 sudo 安装 bind 包:

$ sudo dnf install bind bind-utils -y

bind 包提供了 /etc/named.conf 配置文件,来供你配置 DNS 服务器。

编辑 /etc/named.conf 文件:

sudo vi /etc/named.conf

查找以下行:

listen-on port 53 { 127.0.0.1; };

添加主 DNS 服务器的 IP 地址,如下所示:

listen-on port 53 { 127.0.0.1; 192.168.1.160; };

查找以下行:

allow-query  { localhost; };

添加本地网络范围。该示例系统使用的 IP 地址在 192.168.1.X 的范围内。指定如下:

allow-query  { localhost; 192.168.1.0/24; };

指定转发和反向区域。 区域文件 Zone file 就是具有系统上 DNS 信息(例如 IP 地址和主机名)的文本文件。 转发区域文件 forward zone file 使得将主机名转换为 IP 地址成为可能。 反向区域文件 reverse zone file 则相反。它允许远程系统将 IP 地址转换为主机名。

/etc/named.conf 文件的底部查找以下行:

include "/etc/named.rfc1912.zones";

在此处,你将在该行的正上方指定区域文件信息,如下所示:

zone "dns01.fedora.local" IN {
  type master;
  file "forward.fedora.local";
  allow-update { none; };
};

zone "1.168.192.in-addr.arpa" IN {
  type master;
  file "reverse.fedora.local";
  allow-update { none; };
};

forward.fedora.localreverse.fedora.local 文件是要创建的区域文件的名称。它们可以是任意名字。

保存并退出。

创建区域文件

创建你在 /etc/named.conf 文件中指定的转发和反向区域文件:

$ sudo vi /var/named/forward.fedora.local

添加以下行:

$TTL 86400
@   IN  SOA     dns01.fedora.local. root.fedora.local. (
        2011071001  ;Serial
        3600        ;Refresh
        1800        ;Retry
        604800      ;Expire
        86400       ;Minimum TTL
)
@       IN  NS          dns01.fedora.local.
@       IN  A           192.168.1.160
dns01           IN  A   192.168.1.160
client          IN  A   192.168.1.136

所有粗体(LCTT 译注:本译文中无法呈现粗体)内容都特定于你的环境。保存文件并退出。接下来,编辑 reverse.fedora.local 文件:

$ sudo vi /var/named/reverse.fedora.local

添加以下行:

$TTL 86400
@   IN  SOA     dns01.fedora.local. root.fedora.local. (
        2011071001  ;Serial
        3600        ;Refresh
        1800        ;Retry
        604800      ;Expire
        86400       ;Minimum TTL
)
@       IN  NS          dns01.fedora.local.
@       IN  PTR         fedora.local.
dns01           IN  A   192.168.1.160
client          IN  A   192.168.1.136
160     IN  PTR         dns01.fedora.local.
136     IN  PTR         client.fedora.local.

所有粗体(LCTT 译注:本译文中无法呈现粗体)内容都特定于你的环境。保存文件并退出。

你还需要配置 SELinux 并为配置文件添加正确的所有权。

sudo chgrp named -R /var/named
sudo chown -v root:named /etc/named.conf
sudo restorecon -rv /var/named
sudo restorecon /etc/named.conf

配置防火墙:

sudo firewall-cmd --add-service=dns --perm
sudo firewall-cmd --reload

检查配置是否存在语法错误

sudo named-checkconf /etc/named.conf

如果没有输出或返回错误,那么你的配置有效。

检查转发和反向区域文件。

$ sudo named-checkzone forward.fedora.local /var/named/forward.fedora.local

$ sudo named-checkzone reverse.fedora.local /var/named/reverse.fedora.local

你应该看到 “OK” 的响应:

zone forward.fedora.local/IN: loaded serial 2011071001
OK

zone reverse.fedora.local/IN: loaded serial 2011071001
OK

启用并启动 DNS 服务

$ sudo systemctl enable named
$ sudo systemctl start named

配置 resolv.conf 文件

编辑 /etc/resolv.conf 文件:

$ sudo vi /etc/resolv.conf

查找你当前的 nameserver 行。在示例系统上,使用调制解调器/路由器充当名称服务器,因此当前看起来像这样:

nameserver 192.168.1.1

这需要更改为主 DNS 服务器的 IP 地址:

nameserver 192.168.1.160

保存更改并退出。

不幸的是需要注意一点。如果系统重启或网络重启,那么 NetworkManager 会覆盖 /etc/resolv.conf 文件。这意味着你将丢失所做的所有更改。

为了防止这种情况发生,请将 /etc/resolv.conf 设为不可变:

$ sudo chattr +i /etc/resolv.conf

如果要重新设置,就需要允许其再次被覆盖:

$ sudo chattr -i /etc/resolv.conf

测试 DNS 服务器

$ dig fedoramagazine.org
; <<>> DiG 9.11.13-RedHat-9.11.13-2.fc30 <<>> fedoramagazine.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8391
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6

;; OPT PSEUDOSECTION:
 ; EDNS: version: 0, flags:; udp: 4096
 ; COOKIE: c7350d07f8efaa1286c670ab5e13482d600f82274871195a (good)
 ;; QUESTION SECTION:
 ;fedoramagazine.org.        IN  A

;; ANSWER SECTION:
 fedoramagazine.org.    50  IN  A   35.197.52.145

;; AUTHORITY SECTION:
 fedoramagazine.org.    86150   IN  NS  ns05.fedoraproject.org.
 fedoramagazine.org.    86150   IN  NS  ns02.fedoraproject.org.
 fedoramagazine.org.    86150   IN  NS  ns04.fedoraproject.org.

;; ADDITIONAL SECTION:
 ns02.fedoraproject.org.    86150   IN  A   152.19.134.139
 ns04.fedoraproject.org.    86150   IN  A   209.132.181.17
 ns05.fedoraproject.org.    86150   IN  A   85.236.55.10
 ns02.fedoraproject.org.    86150   IN  AAAA    2610:28:3090:3001:dead:beef:cafe:fed5
 ns05.fedoraproject.org.    86150   IN  AAAA    2001:4178:2:1269:dead:beef:cafe:fed5

 ;; Query time: 830 msec
 ;; SERVER: 192.168.1.160#53(192.168.1.160)
 ;; WHEN: Mon Jan 06 08:46:05 CST 2020
 ;; MSG SIZE  rcvd: 266

需要检查几件事以验证 DNS 服务器是否正常运行。显然,取得结果很重要,但这本身并不意味着 DNS 服务器实际上正常工作。

顶部的 QUERYANSWERAUTHORITY 字段应显示为非零,如我们的示例所示:

;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6

并且 SERVER 字段应有你的 DNS 服务器的 IP 地址:

;; SERVER: 192.168.1.160#53(192.168.1.160)

如果这是你第一次运行 dig 命令,请注意完成查询要花费 830 毫秒的时间:

;; Query time: 830 msec

如果再次运行它,查询将会更快:

$ dig fedoramagazine.org
;; Query time: 0 msec
;; SERVER: 192.168.1.160#53(192.168.1.160)

客户端配置

客户端配置将简单得多。

安装 bind 程序:

$ sudo dnf install bind-utils -y

编辑 /etc/resolv.conf 文件,并将主 DNS 配置为唯一的名称服务器:

$ sudo vi /etc/resolv.conf

它看起来像这样:

nameserver 192.168.1.160

保存更改并退出。然后,使 /etc/resolv.conf 文件不可变,防止其被覆盖并变回默认设置:

$ sudo chattr +i /etc/resolv.conf

测试客户端

你应该获得与 DNS 服务器相同的结果:

$ dig fedoramagazine.org
; <<>> DiG 9.11.13-RedHat-9.11.13-2.fc30 <<>> fedoramagazine.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8391
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6

;; OPT PSEUDOSECTION:
 ; EDNS: version: 0, flags:; udp: 4096
 ; COOKIE: c7350d07f8efaa1286c670ab5e13482d600f82274871195a (good)
 ;; QUESTION SECTION:
 ;fedoramagazine.org.        IN  A

;; ANSWER SECTION:
 fedoramagazine.org.    50  IN  A   35.197.52.145

;; AUTHORITY SECTION:
 fedoramagazine.org.    86150   IN  NS  ns05.fedoraproject.org.
 fedoramagazine.org.    86150   IN  NS  ns02.fedoraproject.org.
 fedoramagazine.org.    86150   IN  NS  ns04.fedoraproject.org.

;; ADDITIONAL SECTION:
 ns02.fedoraproject.org.    86150   IN  A   152.19.134.139
 ns04.fedoraproject.org.    86150   IN  A   209.132.181.17
 ns05.fedoraproject.org.    86150   IN  A   85.236.55.10
 ns02.fedoraproject.org.    86150   IN  AAAA    2610:28:3090:3001:dead:beef:cafe:fed5
 ns05.fedoraproject.org.    86150   IN  AAAA    2001:4178:2:1269:dead:beef:cafe:fed5

 ;; Query time: 1 msec
 ;; SERVER: 192.168.1.160#53(192.168.1.160)
 ;; WHEN: Mon Jan 06 08:46:05 CST 2020
 ;; MSG SIZE  rcvd: 266

确保 SERVER 输出的是你 DNS 服务器的 IP 地址。

你的 DNS 服务器设置完成了,现在所有来自客户端的请求都会经过你的 DNS 服务器了!


via: https://fedoramagazine.org/how-to-setup-a-dns-server-with-bind/

作者:Curt Warfield 选题:lujun9972 译者:geekpi 校对:wxy

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

我们开发的公益小程序“文章助手”增加了一个新特性:在呈现链接时,也提供链接的更多信息,比如该链接的大小、网速和部分内容摘要。这样有助于读者在访问链接前就对该链接有所了解。

之所以没有提供链接全文,主要考虑如下:

  • 全文展示需要付出更多的资源(包括页面内的所有素材)
  • 链接的内容可能不适合公开展示
  • 直接展示全部内容可能违背了微信相关的规定

关于“文章助手”,更多信息请参照这些介绍:

更多动态

谈谈“页面内容接入”

最近对我们的两个小程序配置了“页面内容接入”,并编写了主动推送的功能。

按微信官方的说法,可以“让微信更好的收录到小程序的页面信息,页面信息将可能被用于 搜索、扫条码 ,可帮助小程序获取更多流量。”

已经推送并开始收录的“Linux文章”是我们的官方网站的小程序,主要功能是提供站内文章的阅读和搜索。从目前推送和收录的情况看,从 3 月 9 号开始推送,基本上一天后全部推送完成(一万余个 URL),然后微信以一天最多 800 余篇的速度开始收录,到昨天,已经收录了 3000 余篇。

(上图中 3 月 12 号的推送收录数据可能数据出错了)

总体来看,收录情况还可以。不过我们另外一个提供 Linux 命令查询的小程序“Linux”在 3 月 13 日推送了 URL 之后,至今尚未开始接受推送数据。

此外,从这两天观察小程序的访问来源看,也没有看到来自搜一搜之类的来源的数据增高。从个人的使用体验上,也没注意过搜一搜有类似推荐行为。估计目前还在数据积累阶段,小程序的内容接入还没有实际发挥作用。

本文简短地对 Emacs 的调试工具 GUD 的特性进行了探索。

如果你是一个 C 或 C++ 开发者,你很可能已经使用过 GDB(GNU 调试器),毫无疑问,它是现今最强大、最无可匹敌的调试器之一。它唯一的缺点就是它基于命令行,虽然仍能提供许多强大的功能,但有时也会具有一些局限性。这也就是为什么聪明的人们开始追求整合了编辑器和调试器的 图形化 GUI 集成开发环境 Integrated Development Environment 。仍有些开发者坚信使用鼠标会降低工作效率,在 GUI 上用鼠标点~点~点~是来自恶魔的诱惑。

因为 Emacs 是现今最酷的文本编辑器之一,我将为你展示如何在不碰鼠标且不离开 Emacs 的情况下,实现写代码、编译代码、调试代码的过程。

GUD(LCTT 译注:全称 大统一调试器 Grand Unified Debugger ,鉴于其缩写形式更为人熟知,以下全文将使用缩写替代此全称)是 Emacs 下的一个 模式 mode ,用于在 Emacs 中运行 GDB。它向 GDB 提供了 Emacs 的所有特性,使用户无需离开编辑器就可以对代码进行调试。

使用 GUD 的前期准备

如果你正在使用一个 Linux 机器,很可能你已经安装了 GDB 和 gcc,接下来就是要确保已经安装了 Emacs。以下的内容我将假设读者熟悉 GDB 并且至少用它做过基本的调试。如果你未曾接触过 GDB,你可以做个快速入门,这些资料在网上随处可得。

对于那些 Emacs 新手,我将向你介绍一些基本术语。纵览整篇文章,你将看到诸如 C-c M-x 等快捷键。此处 C 代表 Ctrl 键,M 代表 Alt 键。C-c 代表 Ctrl 键和 c 键被同时按下。如果你看到 C-c c,它代表同时按下 Ctrl 键和 c 键,释放后紧接着按下 c 键。在 Emacs 中,编辑文本的主要区域被称为 主缓冲区 main buffer ,而在 Emacs 窗口下方用于输入命令的区域被称为 迷你缓冲区 mini buffer

启动 Emacs,并按下 C-x C-f 来创建一个新文件。Emacs 将提示你输入一个文件名,此处让我们将文件命名为 buggyFactorial.cpp。一旦文件打开,输入如下代码:

#include<iostream>
#include <assert.h>

int factorial(int num) {
  int product = 1;
  while(num--) {
    product *= num;
  }
  return product;
}
int main() {
  int result = factorial(5);
  assert(result == 120);
}

使用 C-x C-s 快捷键保存文件。文件保存完毕,是时候进行编译了。按下 M-x,在弹出的 提示符 prompt 后输入 compile 并点击回车。然后在提示符后,将内容替换为 g++ -g buggyFactorial.cpp 并再次点击回车。

图 1: Emacs 迷你缓冲区中编译命令

这将在 Emacs 中开启另一个缓冲区,显示编译的状态。如果你的代码输入没有错误,你将预期得到如图 2 所示的缓冲区。

图 2: 编译状态

要想隐藏编译缓冲区,首先确保你的光标在编译缓冲区中(你可以不使用鼠标,而是通过 C-x o 快捷键将光标从一个缓冲区移动到另一个),然后按下 C-x 0。下一步就是运行代码,并观察是否运行良好。按下 M-! 快捷键并在迷你缓冲区的提示符后面输入 ./a.out

图 3: 代码在迷你缓冲区中的输出

你可以看到迷你缓冲区中显示断言失败。很明显代码中有错误,因为 5 的阶乘是 120。那么让我们现在开始调试吧。

使用 GUD 调式代码

现在,我们的代码已经编译完成,是时候看看到底哪里出错了。按下 M-x 快捷键并在提示符后输入 gdb。在接下来的提示符后,输入 gdb -i=mi a.out。如果一切顺利,GDB 会在 Emacs 缓冲区中启动,你会看到如图 4 所示的窗口。

图 4: Emacs 中的 GDB 缓冲区

gdb 提示符后,输入 break main 来设置断点,并输入 r 来运行程序。程序会开始运行并停在 main() 函数处。

一旦 GDB 到达了 main 处设置的断点,就会弹出一个新的缓冲区显示你正在调试的代码。注意左侧的红点,正是你设置断点的位置,同时会有一个小的标志提示你当前代码运行到了哪一行。当前,该标志就在断点处(如图 5)。

图 5: GDB 与代码显示在两个分离的窗口

为了调试 factorial 函数,我们需要单步运行。想要达到此目的,你可以在 GBD 提示符使用 GDB 命令 step,或者使用 Emacs 快捷键 C-c C-s。还有其它一些快捷键,但我更喜欢 GDB 命令。因此我将在本文的后续部分使用它们。

单步运行时让我们注意一下局部变量中的阶乘值。参考图 6 来设置在 Emacs 帧中显示局部变量值。

图 6: 在 Emacs 中使用独立帧显示局部变量

在 GDB 提示符中进行单步运行并观察局部变量值的变化。在循环的第一次迭代中,我们发现了一个问题。此处乘法的结果应该是 5 而不是 4。

本文到这里也差不多结束了,读者可以自行探索发现 GUD 模式这片新大陆。GDB 中的所有命令都可以在 GUD 模式中运行。我将此代码的修复留给读者作为一个练习。看看你在调试的过程中,可以做哪一些定制化,来使你的工作流更加简单和高效。


via: https://opensourceforu.com/2019/09/debugging-in-emacs-the-grand-unified-debugger/

作者:Vineeth Kartha 选题:lujun9972 译者:cycoe 校对:wxy

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