分类 技术 下的文章

这系列的第一篇通过创建一个简单的骰子游戏来探究 Python。现在是来从零制作你自己的游戏的时间。

在我的这系列的第一篇文章 中, 我已经讲解如何使用 Python 创建一个简单的、基于文本的骰子游戏。这次,我将展示如何使用 Python 模块 Pygame 来创建一个图形化游戏。它将需要几篇文章才能来得到一个确实做成一些东西的游戏,但是到这系列的结尾,你将更好地理解如何查找和学习新的 Python 模块和如何从其基础上构建一个应用程序。

在开始前,你必须安装 Pygame

安装新的 Python 模块

有几种方法来安装 Python 模块,但是最通用的两个是:

  • 从你的发行版的软件存储库
  • 使用 Python 的软件包管理器 pip

两个方法都工作的很好,并且每一个都有它自己的一套优势。如果你是在 Linux 或 BSD 上开发,可以利用你的发行版的软件存储库来自动和及时地更新。

然而,使用 Python 的内置软件包管理器可以给予你控制更新模块时间的能力。而且,它不是特定于操作系统的,这意味着,即使当你不是在你常用的开发机器上时,你也可以使用它。pip 的其它的优势是允许本地安装模块,如果你没有正在使用的计算机的管理权限,这是有用的。

使用 pip

如果 Python 和 Python3 都安装在你的系统上,你想使用的命令很可能是 pip3,它用来区分 Python 2.x 的 pip 的命令。如果你不确定,先尝试 pip3

pip 命令有些像大多数 Linux 软件包管理器一样工作。你可以使用 search 搜索 Python 模块,然后使用 install 安装它们。如果你没有你正在使用的计算机的管理权限来安装软件,你可以使用 --user 选项来仅仅安装模块到你的家目录。

$ pip3 search pygame
[...]
Pygame (1.9.3)                 - Python Game Development
sge-pygame (1.5)               - A 2-D game engine for Python
pygame_camera (0.1.1)          - A Camera lib for PyGame
pygame_cffi (0.2.1)            - A cffi-based SDL wrapper that copies the pygame API.
[...]
$ pip3 install Pygame --user

Pygame 是一个 Python 模块,这意味着它仅仅是一套可以使用在你的 Python 程序中的库。换句话说,它不是一个像 IDLENinja-IDE 一样可以让你启动的程序。

Pygame 新手入门

一个电子游戏需要一个背景设定:故事发生的地点。在 Python 中,有两种不同的方法来创建你的故事背景:

  • 设置一种背景颜色
  • 设置一张背景图片

你的背景仅是一张图片或一种颜色。你的电子游戏人物不能与在背景中的东西相互作用,因此,不要在后面放置一些太重要的东西。它仅仅是设置装饰。

设置你的 Pygame 脚本

要开始一个新的 Pygame 工程,先在计算机上创建一个文件夹。游戏的全部文件被放在这个目录中。在你的工程文件夹内部保持所需要的所有的文件来运行游戏是极其重要的。

一个 Python 脚本以文件类型、你的姓名,和你想使用的许可证开始。使用一个开放源码许可证,以便你的朋友可以改善你的游戏并与你一起分享他们的更改:

#!/usr/bin/env python3
# by Seth Kenlon

## GPLv3
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

然后,你告诉 Python 你想使用的模块。一些模块是常见的 Python 库,当然,你想包括一个你刚刚安装的 Pygame 模块。

import pygame  # 加载 pygame 关键字
import sys     # 让 python 使用你的文件系统
import os      # 帮助 python 识别你的操作系统

由于你将用这个脚本文件做很多工作,在文件中分成段落是有帮助的,以便你知道在哪里放代码。你可以使用块注释来做这些,这些注释仅在看你的源文件代码时是可见的。在你的代码中创建三个块。

'''
Objects
'''

# 在这里放置 Python 类和函数

'''
Setup
'''

# 在这里放置一次性的运行代码

'''
Main Loop
'''

# 在这里放置游戏的循环代码指令

接下来,为你的游戏设置窗口大小。注意,不是每一个人都有大计算机屏幕,所以,最好使用一个适合大多数人的计算机的屏幕大小。

这里有一个方法来切换全屏模式,很多现代电子游戏都会这样做,但是,由于你刚刚开始,简单起见仅设置一个大小即可。

'''
Setup
'''
worldx = 960
worldy = 720

在脚本中使用 Pygame 引擎前,你需要一些基本的设置。你必须设置帧频,启动它的内部时钟,然后开始 (init)Pygame 。

fps   = 40  # 帧频
ani   = 4   # 动画循环
clock = pygame.time.Clock()
pygame.init()

现在你可以设置你的背景。

设置背景

在你继续前,打开一个图形应用程序,为你的游戏世界创建一个背景。在你的工程目录中的 images 文件夹内部保存它为 stage.png

这里有一些你可以使用的自由图形应用程序。

  • Krita 是一个专业级绘图素材模拟器,它可以被用于创建漂亮的图片。如果你对创建电子游戏艺术作品非常感兴趣,你甚至可以购买一系列的游戏艺术作品教程
  • Pinta 是一个基本的,易于学习的绘图应用程序。
  • Inkscape 是一个矢量图形应用程序。使用它来绘制形状、线、样条曲线和贝塞尔曲线。

你的图像不必很复杂,你可以以后回去更改它。一旦有了它,在你文件的 Setup 部分添加这些代码:

world    = pygame.display.set_mode([worldx,worldy])
backdrop = pygame.image.load(os.path.join('images','stage.png').convert())
backdropbox = world.get_rect()

如果你仅仅用一种颜色来填充你的游戏的背景,你需要做的就是:

world = pygame.display.set_mode([worldx,worldy])

你也必须定义颜色以使用。在你的 Setup 部分,使用红、绿、蓝 (RGB) 的值来创建一些颜色的定义。

'''
Setup
'''

BLUE  = (25,25,200)
BLACK = (23,23,23 )
WHITE = (254,254,254)

至此,你理论上可以启动你的游戏了。问题是,它可能仅持续了一毫秒。

为证明这一点,保存你的文件为 your-name_game.py(用你真实的名称替换 your-name)。然后启动你的游戏。

如果你正在使用 IDLE,通过选择来自 “Run” 菜单的 “Run Module” 来运行你的游戏。

如果你正在使用 Ninja,在左侧按钮条中单击 “Run file” 按钮。

你也可以直接从一个 Unix 终端或一个 Windows 命令提示符中运行一个 Python 脚本。

$ python3 ./your-name_game.py

如果你正在使用 Windows,使用这命令:

py.exe your-name_game.py

启动它,不过不要期望很多,因为你的游戏现在仅仅持续几毫秒。你可以在下一部分中修复它。

循环

除非另有说明,一个 Python 脚本运行一次并仅一次。近来计算机的运行速度是非常快的,所以你的 Python 脚本运行时间会少于 1 秒钟。

为强制你的游戏来处于足够长的打开和活跃状态来让人看到它(更不要说玩它),使用一个 while 循环。为使你的游戏保存打开,你可以设置一个变量为一些值,然后告诉一个 while 循环只要变量保持未更改则一直保存循环。

这经常被称为一个“主循环”,你可以使用术语 main 作为你的变量。在你的 Setup 部分的任意位置添加代码:

main = True

在主循环期间,使用 Pygame 关键字来检查键盘上的按键是否已经被按下或释放。添加这些代码到你的主循环部分:

'''
Main loop
'''
while main == True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit(); sys.exit()
            main = False

        if event.type == pygame.KEYDOWN:
            if event.key == ord('q'):
                pygame.quit()
                sys.exit()
                main = False

也是在你的循环中,刷新你世界的背景。

如果你使用一个图片作为背景:

world.blit(backdrop, backdropbox)

如果你使用一种颜色作为背景:

world.fill(BLUE)

最后,告诉 Pygame 来重新刷新屏幕上的所有内容,并推进游戏的内部时钟。

    pygame.display.flip()
    clock.tick(fps)

保存你的文件,再次运行它来查看你曾经创建的最无趣的游戏。

退出游戏,在你的键盘上按 q 键。

在这系列的 下一篇文章 中,我将向你演示,如何加强你当前空空如也的游戏世界,所以,继续学习并创建一些将要使用的图形!


via: https://opensource.com/article/17/12/game-framework-python

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

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

笔记本电池是针对 Windows 操作系统进行了高度优化的,当我在笔记本电脑中使用 Windows 操作系统时,我已经意识到这一点,但对于 Linux 来说却不一样。

多年来,Linux 在电池优化方面取得了很大进步,但我们仍然需要做一些必要的事情来改善 Linux 中笔记本电脑的电池寿命。

当我考虑延长电池寿命时,我没有多少选择,但我觉得 TLP 对我来说是一个更好的解决方案,所以我会继续使用它。

在本教程中,我们将详细讨论 TLP 以延长电池寿命。

我们之前在我们的网站上写过三篇关于 Linux 笔记本电池节电工具 的文章:PowerTOP电池充电状态

TLP

TLP 是一款自由开源的高级电源管理工具,可在不进行任何配置更改的情况下延长电池寿命。

由于它的默认配置已针对电池寿命进行了优化,因此你可能只需要安装,然后就忘记它吧。

此外,它可以高度定制化,以满足你的特定要求。TLP 是一个具有自动后台任务的纯命令行工具。它不包含GUI。

TLP 适用于各种品牌的笔记本电脑。设置电池充电阈值仅适用于 IBM/Lenovo ThinkPad。

所有 TLP 设置都存储在 /etc/default/tlp 中。其默认配置提供了开箱即用的优化的节能设置。

以下 TLP 设置可用于自定义,如果需要,你可以相应地进行必要的更改。

TLP 功能

  • 内核笔记本电脑模式和脏缓冲区超时
  • 处理器频率调整,包括 “turbo boost”/“turbo core”
  • 限制最大/最小的 P 状态以控制 CPU 的功耗
  • HWP 能源性能提示
  • 用于多核/超线程的功率感知进程调度程序
  • 处理器性能与节能策略(x86_energy_perf_policy
  • 硬盘高级电源管理级别(APM)和降速超时(按磁盘)
  • AHCI 链路电源管理(ALPM)与设备黑名单
  • PCIe 活动状态电源管理(PCIe ASPM)
  • PCI(e) 总线设备的运行时电源管理
  • Radeon 图形电源管理(KMS 和 DPM)
  • Wifi 省电模式
  • 关闭驱动器托架中的光盘驱动器
  • 音频省电模式
  • I/O 调度程序(按磁盘)
  • USB 自动暂停,支持设备黑名单/白名单(输入设备自动排除)
  • 在系统启动和关闭时启用或禁用集成的 wifi、蓝牙或 wwan 设备
  • 在系统启动时恢复无线电设备状态(从之前的关机时的状态)
  • 无线电设备向导:在网络连接/断开和停靠/取消停靠时切换无线电
  • 禁用 LAN 唤醒
  • 挂起/休眠后恢复集成的 WWAN 和蓝牙状态
  • 英特尔处理器的动态电源降低 —— 需要内核和 PHC-Patch 支持
  • 电池充电阈值 —— 仅限 ThinkPad
  • 重新校准电池 —— 仅限 ThinkPad

如何在 Linux 上安装 TLP

TLP 包在大多数发行版官方存储库中都可用,因此,使用发行版的 包管理器 来安装它。

对于 Fedora 系统,使用 DNF 命令 安装 TLP。

$ sudo dnf install tlp tlp-rdw

ThinkPad 需要一些附加软件包。

$ sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm
$ sudo dnf install http://repo.linrunner.de/fedora/tlp/repos/releases/tlp-release.fc$(rpm -E %fedora).noarch.rpm
$ sudo dnf install akmod-tp_smapi akmod-acpi_call kernel-devel

安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。

$ sudo dnf install smartmontools

对于 Debian/Ubuntu 系统,使用 APT-GET 命令APT 命令 安装 TLP。

$ sudo apt install tlp tlp-rdw

ThinkPad 需要一些附加软件包。

$ sudo apt-get install tp-smapi-dkms acpi-call-dkms

安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。

$ sudo apt-get install smartmontools

当基于 Ubuntu 的系统的官方软件包过时时,请使用以下 PPA 存储库,该存储库提供最新版本。运行以下命令以使用 PPA 安装 TLP。

$ sudo add-apt-repository ppa:linrunner/tlp
$ sudo apt-get update
$ sudo apt-get install tlp

对于基于 Arch Linux 的系统,使用 Pacman 命令 安装 TLP。

$ sudo pacman -S tlp tlp-rdw

ThinkPad 需要一些附加软件包。

$ pacman -S tp_smapi acpi_call

安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。

$ sudo pacman -S smartmontools

对于基于 Arch Linux 的系统,在启动时启用 TLP 和 TLP-Sleep 服务。

$ sudo systemctl enable tlp.service
$ sudo systemctl enable tlp-sleep.service

对于基于 Arch Linux 的系统,你还应该屏蔽以下服务以避免冲突,并确保 TLP 的无线电设备切换选项的正确操作。

$ sudo systemctl mask systemd-rfkill.service
$ sudo systemctl mask systemd-rfkill.socket

对于 RHEL/CentOS 系统,使用 YUM 命令 安装 TLP。

$ sudo yum install tlp tlp-rdw

安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。

$ sudo yum install smartmontools

对于 openSUSE Leap 系统,使用 Zypper 命令 安装 TLP。

$ sudo zypper install TLP

安装 smartmontool 以显示 tlp-stat 中 S.M.A.R.T. 数据。

$ sudo zypper install smartmontools

成功安装 TLP 后,使用以下命令启动服务。

$ systemctl start tlp.service

使用方法

显示电池信息

$ sudo tlp-stat -b
或
$ sudo tlp-stat --battery
--- TLP 1.1 --------------------------------------------

+++ Battery Status
/sys/class/power_supply/BAT0/manufacturer                   = SMP
/sys/class/power_supply/BAT0/model_name                     = L14M4P23
/sys/class/power_supply/BAT0/cycle_count                    = (not supported)
/sys/class/power_supply/BAT0/energy_full_design             =  60000 [mWh]
/sys/class/power_supply/BAT0/energy_full                    =  48850 [mWh]
/sys/class/power_supply/BAT0/energy_now                     =  48850 [mWh]
/sys/class/power_supply/BAT0/power_now                      =      0 [mW]
/sys/class/power_supply/BAT0/status                         = Full

Charge                                                      =  100.0 [%]
Capacity                                                    =   81.4 [%]

显示磁盘信息

$ sudo tlp-stat -d
或
$ sudo tlp-stat --disk
--- TLP 1.1 --------------------------------------------

+++ Storage Devices
/dev/sda:
  Model     = WDC WD10SPCX-24HWST1                    
  Firmware  = 02.01A02
  APM Level = 128
  Status    = active/idle
  Scheduler = mq-deadline

  Runtime PM: control = on, autosuspend_delay = (not available)

  SMART info:
      4 Start_Stop_Count          =    18787 
      5 Reallocated_Sector_Ct     =        0 
      9 Power_On_Hours            =      606 [h]
     12 Power_Cycle_Count         =     1792 
    193 Load_Cycle_Count          =    25775 
    194 Temperature_Celsius       =       31    [°C]


+++ AHCI Link Power Management (ALPM)
/sys/class/scsi_host/host0/link_power_management_policy  = med_power_with_dipm
/sys/class/scsi_host/host1/link_power_management_policy  = med_power_with_dipm
/sys/class/scsi_host/host2/link_power_management_policy  = med_power_with_dipm
/sys/class/scsi_host/host3/link_power_management_policy  = med_power_with_dipm

+++ AHCI Host Controller Runtime Power Management
/sys/bus/pci/devices/0000:00:17.0/ata1/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata2/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata3/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata4/power/control = on

显示 PCI 设备信息

$ sudo tlp-stat -e
或
$ sudo tlp-stat --pcie
$ sudo tlp-stat -e
or
$ sudo tlp-stat --pcie

--- TLP 1.1 --------------------------------------------

+++ Runtime Power Management
Device blacklist = (not configured)
Driver blacklist = amdgpu nouveau nvidia radeon pcieport

/sys/bus/pci/devices/0000:00:00.0/power/control = auto (0x060000, Host bridge, skl_uncore)
/sys/bus/pci/devices/0000:00:01.0/power/control = auto (0x060400, PCI bridge, pcieport)
/sys/bus/pci/devices/0000:00:02.0/power/control = auto (0x030000, VGA compatible controller, i915)
/sys/bus/pci/devices/0000:00:14.0/power/control = auto (0x0c0330, USB controller, xhci_hcd)

......

显示图形卡信息

$ sudo tlp-stat -g
或
$ sudo tlp-stat --graphics
--- TLP 1.1 --------------------------------------------

+++ Intel Graphics
/sys/module/i915/parameters/enable_dc        = -1 (use per-chip default)
/sys/module/i915/parameters/enable_fbc       =  1 (enabled)
/sys/module/i915/parameters/enable_psr       =  0 (disabled)
/sys/module/i915/parameters/modeset          = -1 (use per-chip default)

显示处理器信息

$ sudo tlp-stat -p
或
$ sudo tlp-stat --processor
--- TLP 1.1 --------------------------------------------

+++ Processor
CPU model      = Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz

/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver    = intel_pstate
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor  = powersave
/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors = performance powersave
/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq  =   800000 [kHz]
/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq  =  3500000 [kHz]
/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference = balance_power
/sys/devices/system/cpu/cpu0/cpufreq/energy_performance_available_preferences = default performance balance_performance balance_power power 

......

/sys/devices/system/cpu/intel_pstate/min_perf_pct      =  22 [%]
/sys/devices/system/cpu/intel_pstate/max_perf_pct      = 100 [%]
/sys/devices/system/cpu/intel_pstate/no_turbo          =   0
/sys/devices/system/cpu/intel_pstate/turbo_pct         =  33 [%]
/sys/devices/system/cpu/intel_pstate/num_pstates       =  28

x86_energy_perf_policy: program not installed.

/sys/module/workqueue/parameters/power_efficient       = Y
/proc/sys/kernel/nmi_watchdog                          = 0

+++ Undervolting
PHC kernel not available.

显示系统数据信息

$ sudo tlp-stat -s
或
$ sudo tlp-stat --system
--- TLP 1.1 --------------------------------------------

+++ System Info
System         = LENOVO Lenovo ideapad Y700-15ISK 80NV
BIOS           = CDCN35WW
Release        = "Manjaro Linux"
Kernel         = 4.19.6-1-MANJARO #1 SMP PREEMPT Sat Dec 1 12:21:26 UTC 2018 x86_64
/proc/cmdline  = BOOT_IMAGE=/boot/vmlinuz-4.19-x86_64 root=UUID=69d9dd18-36be-4631-9ebb-78f05fe3217f rw quiet resume=UUID=a2092b92-af29-4760-8e68-7a201922573b
Init system    = systemd 
Boot mode      = BIOS (CSM, Legacy)

+++ TLP Status
State          = enabled
Last run       = 11:04:00  IST,    596 sec(s) ago
Mode           = battery
Power source   = battery

显示温度和风扇速度信息

$ sudo tlp-stat -t
或
$ sudo tlp-stat --temp
--- TLP 1.1 --------------------------------------------

+++ Temperatures
CPU temp               =    36 [°C]
Fan speed              = (not available)

显示 USB 设备数据信息

$ sudo tlp-stat -u
或
$ sudo tlp-stat --usb
--- TLP 1.1 --------------------------------------------

+++ USB
Autosuspend         = disabled
Device whitelist    = (not configured)
Device blacklist    = (not configured)
Bluetooth blacklist = disabled
Phone blacklist     = disabled
WWAN blacklist      = enabled

Bus 002 Device 001 ID 1d6b:0003 control = auto, autosuspend_delay_ms =     0 -- Linux Foundation 3.0 root hub (hub)
Bus 001 Device 003 ID 174f:14e8 control = auto, autosuspend_delay_ms =  2000 -- Syntek  (uvcvideo)

......

显示警告信息

$ sudo tlp-stat -w
或
$ sudo tlp-stat --warn
--- TLP 1.1 --------------------------------------------

No warnings detected.

状态报告及配置和所有活动的设置

$ sudo tlp-stat
--- TLP 1.1 --------------------------------------------

+++ Configured Settings: /etc/default/tlp
TLP_ENABLE=1
TLP_DEFAULT_MODE=AC
TLP_PERSISTENT_DEFAULT=0
DISK_IDLE_SECS_ON_AC=0
DISK_IDLE_SECS_ON_BAT=2
MAX_LOST_WORK_SECS_ON_AC=15
MAX_LOST_WORK_SECS_ON_BAT=60

......

+++ System Info
System = LENOVO Lenovo ideapad Y700-15ISK 80NV
BIOS = CDCN35WW
Release = "Manjaro Linux"
Kernel = 4.19.6-1-MANJARO #1 SMP PREEMPT Sat Dec 1 12:21:26 UTC 2018 x86_64
/proc/cmdline = BOOT_IMAGE=/boot/vmlinuz-4.19-x86_64 root=UUID=69d9dd18-36be-4631-9ebb-78f05fe3217f rw quiet resume=UUID=a2092b92-af29-4760-8e68-7a201922573b
Init system = systemd
Boot mode = BIOS (CSM, Legacy)

+++ TLP Status
State = enabled
Last run = 11:04:00 IST, 684 sec(s) ago
Mode = battery
Power source = battery

+++ Processor
CPU model = Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz

/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver = intel_pstate
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor = powersave
/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors = performance powersave

......

/sys/devices/system/cpu/intel_pstate/min_perf_pct = 22 [%]
/sys/devices/system/cpu/intel_pstate/max_perf_pct = 100 [%]
/sys/devices/system/cpu/intel_pstate/no_turbo = 0
/sys/devices/system/cpu/intel_pstate/turbo_pct = 33 [%]
/sys/devices/system/cpu/intel_pstate/num_pstates = 28

x86_energy_perf_policy: program not installed.

/sys/module/workqueue/parameters/power_efficient = Y
/proc/sys/kernel/nmi_watchdog = 0

+++ Undervolting
PHC kernel not available.

+++ Temperatures
CPU temp = 42 [°C]
Fan speed = (not available)

+++ File System
/proc/sys/vm/laptop_mode = 2
/proc/sys/vm/dirty_writeback_centisecs = 6000
/proc/sys/vm/dirty_expire_centisecs = 6000
/proc/sys/vm/dirty_ratio = 20
/proc/sys/vm/dirty_background_ratio = 10

+++ Storage Devices
/dev/sda:
 Model = WDC WD10SPCX-24HWST1
 Firmware = 02.01A02
 APM Level = 128
 Status = active/idle
 Scheduler = mq-deadline

 Runtime PM: control = on, autosuspend_delay = (not available)

 SMART info:
 4 Start_Stop_Count = 18787
 5 Reallocated_Sector_Ct = 0
 9 Power_On_Hours = 606 [h]
 12 Power_Cycle_Count = 1792
 193 Load_Cycle_Count = 25777
 194 Temperature_Celsius = 31 [°C]


+++ AHCI Link Power Management (ALPM)
/sys/class/scsi_host/host0/link_power_management_policy = med_power_with_dipm
/sys/class/scsi_host/host1/link_power_management_policy = med_power_with_dipm
/sys/class/scsi_host/host2/link_power_management_policy = med_power_with_dipm
/sys/class/scsi_host/host3/link_power_management_policy = med_power_with_dipm

+++ AHCI Host Controller Runtime Power Management
/sys/bus/pci/devices/0000:00:17.0/ata1/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata2/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata3/power/control = on
/sys/bus/pci/devices/0000:00:17.0/ata4/power/control = on

+++ PCIe Active State Power Management
/sys/module/pcie_aspm/parameters/policy = powersave

+++ Intel Graphics
/sys/module/i915/parameters/enable_dc = -1 (use per-chip default)
/sys/module/i915/parameters/enable_fbc = 1 (enabled)
/sys/module/i915/parameters/enable_psr = 0 (disabled)
/sys/module/i915/parameters/modeset = -1 (use per-chip default)

+++ Wireless
bluetooth = on
wifi = on
wwan = none (no device)

hci0(btusb) : bluetooth, not connected
wlp8s0(iwlwifi) : wifi, connected, power management = on

+++ Audio
/sys/module/snd_hda_intel/parameters/power_save = 1
/sys/module/snd_hda_intel/parameters/power_save_controller = Y

+++ Runtime Power Management
Device blacklist = (not configured)
Driver blacklist = amdgpu nouveau nvidia radeon pcieport

/sys/bus/pci/devices/0000:00:00.0/power/control = auto (0x060000, Host bridge, skl_uncore)
/sys/bus/pci/devices/0000:00:01.0/power/control = auto (0x060400, PCI bridge, pcieport)
/sys/bus/pci/devices/0000:00:02.0/power/control = auto (0x030000, VGA compatible controller, i915)

......

+++ USB
Autosuspend = disabled
Device whitelist = (not configured)
Device blacklist = (not configured)
Bluetooth blacklist = disabled
Phone blacklist = disabled
WWAN blacklist = enabled

Bus 002 Device 001 ID 1d6b:0003 control = auto, autosuspend_delay_ms = 0 -- Linux Foundation 3.0 root hub (hub)
Bus 001 Device 003 ID 174f:14e8 control = auto, autosuspend_delay_ms = 2000 -- Syntek (uvcvideo)
Bus 001 Device 002 ID 17ef:6053 control = on, autosuspend_delay_ms = 2000 -- Lenovo (usbhid)
Bus 001 Device 004 ID 8087:0a2b control = auto, autosuspend_delay_ms = 2000 -- Intel Corp. (btusb)
Bus 001 Device 001 ID 1d6b:0002 control = auto, autosuspend_delay_ms = 0 -- Linux Foundation 2.0 root hub (hub)

+++ Battery Status
/sys/class/power_supply/BAT0/manufacturer = SMP
/sys/class/power_supply/BAT0/model_name = L14M4P23
/sys/class/power_supply/BAT0/cycle_count = (not supported)
/sys/class/power_supply/BAT0/energy_full_design = 60000 [mWh]
/sys/class/power_supply/BAT0/energy_full = 51690 [mWh]
/sys/class/power_supply/BAT0/energy_now = 50140 [mWh]
/sys/class/power_supply/BAT0/power_now = 12185 [mW]
/sys/class/power_supply/BAT0/status = Discharging

Charge = 97.0 [%]
Capacity = 86.2 [%]

via: https://www.2daygeek.com/tlp-increase-optimize-linux-laptop-battery-life/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:wxy 校对:wxy

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

学习在 Linux 中进程是如何与其他进程进行同步的。

本篇是 Linux 下进程间通信(IPC)系列的第二篇文章。第一篇文章 聚焦于通过共享文件和共享内存段这样的共享存储来进行 IPC。这篇文件的重点将转向管道,它是连接需要通信的进程之间的通道。管道拥有一个写端用于写入字节数据,还有一个读端用于按照先入先出的顺序读入这些字节数据。而这些字节数据可能代表任何东西:数字、员工记录、数字电影等等。

管道有两种类型,命名管道和无名管道,都可以交互式的在命令行或程序中使用它们;相关的例子在下面展示。这篇文章也将介绍内存队列,尽管它们有些过时了,但它们不应该受这样的待遇。

在本系列的第一篇文章中的示例代码承认了在 IPC 中可能受到竞争条件(不管是基于文件的还是基于内存的)的威胁。自然地我们也会考虑基于管道的 IPC 的安全并发问题,这个也将在本文中提及。针对管道和内存队列的例子将会使用 POSIX 推荐使用的 API,POSIX 的一个核心目标就是线程安全。

请查看一些 mq\_open 函数的 man 页,这个函数属于内存队列的 API。这个 man 页中有关 特性 的章节带有一个小表格:

接口特性
mq_open()线程安全MT-Safe

上面的 MT-Safe(MT 指的是 多线程 multi-threaded )意味着 mq_open 函数是线程安全的,进而暗示是进程安全的:一个进程的执行和它的一个线程执行的过程类似,假如竞争条件不会发生在处于相同进程的线程中,那么这样的条件也不会发生在处于不同进程的线程中。MT-Safe 特性保证了调用 mq_open 时不会出现竞争条件。一般来说,基于通道的 IPC 是并发安全的,尽管在下面例子中会出现一个有关警告的注意事项。

无名管道

首先让我们通过一个特意构造的命令行例子来展示无名管道是如何工作的。在所有的现代系统中,符号 | 在命令行中都代表一个无名管道。假设我们的命令行提示符为 %,接下来考虑下面的命令:

## 写入方在 | 左边,读取方在右边
% sleep 5 | echo "Hello, world!" 

sleepecho 程序以不同的进程执行,无名管道允许它们进行通信。但是上面的例子被特意设计为没有通信发生。问候语 “Hello, world!” 出现在屏幕中,然后过了 5 秒后,命令行返回,暗示 sleepecho 进程都已经结束了。这期间发生了什么呢?

在命令行中的竖线 | 的语法中,左边的进程(sleep)是写入方,右边的进程(echo)为读取方。默认情况下,读取方将会阻塞,直到从通道中能够读取到字节数据,而写入方在写完它的字节数据后,将发送 流已终止 end-of-stream 的标志。(即便写入方过早终止了,一个流已终止的标志还是会发给读取方。)无名管道将保持到写入方和读取方都停止的那个时刻。

在上面的例子中,sleep 进程并没有向通道写入任何的字节数据,但在 5 秒后就终止了,这时将向通道发送一个流已终止的标志。与此同时,echo 进程立即向标准输出(屏幕)写入问候语,因为这个进程并不从通道中读入任何字节,所以它并没有等待。一旦 sleepecho 进程都终止了,不会再用作通信的无名管道将会消失然后返回命令行提示符。

下面这个更加实用的示例将使用两个无名管道。我们假定文件 test.dat 的内容如下:

this
is
the
way
the
world
ends

下面的命令:

% cat test.dat | sort | uniq

会将 cat 连接 concatenate 的缩写)进程的输出通过管道传给 sort 进程以生成排序后的输出,然后将排序后的输出通过管道传给 uniq 进程以消除重复的记录(在本例中,会将两次出现的 “the” 缩减为一个):

ends
is
the
this
way
world

下面展示的情景展示的是一个带有两个进程的程序通过一个无名管道通信来进行通信。

示例 1. 两个进程通过一个无名管道来进行通信

#include <sys/wait.h> /* wait */
#include <stdio.h>
#include <stdlib.h>   /* exit functions */
#include <unistd.h>   /* read, write, pipe, _exit */
#include <string.h>

#define ReadEnd  0
#define WriteEnd 1

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1);    /** failure **/
}

int main() {
  int pipeFDs[2]; /* two file descriptors */
  char buf;       /* 1-byte buffer */
  const char* msg = "Nature's first green is gold\n"; /* bytes to write */

  if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");
  pid_t cpid = fork();                                /* fork a child process */
  if (cpid < 0) report_and_exit("fork");              /* check for failure */

  if (0 == cpid) {    /*** child ***/                 /* child process */
    close(pipeFDs[WriteEnd]);                         /* child reads, doesn't write */

    while (read(pipeFDs[ReadEnd], &buf, 1) > 0)       /* read until end of byte stream */
      write(STDOUT_FILENO, &buf, sizeof(buf));        /* echo to the standard output */

    close(pipeFDs[ReadEnd]);                          /* close the ReadEnd: all done */
    _exit(0);                                         /* exit and notify parent at once  */
  }
  else {              /*** parent ***/
    close(pipeFDs[ReadEnd]);                          /* parent writes, doesn't read */

    write(pipeFDs[WriteEnd], msg, strlen(msg));       /* write the bytes to the pipe */
    close(pipeFDs[WriteEnd]);                         /* done writing: generate eof */

    wait(NULL);                                       /* wait for child to exit */
    exit(0);                                          /* exit normally */
  }
  return 0;
}

上面名为 pipeUN 的程序使用系统函数 fork 来创建一个进程。尽管这个程序只有一个单一的源文件,在它正确执行的情况下将会发生多进程的情况。

下面的内容是对库函数 fork 如何工作的一个简要回顾:

  • fork 函数由进程调用,在失败时返回 -1 给父进程。在 pipeUN 这个例子中,相应的调用是:
pid_t cpid = fork(); /* called in parent */

函数调用后的返回值也被保存下来了。在这个例子中,保存在整数类型 pid_t 的变量 cpid 中。(每个进程有它自己的进程 ID,这是一个非负的整数,用来标记进程)。复刻一个新的进程可能会因为多种原因而失败,包括进程表满了的原因,这个结构由系统维持,以此来追踪进程状态。明确地说,僵尸进程假如没有被处理掉,将可能引起进程表被填满的错误。

  • 假如 fork 调用成功,则它将创建一个新的子进程,向父进程返回一个值,向子进程返回另外的一个值。在调用 fork 后父进程和子进程都将执行相同的代码。(子进程继承了到此为止父进程中声明的所有变量的拷贝),特别地,一次成功的 fork 调用将返回如下的东西:
+ 向子进程返回 `0`
+ 向父进程返回子进程的进程 ID
  • 在一次成功的 fork 调用后,一个 if/else 或等价的结构将会被用来隔离针对父进程和子进程的代码。在这个例子中,相应的声明为:
if (0 == cpid) { /*** child ***/
...
}
else { /*** parent ***/
...
} 

假如成功地复刻出了一个子进程,pipeUN 程序将像下面这样去执行。在一个整数的数列里:

int pipeFDs[2]; /* two file descriptors */

来保存两个文件描述符,一个用来向管道中写入,另一个从管道中写入。(数组元素 pipeFDs[0] 是读端的文件描述符,元素 pipeFDs[1] 是写端的文件描述符。)在调用 fork 之前,对系统 pipe 函数的成功调用,将立刻使得这个数组获得两个文件描述符:

if (pipe(pipeFDs) < 0) report_and_exit("pipeFD");

父进程和子进程现在都有了文件描述符的副本。但分离关注点模式意味着每个进程恰好只需要一个描述符。在这个例子中,父进程负责写入,而子进程负责读取,尽管这样的角色分配可以反过来。在 if 子句中的第一个语句将用于关闭管道的读端:

close(pipeFDs[WriteEnd]); /* called in child code */

在父进程中的 else 子句将会关闭管道的读端:

close(pipeFDs[ReadEnd]); /* called in parent code */

然后父进程将向无名管道中写入某些字节数据(ASCII 代码),子进程读取这些数据,然后向标准输出中回放它们。

在这个程序中还需要澄清的一点是在父进程代码中的 wait 函数。一旦被创建后,子进程很大程度上独立于它的父进程,正如简短的 pipeUN 程序所展示的那样。子进程可以执行任意的代码,而它们可能与父进程完全没有关系。但是,假如当子进程终止时,系统将会通过一个信号来通知父进程。

要是父进程在子进程之前终止又该如何呢?在这种情形下,除非采取了预防措施,子进程将会变成在进程表中的一个僵尸进程。预防措施有两大类型:第一种是让父进程去通知系统,告诉系统它对子进程的终止没有任何兴趣:

signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */

第二种方法是在子进程终止时,让父进程执行一个 wait。这样就确保了父进程可以独立于子进程而存在。在 pipeUN 程序中使用了第二种方法,其中父进程的代码使用的是下面的调用:

wait(NULL); /* called in parent */

这个对 wait 的调用意味着一直等待直到任意一个子进程的终止发生,因此在 pipeUN 程序中,只有一个子进程。(其中的 NULL 参数可以被替换为一个保存有子程序退出状态的整数变量的地址。)对于更细粒度的控制,还可以使用更灵活的 waitpid 函数,例如特别指定多个子进程中的某一个。

pipeUN 将会采取另一个预防措施。当父进程结束了等待,父进程将会调用常规的 exit 函数去退出。对应的,子进程将会调用 _exit 变种来退出,这类变种将快速跟踪终止相关的通知。在效果上,子进程会告诉系统立刻去通知父进程它的这个子进程已经终止了。

假如两个进程向相同的无名管道中写入内容,字节数据会交错吗?例如,假如进程 P1 向管道写入内容:

foo bar

同时进程 P2 并发地写入:

baz baz

到相同的管道,最后的结果似乎是管道中的内容将会是任意错乱的,例如像这样:

baz foo baz bar

只要没有写入超过 PIPE_BUF 字节,POSIX 标准就能确保写入不会交错。在 Linux 系统中, PIPE_BUF 的大小是 4096 字节。对于管道我更喜欢只有一个写入方和一个读取方,从而绕过这个问题。

命名管道

无名管道没有备份文件:系统将维持一个内存缓存来将字节数据从写方传给读方。一旦写方和读方终止,这个缓存将会被回收,进而无名管道消失。相反的,命名管道有备份文件和一个不同的 API。

下面让我们通过另一个命令行示例来了解命名管道的要点。下面是具体的步骤:

  • 开启两个终端。这两个终端的工作目录应该相同。
  • 在其中一个终端中,键入下面的两个命令(命令行提示符仍然是 %,我的注释以 ## 打头。):
% mkfifo tester ## 创建一个备份文件,名为 tester
% cat tester    ## 将管道的内容输出到 stdout 

在最开始,没有任何东西会出现在终端中,因为到现在为止没有在命名管道中写入任何东西。

  • 在第二个终端中输入下面的命令:
% cat > tester ## redirect keyboard input to the pipe
hello, world!  ## then hit Return key
bye, bye       ## ditto
<Control-C>    ## terminate session with a Control-C

无论在这个终端中输入什么,它都会在另一个终端中显示出来。一旦键入 Ctrl+C,就会回到正常的命令行提示符,因为管道已经被关闭了。

  • 通过移除实现命名管道的文件来进行清理:
% unlink tester

正如 mkfifo 程序的名字所暗示的那样,命名管道也被叫做 FIFO,因为第一个进入的字节,就会第一个出,其他的类似。有一个名为 mkfifo 的库函数,用它可以在程序中创建一个命名管道,它将在下一个示例中被用到,该示例由两个进程组成:一个向命名管道写入,而另一个从该管道读取。

示例 2. fifoWriter 程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

#define MaxLoops         12000   /* outer loop */
#define ChunkSize           16   /* how many written at a time */
#define IntsPerChunk         4   /* four 4-byte ints per chunk */
#define MaxZs              250   /* max microseconds to sleep */

int main() {
  const char* pipeName = "./fifoChannel";
  mkfifo(pipeName, 0666);                      /* read/write for user/group/others */
  int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
  if (fd < 0) return -1;                       /* can't go on */

  int i;
  for (i = 0; i < MaxLoops; i++) {          /* write MaxWrites times */
    int j;
    for (j = 0; j < ChunkSize; j++) {       /* each time, write ChunkSize bytes */
      int k;
      int chunk[IntsPerChunk];
      for (k = 0; k < IntsPerChunk; k++)
        chunk[k] = rand();
      write(fd, chunk, sizeof(chunk));
    }
    usleep((rand() % MaxZs) + 1);           /* pause a bit for realism */
  }

  close(fd);           /* close pipe: generates an end-of-stream marker */
  unlink(pipeName);    /* unlink from the implementing file */
  printf("%i ints sent to the pipe.\n", MaxLoops * ChunkSize * IntsPerChunk);

  return 0;
}

上面的 fifoWriter 程序可以被总结为如下:

  • 首先程序创建了一个命名管道用来写入数据:
mkfifo(pipeName, 0666); /* read/write perms for user/group/others */
int fd = open(pipeName, O_CREAT | O_WRONLY);

其中的 pipeName 是备份文件的名字,传递给 mkfifo 作为它的第一个参数。接着命名管道通过我们熟悉的 open 函数调用被打开,而这个函数将会返回一个文件描述符。

  • 在实现层面上,fifoWriter 不会一次性将所有的数据都写入,而是写入一个块,然后休息随机数目的微秒时间,接着再循环往复。总的来说,有 768000 个 4 字节整数值被写入到命名管道中。
  • 在关闭命名管道后,fifoWriter 也将使用 unlink 取消对该文件的连接。
close(fd); /* close pipe: generates end-of-stream marker */
unlink(pipeName); /* unlink from the implementing file */

一旦连接到管道的每个进程都执行了 unlink 操作后,系统将回收这些备份文件。在这个例子中,只有两个这样的进程 fifoWriterfifoReader,它们都做了 unlink 操作。

这个两个程序应该在不同终端的相同工作目录中执行。但是 fifoWriter 应该在 fifoReader 之前被启动,因为需要 fifoWriter 去创建管道。然后 fifoReader 才能够获取到刚被创建的命名管道。

示例 3. fifoReader 程序

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

unsigned is_prime(unsigned n) { /* not pretty, but efficient */
  if (n <= 3) return n > 1;
  if (0 == (n % 2) || 0 == (n % 3)) return 0;

  unsigned i;
  for (i = 5; (i * i) <= n; i += 6)
    if (0 == (n % i) || 0 == (n % (i + 2))) return 0;

  return 1; /* found a prime! */
}

int main() {
  const char* file = "./fifoChannel";
  int fd = open(file, O_RDONLY);
  if (fd < 0) return -1; /* no point in continuing */
  unsigned count = 0, total = 0, primes_count = 0;

  while (1) {
    int next;
    int i;

    ssize_t count = read(fd, &next, sizeof(int));
    if (0 == count) break;                  /* end of stream */
    else if (count == sizeof(int)) {        /* read a 4-byte int value */
      total++;
      if (is_prime(next)) primes_count++;
    }
  }

  close(fd);       /* close pipe from read end */
  unlink(file);    /* unlink from the underlying file */
  printf("Received ints: %u, primes: %u\n", total, primes_count);

  return 0;
}

上面的 fifoReader 的内容可以总结为如下:

  • 因为 fifoWriter 已经创建了命名管道,所以 fifoReader 只需要利用标准的 open 调用来通过备份文件来获取到管道中的内容:
const char* file = "./fifoChannel";
int fd = open(file, O_RDONLY);

这个文件的是以只读打开的。

  • 然后这个程序进入一个潜在的无限循环,在每次循环时,尝试读取 4 字节的块。read 调用:
ssize_t count = read(fd, &next, sizeof(int));

返回 0 来暗示该流的结束。在这种情况下,fifoReader 跳出循环,关闭命名管道,并在终止前 unlink 备份文件。

  • 在读入 4 字节整数后,fifoReader 检查这个数是否为质数。这个操作代表了一个生产级别的读取器可能在接收到的字节数据上执行的逻辑操作。在示例运行中,在接收到的 768000 个整数中有 37682 个质数。

重复运行示例, fifoReader 将成功地读取 fifoWriter 写入的所有字节。这不是很让人惊讶的。这两个进程在相同的机器上执行,从而可以不用考虑网络相关的问题。命名管道是一个可信且高效的 IPC 机制,因而被广泛使用。

下面是这两个程序的输出,它们在不同的终端中启动,但处于相同的工作目录:

% ./fifoWriter
768000 ints sent to the pipe.
###
% ./fifoReader
Received ints: 768000, primes: 37682

消息队列

管道有着严格的先入先出行为:第一个被写入的字节将会第一个被读,第二个写入的字节将第二个被读,以此类推。消息队列可以做出相同的表现,但它又足够灵活,可以使得字节块可以不以先入先出的次序来接收。

正如它的名字所提示的那样,消息队列是一系列的消息,每个消息包含两部分:

  • 荷载,一个字节序列(在 C 中是 char)
  • 类型,以一个正整数值的形式给定,类型用来分类消息,为了更灵活的回收

看一下下面对一个消息队列的描述,每个消息由一个整数类型标记:

          +-+    +-+    +-+    +-+
sender--->|3|--->|2|--->|2|--->|1|--->receiver
          +-+    +-+    +-+    +-+

在上面展示的 4 个消息中,标记为 1 的是开头,即最接近接收端,然后另个标记为 2 的消息,最后接着一个标记为 3 的消息。假如按照严格的 FIFO 行为执行,消息将会以 1-2-2-3 这样的次序被接收。但是消息队列允许其他收取次序。例如,消息可以被接收方以 3-2-1-2 的次序接收。

mqueue 示例包含两个程序,sender 将向消息队列中写入数据,而 receiver 将从这个队列中读取数据。这两个程序都包含的头文件 queue.h 如下所示:

示例 4. 头文件 queue.h

#define ProjectId 123
#define PathName  "queue.h" /* any existing, accessible file would do */
#define MsgLen    4
#define MsgCount  6

typedef struct { 
  long type;                 /* must be of type long */ 
  char payload[MsgLen + 1];  /* bytes in the message */  
} queuedMessage;

上面的头文件定义了一个名为 queuedMessage 的结构类型,它带有 payload(字节数组)和 type(整数)这两个域。该文件也定义了一些符号常数(使用 #define 语句),前两个常数被用来生成一个 key,而这个 key 反过来被用来获取一个消息队列的 ID。ProjectId 可以是任何正整数值,而 PathName 必须是一个存在的、可访问的文件,在这个示例中,指的是文件 queue.h。在 senderreceiver 中,它们都有的设定语句为:

key_t key = ftok(PathName, ProjectId); /* generate key */
int qid = msgget(key, 0666 | IPC_CREAT); /* use key to get queue id */

ID qid 在效果上是消息队列文件描述符的对应物。

示例 5. sender 程序

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include <string.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key = ftok(PathName, ProjectId);
  if (key < 0) report_and_exit("couldn't get key...");

  int qid = msgget(key, 0666 | IPC_CREAT);
  if (qid < 0) report_and_exit("couldn't get queue id...");

  char* payloads[] = {"msg1", "msg2", "msg3", "msg4", "msg5", "msg6"};
  int types[] = {1, 1, 2, 2, 3, 3}; /* each must be > 0 */
  int i;
  for (i = 0; i < MsgCount; i++) {
    /* build the message */
    queuedMessage msg;
    msg.type = types[i];
    strcpy(msg.payload, payloads[i]);

    /* send the message */
    msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT); /* don't block */
    printf("%s sent as type %i\n", msg.payload, (int) msg.type);
  }
  return 0;
}

上面的 sender 程序将发送出 6 个消息,每两个为一个类型:前两个是类型 1,接着的连个是类型 2,最后的两个为类型 3。发送的语句:

msgsnd(qid, &msg, sizeof(msg), IPC_NOWAIT);

被配置为非阻塞的(IPC_NOWAIT 标志),是因为这里的消息体量上都很小。唯一的危险在于一个完整的序列将可能导致发送失败,而这个例子不会。下面的 receiver 程序也将使用 IPC_NOWAIT 标志来接收消息。

示例 6. receiver 程序

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdlib.h>
#include "queue.h"

void report_and_exit(const char* msg) {
  perror(msg);
  exit(-1); /* EXIT_FAILURE */
}

int main() {
  key_t key= ftok(PathName, ProjectId); /* key to identify the queue */
  if (key < 0) report_and_exit("key not gotten...");

  int qid = msgget(key, 0666 | IPC_CREAT); /* access if created already */
  if (qid < 0) report_and_exit("no access to queue...");

  int types[] = {3, 1, 2, 1, 3, 2}; /* different than in sender */
  int i;
  for (i = 0; i < MsgCount; i++) {
    queuedMessage msg; /* defined in queue.h */
    if (msgrcv(qid, &msg, sizeof(msg), types[i], MSG_NOERROR | IPC_NOWAIT) < 0)
      puts("msgrcv trouble...");
    printf("%s received as type %i\n", msg.payload, (int) msg.type);
  }

  /** remove the queue **/
  if (msgctl(qid, IPC_RMID, NULL) < 0)  /* NULL = 'no flags' */
    report_and_exit("trouble removing queue...");

  return 0;
}

这个 receiver 程序不会创建消息队列,尽管 API 尽管建议那样。在 receiver 中,对

int qid = msgget(key, 0666 | IPC_CREAT);

的调用可能因为带有 IPC_CREAT 标志而具有误导性,但是这个标志的真实意义是如果需要就创建,否则直接获取sender 程序调用 msgsnd 来发送消息,而 receiver 调用 msgrcv 来接收它们。在这个例子中,sender 以 1-1-2-2-3-3 的次序发送消息,但 receiver 接收它们的次序为 3-1-2-1-3-2,这显示消息队列没有被严格的 FIFO 行为所拘泥:

% ./sender
msg1 sent as type 1
msg2 sent as type 1
msg3 sent as type 2
msg4 sent as type 2
msg5 sent as type 3
msg6 sent as type 3

% ./receiver
msg5 received as type 3
msg1 received as type 1
msg3 received as type 2
msg2 received as type 1
msg6 received as type 3
msg4 received as type 2

上面的输出显示 senderreceiver 可以在同一个终端中启动。输出也显示消息队列是持久的,即便 sender 进程在完成创建队列、向队列写数据、然后退出的整个过程后,该队列仍然存在。只有在 receiver 进程显式地调用 msgctl 来移除该队列,这个队列才会消失:

if (msgctl(qid, IPC_RMID, NULL) < 0) /* remove queue */

总结

管道和消息队列的 API 在根本上来说都是单向的:一个进程写,然后另一个进程读。当然还存在双向命名管道的实现,但我认为这个 IPC 机制在它最为简单的时候反而是最佳的。正如前面提到的那样,消息队列已经不大受欢迎了,尽管没有找到什么特别好的原因来解释这个现象;而队列仍然是 IPC 工具箱中的一个工具。这个快速的 IPC 工具箱之旅将以第 3 部分(通过套接字和信号来示例 IPC)来终结。


via: https://opensource.com/article/19/4/interprocess-communication-linux-channels

作者:Marty Kalin 选题:lujun9972 译者:FSSlc 校对:wxy

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

你可能会根据你的需要执行以下命令。我会在这里列举一些你会用到这些命令的例子。

当你添加一个网卡或者从一个物理网卡创建出一个虚拟网卡的时候,你可能需要使用这些命令将新网卡启用起来。另外,如果你对网卡做了某些修改或者网卡本身没有启用,那么你也需要使用以下的某个命令将网卡启用起来。

启用、禁用网卡有很多种方法。在这篇文章里,我们会介绍我们使用过的最好的 5 种方法。

启用禁用网卡可以使用以下 5 个方法来完成:

  • ifconfig 命令:用于配置网卡。它可以提供网卡的很多信息。
  • ifdown/up 命令:ifdown 命令用于禁用网卡,ifup 命令用于启用网卡。
  • ip 命令:用于管理网卡,用于替代老旧的、不推荐使用的 ifconfig 命令。它和 ifconfig 命令很相似,但是提供了很多 ifconfig 命令所不具有的强大的特性。
  • nmcli 命令:是一个控制 NetworkManager 并报告网络状态的命令行工具。
  • nmtui 命令:是一个与 NetworkManager 交互的、基于 curses 图形库的终端 UI 应用。

以下显示的是我的 Linux 系统中可用网卡的信息。

# ip a
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s3:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s3
       valid_lft 86049sec preferred_lft 86049sec
    inet6 fe80::3899:270f:ae38:b433/64 scope link noprefixroute
       valid_lft forever preferred_lft forever
3: enp0s8:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:30:5d:52 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.3/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s8
       valid_lft 86049sec preferred_lft 86049sec
    inet6 fe80::32b7:8727:bdf2:2f3/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

1、如何使用 ifconfig 命令启用禁用网卡?

ifconfig 命令用于配置网卡。

在系统启动过程中如果需要启用网卡,调用的命令就是 ifconfigifconfig 可以提供很多网卡的信息。不管我们想修改网卡的什么配置,都可以使用该命令。

ifconfig 的常用语法:

# ifconfig [NIC_NAME] Down/Up

执行以下命令禁用 enp0s3 网卡。注意,这里你需要输入你自己的网卡名字。

# ifconfig enp0s3 down

从以下输出结果可以看到网卡已经被禁用了。

# ip a | grep -A 1 "enp0s3:"
2: enp0s3:  mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff

执行以下命令启用 enp0s3 网卡。

# ifconfig enp0s3 up

从以下输出结果可以看到网卡已经启用了。

# ip a | grep -A 5 "enp0s3:"
2: enp0s3:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s3
       valid_lft 86294sec preferred_lft 86294sec
    inet6 fe80::3899:270f:ae38:b433/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

2、如何使用 ifdown/up 命令启用禁用网卡?

ifdown 命令用于禁用网卡,ifup 命令用于启用网卡。

注意:这两个命令不支持以 enpXXX 命名的新的网络设备。

ifdown/ifup 的常用语法:

# ifdown [NIC_NAME]
# ifup [NIC_NAME]

执行以下命令禁用 eth1 网卡。

# ifdown eth1

从以下输出结果可以看到网卡已经被禁用了。

# ip a | grep -A 3 "eth1:"
3: eth1:  mtu 1500 qdisc pfifo_fast state DOWN qlen 1000
    link/ether 08:00:27:d5:a0:18 brd ff:ff:ff:ff:ff:ff

执行以下命令启用 eth1 网卡。

# ifup eth1

从以下输出结果可以看到网卡已经启用了。

# ip a | grep -A 5 "eth1:"
3: eth1:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:d5:a0:18 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.7/24 brd 192.168.1.255 scope global eth1
    inet6 fe80::a00:27ff:fed5:a018/64 scope link tentative dadfailed
       valid_lft forever preferred_lft forever

ifupifdown 不支持以 enpXXX 命名的网卡。当执行该命令时得到的结果如下:

# ifdown enp0s8
Unknown interface enp0s8

3、如何使用 ip 命令启用禁用网卡?

ip 命令用于管理网卡,用于替代老旧的、不推荐使用的 ifconfig 命令。

它和 ifconfig 命令很相似,但是提供了很多 ifconfig 命令不具有的强大的特性。

ip 的常用语法:

# ip link set  Down/Up

执行以下命令禁用 enp0s3 网卡。

# ip link set enp0s3 down

从以下输出结果可以看到网卡已经被禁用了。

# ip a | grep -A 1 "enp0s3:"
2: enp0s3:  mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
    link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff

执行以下命令启用 enp0s3 网卡。

# ip link set enp0s3 up

从以下输出结果可以看到网卡已经启用了。

# ip a | grep -A 5 "enp0s3:"
2: enp0s3:  mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 08:00:27:c2:e4:e8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.4/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s3
       valid_lft 86294sec preferred_lft 86294sec
    inet6 fe80::3899:270f:ae38:b433/64 scope link noprefixroute
       valid_lft forever preferred_lft forever

4、如何使用 nmcli 命令启用禁用网卡?

nmcli 是一个控制 NetworkManager 并报告网络状态的命令行工具。

nmcli 可以用做 nm-applet 或者其他图形化客户端的替代品。它可以用于展示、创建、修改、删除、启用和停用网络连接。除此之后,它还可以用来管理和展示网络设备状态。

nmcli 命令大部分情况下都是使用“配置名称”工作而不是“设备名称”。所以,执行以下命令,获取网卡对应的配置名称。(LCTT 译注:在使用 nmtui 或者 nmcli 管理网络连接的时候,可以为网络连接配置一个名称,就是这里提到的 配置名称 Profile name `)

# nmcli con show
NAME                UUID                                  TYPE      DEVICE
Wired connection 1  3d5afa0a-419a-3d1a-93e6-889ce9c6a18c  ethernet  enp0s3
Wired connection 2  a22154b7-4cc4-3756-9d8d-da5a4318e146  ethernet  enp0s8

nmcli 的常用语法:

# nmcli con  Down/Up

执行以下命令禁用 enp0s3 网卡。在禁用网卡的时候,你需要使用配置名称而不是设备名称。

# nmcli con down 'Wired connection 1'
Connection 'Wired connection 1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6)

从以下输出结果可以看到网卡已经禁用了。

# nmcli dev status
DEVICE  TYPE      STATE         CONNECTION
enp0s8  ethernet  connected     Wired connection 2
enp0s3  ethernet  disconnected  --
lo      loopback  unmanaged     --

执行以下命令启用 enp0s3 网卡。同样的,这里你需要使用配置名称而不是设备名称。

# nmcli con up 'Wired connection 1'
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/7)

从以下输出结果可以看到网卡已经启用了。

# nmcli dev status
DEVICE  TYPE      STATE      CONNECTION
enp0s8  ethernet  connected  Wired connection 2
enp0s3  ethernet  connected  Wired connection 1
lo      loopback  unmanaged  --

5、如何使用 nmtui 命令启用禁用网卡?

nmtui 是一个与 NetworkManager 交互的、基于 curses 图形库的终端 UI 应用。

在启用 nmtui 的时候,如果第一个参数没有特别指定,它会引导用户选择对应的操作去执行。

执行以下命令打开 mntui 界面。选择 “Active a connection” 然后点击 “OK”。

# nmtui

选择你要禁用的网卡,然后点击 “Deactivate” 按钮,就可以将网卡禁用。

如果要启用网卡,使用上述同样的步骤即可。


via: https://www.2daygeek.com/enable-disable-up-down-nic-network-interface-port-linux-using-ifconfig-ifdown-ifup-ip-nmcli-nmtui/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:bodhix 校对:wxy

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

Android 模拟器允许我们直接从 Linux 系统上运行我们最喜欢的 Android 应用程序或游戏。对于 Linux 来说,有很多的这样的 Android 模拟器,在过去我们介绍过几个此类应用程序。

你可以通过导航到下面的网址回顾它们。

今天我们将讨论 Anbox Android 模拟器。

Anbox 是什么?

Anbox 是 “Android in a box” 的缩写。Anbox 是一个基于容器的方法,可以在普通的 GNU/Linux 系统上启动完整的 Android 系统。

它是现代化的新模拟器之一。

Anbox 可以让你在 Linux 系统上运行 Android,而没有虚拟化的迟钝,因为核心的 Android 操作系统已经使用 Linux 命名空间(LXE)放置到容器中了。

Android 容器不能直接访问到任何硬件,所有硬件的访问都是通过在主机上的守护进程进行的。

每个应用程序将在一个单独窗口打开,就像其它本地系统应用程序一样,并且它可以显示在启动器中。

如何在 Linux 中安装 Anbox ?

Anbox 也可作为 snap 软件包安装,请确保你已经在你的系统上启用了 snap 支持。

Anbox 软件包最近被添加到 Ubuntu 18.10 (Cosmic) 和 Debian 10 (Buster) 软件仓库。如果你正在运行这些版本,那么你可以轻松地在官方发行版的软件包管理器的帮助下安装。否则可以用 snap 软件包安装。

为使 Anbox 工作,确保需要的内核模块已经安装在你的系统中。对于基于 Ubuntu 的用户,使用下面的 PPA 来安装它。

$ sudo add-apt-repository ppa:morphis/anbox-support
$ sudo apt update
$ sudo apt install linux-headers-generic anbox-modules-dkms

在你安装 anbox-modules-dkms 软件包后,你必须手动重新加载内核模块,或需要系统重新启动。

$ sudo modprobe ashmem_linux
$ sudo modprobe binder_linux

对于 Debian/Ubuntu 系统,使用 APT-GET 命令APT 命令 来安装 anbox。

$ sudo apt install anbox

对于基于 Arch Linux 的系统,我们总是习惯从 AUR 储存库中获取软件包。所以,使用任一个的 AUR 助手 来安装它。我喜欢使用 Yay 工具

$ yuk -S anbox-git

否则,你可以通过导航到下面的文章来 在 Linux 中安装和配置 snap。如果你已经在你的系统上安装 snap,其它的步骤可以忽略。

$ sudo snap install --devmode --beta anbox

Anbox 的必要条件

默认情况下,Anbox 并没有带有 Google Play Store。因此,我们需要手动下载每个应用程序(APK),并使用 Android 调试桥(ADB)安装它。

ADB 工具在大多数的发行版的软件仓库是轻易可获得的,我们可以容易地安装它。

对于 Debian/Ubuntu 系统,使用 APT-GET 命令APT 命令 来安装 ADB。

$ sudo apt install android-tools-adb

对于 Fedora 系统,使用 DNF 命令 来安装 ADB。

$ sudo dnf install android-tools

对于基于 Arch Linux 的系统,使用 Pacman 命令 来安装 ADB。

$ sudo pacman -S android-tools

对于 openSUSE Leap 系统,使用 Zypper 命令 来安装 ADB。

$ sudo zypper install android-tools

在哪里下载 Android 应用程序?

既然我们不能使用 Play Store ,你就得从信得过的网站来下载 APK 软件包,像 APKMirror ,然后手动安装它。

如何启动 Anbox?

Anbox 可以从 Dash 启动。这是默认的 Anbox 外貌。

如何把应用程序推到 Anbox ?

像我先前所说,我们需要手动安装它。为测试目的,我们将安装 YouTube 和 Firefox 应用程序。

首先,你需要启动 ADB 服务。为做到这样,运行下面的命令。

$ adb devices

我们已经下载 YouTube 和 Firefox 应用程序,现在我们将安装。

语法格式:

$ adb install Name-Of-Your-Application.apk

安装 YouTube 和 Firefox 应用程序:

$ adb install 'com.google.android.youtube_14.13.54-1413542800_minAPI19(x86_64)(nodpi)_apkmirror.com.apk'
Success

$ adb install 'org.mozilla.focus_9.0-330191219_minAPI21(x86)(nodpi)_apkmirror.com.apk'
Success

我已经在我的 Anbox 中安装 YouTube 和 Firefox。查看下面的截图。

像我们在文章的开始所说,它将以新的标签页打开任何的应用程序。在这里,我们将打开 Firefox ,并访问 2daygeek.com 网站。


via: https://www.2daygeek.com/anbox-best-android-emulator-for-linux/

作者:Magesh Maruthamuthu 选题:lujun9972 译者:robsean 校对:wxy

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

学习如何安装和配置 Designate,这是一个 OpenStack 的多租户 DNS 即服务(DNSaaS)。

Designate 是一个多租户的 DNS 即服务,它包括一个用于域名和记录管理的 REST API 和集成了 Neutron 的框架,并支持 Bind9。

DNSaaS 可以提供:

  • 一个管理区域和记录的干净利落的 REST API
  • 自动生成记录(集成 OpenStack)
  • 支持多个授权名字服务器
  • 可以托管多个项目/组织

 title=

这篇文章解释了如何在 CentOS 和 RHEL 上手动安装和配置 Designate 的最新版本,但是同样的配置也可以用在其它发行版上。

在 OpenStack 上安装 Designate

在我的 GitHub 仓库里,我已经放了 Ansible 的 bind 和 Designate 角色的示范设置。

这个设置假定 bing 服务是安装 OpenStack 控制器节点之外(即使你可以在本地安装 bind)。

1、在 OpenStack 控制节点上安装 Designate 和 bind 软件包:

# yum install openstack-designate-* bind bind-utils -y

2、创建 Designate 数据库和用户:

MariaDB [(none)]> CREATE DATABASE designate CHARACTER SET utf8 COLLATE utf8_general_ci;
       
MariaDB [(none)]> GRANT ALL PRIVILEGES ON designate.* TO \
'designate'@'localhost' IDENTIFIED BY 'rhlab123';

MariaDB [(none)]> GRANT ALL PRIVILEGES ON designate.* TO 'designate'@'%' \
IDENTIFIED BY 'rhlab123';

注意:bind 包必须安装在控制节点之外才能使 远程名字服务控制 Remote Name Daemon Control (RNDC)功能正常。

配置 bind(DNS 服务器)

1、生成 RNDC 文件:

rndc-confgen -a -k designate -c /etc/rndc.key -r /dev/urandom

cat <<EOF> etcrndc.conf
include "/etc/rndc.key";
options {
  default-key "designate";
  default-server {{ DNS_SERVER_IP }};
  default-port 953;
};
EOF

2、将下列配置添加到 named.conf

include "/etc/rndc.key"; 
controls {
  inet {{ DNS_SERVER_IP }} allow { localhost;{{ CONTROLLER_SERVER_IP }}; } keys { "designate"; };
};

option 节中,添加:

options {
  ...
  allow-new-zones yes;
  request-ixfr no;
  listen-on port 53 { any; };
  recursion no;
  allow-query { 127.0.0.1; {{ CONTROLLER_SERVER_IP }}; };
};

添加正确的权限:

chown named:named /etc/rndc.key
chown named:named /etc/rndc.conf
chmod 600 /etc/rndc.key
chown -v root:named /etc/named.conf
chmod g+w /var/named

# systemctl restart named
# setsebool named_write_master_zones 1

3、把 rndc.keyrndc.conf 推入 OpenStack 控制节点:

# scp -r /etc/rndc* {{ CONTROLLER_SERVER_IP }}:/etc/

创建 OpenStack Designate 服务和端点

输入:

# openstack user create --domain default --password-prompt designate
# openstack role add --project services --user designate admin
# openstack service create --name designate --description "DNS" dns

# openstack endpoint create --region RegionOne dns public http://{{ CONTROLLER_SERVER_IP }}:9001/
# openstack endpoint create --region RegionOne dns internal http://{{ CONTROLLER_SERVER_IP }}:9001/  
# openstack endpoint create --region RegionOne dns admin http://{{ CONTROLLER_SERVER_IP }}:9001/

配置 Designate 服务

1、编辑 /etc/designate/designate.conf

[service:api] 节配置 auth_strategy

[service:api]
listen = 0.0.0.0:9001
auth_strategy = keystone
api_base_uri = http://{{ CONTROLLER_SERVER_IP }}:9001/
enable_api_v2 = True
enabled_extensions_v2 = quotas, reports

[keystone_authtoken] 节配置下列选项:

[keystone_authtoken]
auth_type = password
username = designate
password = rhlab123
project_name = service
project_domain_name = Default
user_domain_name = Default
www_authenticate_uri = http://{{ CONTROLLER_SERVER_IP }}:5000/
auth_url = http://{{ CONTROLLER_SERVER_IP }}:5000/ 

[service:worker] 节,启用 worker 模型:

enabled = True
notify = True

[storage:sqlalchemy] 节,配置数据库访问:

[storage:sqlalchemy]
connection = mysql+pymysql://designate:rhlab123@{{ CONTROLLER_SERVER_IP }}/designate

填充 Designate 数据库:

# su -s /bin/sh -c "designate-manage database sync" designate

2、 创建 Designate 的 pools.yaml 文件(包含 target 和 bind 细节):

编辑 /etc/designate/pools.yaml

- name: default
  # The name is immutable. There will be no option to change the name after
  # creation and the only way will to change it will be to delete it
  # (and all zones associated with it) and recreate it.
  description: Default Pool

  attributes: {}

  # List out the NS records for zones hosted within this pool
  # This should be a record that is created outside of designate, that
  # points to the public IP of the controller node.
  ns_records:
    - hostname: {{Controller_FQDN}}. # Thisis mDNS
      priority: 1

  # List out the nameservers for this pool. These are the actual BIND servers.
  # We use these to verify changes have propagated to all nameservers.
  nameservers:
    - host: {{ DNS_SERVER_IP }}
      port: 53

  # List out the targets for this pool. For BIND there will be one
  # entry for each BIND server, as we have to run rndc command on each server
  targets:
    - type: bind9
      description: BIND9 Server 1

      # List out the designate-mdns servers from which BIND servers should
      # request zone transfers (AXFRs) from.
      # This should be the IP of the controller node.
      # If you have multiple controllers you can add multiple masters
      # by running designate-mdns on them, and adding them here.
      masters:
        - host: {{ CONTROLLER_SERVER_IP }}
          port: 5354

      # BIND Configuration options
      options:
        host: {{ DNS_SERVER_IP }}
        port: 53
        rndc_host: {{ DNS_SERVER_IP }}
        rndc_port: 953
        rndc_key_file: /etc/rndc.key
        rndc_config_file: /etc/rndc.conf

填充 Designate 池:

su -s /bin/sh -c "designate-manage pool update" designate

3、启动 Designate 中心和 API 服务:

systemctl enable --now designate-central designate-api

4、验证 Designate 服务运行:

# openstack dns service list

+--------------+--------+-------+--------------+
| service_name | status | stats | capabilities |
+--------------+--------+-------+--------------+
| central      | UP     | -     | -            |
| api          | UP     | -     | -            |
| mdns         | UP     | -     | -            |
| worker       | UP     | -     | -            |
| producer     | UP     | -     | -            |
+--------------+--------+-------+--------------+

用外部 DNS 配置 OpenStack Neutron

1、为 Designate 服务配置 iptables:

# iptables -I INPUT -p tcp -m multiport --dports 9001 -m comment --comment "designate incoming" -j ACCEPT
       
# iptables -I INPUT -p tcp -m multiport --dports 5354 -m comment --comment "Designate mdns incoming" -j ACCEPT
       
# iptables -I INPUT -p tcp -m multiport --dports 53 -m comment --comment "bind incoming" -j ACCEPT
        
# iptables -I INPUT -p udp -m multiport --dports 53 -m comment --comment "bind/powerdns incoming" -j ACCEPT
       
# iptables -I INPUT -p tcp -m multiport --dports 953 -m comment --comment "rndc incoming - bind only" -j ACCEPT
       
# service iptables save; service iptables restart
# setsebool named_write_master_zones 1

2、 编辑 /etc/neutron/neutron.conf[default] 节:

external_dns_driver = designate

3、 在 /etc/neutron/neutron.conf 中添加 [designate] 节:

[designate]
url = http://{{ CONTROLLER_SERVER_IP }}:9001/v2 ## This end point of designate
auth_type = password
auth_url = http://{{ CONTROLLER_SERVER_IP }}:5000
username = designate
password = rhlab123
project_name = services
project_domain_name = Default
user_domain_name = Default
allow_reverse_dns_lookup = True
ipv4_ptr_zone_prefix_size = 24
ipv6_ptr_zone_prefix_size = 116 

4、编辑 neutron.confdns_domain

dns_domain = rhlab.dev.

重启:

# systemctl restart neutron-*

5、在 /etc/neutron/plugins/ml2/ml2_conf.ini 中的组成层 2(ML2)中添加 dns

extension_drivers=port_security,qos,dns

6、在 Designate 中添加区域:

# openstack zone create –[email protected] rhlab.dev.

rhlab.dev 区域中添加记录:

# openstack recordset create --record '192.168.1.230' --type A rhlab.dev. Test

Designate 现在就安装和配置好了。


via: https://opensource.com/article/19/4/getting-started-openstack-designate

作者:Amjad Yaseen 选题:lujun9972 译者:wxy 校对:wxy

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