3
18
2012
4

使用 gnokii 读取 3G 网卡的短信

使用 gnokii 读取 3G 网卡短信的方法ArchWiki上有写。安装 gnokii 后复制配置文件并将自己添加到uucp用户组中:

cp /etc/gnokiirc ~/.config/gnokii/config
sudo gpasswd -a `whoami` uucp

然后修改下配置文件,主要是port = /dev/ttyUSB0model = AT这两处。用户组的修改要下次登录时才生效,或者使用newgrp命令来登录到uucp组。据说此命令在某些 shell 里是内建命令,不过在 bash 和 zsh 里没有,只能调用外部命令,所以会开启一个新的 shell。

newgrp uucp

现在就可以读短信了。接收新信息并存储,可使用命令

gnokii --smsreader

完事之后按Ctrl-C中断。要把短信读出来,gnokii 可以把短信存储为 mbox 格式。这是一种邮件格式,使用 mutt 即可阅读。-f后边的sms即是要保存的文件名。注意,gnokii 仍会将消息输出到终端。ArchWiki 上说的是使用 xgnokii GUI 程序来读取,但是我没有找到这个程序(只有 manpage)。

gnokii --getsms SM 0 end -f sms

对于中文短信,这样会导致乱码。所以我写了个 Python 脚本来处理。三件事:一是将内容编码标识为 UTF-8,二是把按字节截断的邮件主题(短信正文的前若干个字节)最后几个无效的编码替换掉,三是将邮件主题按标准进行编码。这些事 Python 处理起来挺容易的 ;-)

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import os
import sys
import email.header

for i in os.fdopen(sys.stdin.fileno(), encoding='utf-8', errors='replace'):
  if i.startswith('Subject: '):
    s = i[9:-1]
    print('Content-Type: text/plain; charset=utf-8')
    print('Subject:', email.header.Header(s, 'utf-8').encode())
  else:
    sys.stdout.write(i)
Category: Linux | Tags: LInux Python 网络 mutt
3
3
2012
6

GM 脚本:github 默认下载格式改为 gzip

不知道为什么,新版 github 在项目首页上只提供 zip 归档格式的下载,要下载 gzip 包还需要点击 download 到新页面去。可是我就是不喜欢 zip 格式嘛——

// ==UserScript==
// @name           github 下载默认 gzip 格式
// @namespace      http://lilydjwg.is-programmer.com/ 
// @description    把默认的 zip 下载按键改成 gzip 的
// @include        https://github.com/*
// ==/UserScript==
 
var dl = document.querySelector('.btn-download');
if(dl){
  dl.title = dl.title.replace('zip', 'gzip')
  dl.href = dl.href.replace('/zipball/', '/tarball/');
  var icon = dl.querySelector('.icon');
  if(icon){
    icon.nextSibling.textContent = 'GZIP';
  }
}
Category: 火狐 | Tags: GreaseMonkey 火狐 linux github
2
28
2012
8

拯救分区表

今天,因为弄错了磁盘顺序,不小心把移动硬盘的前 40M 左右的数据覆盖掉了。所幸发现的时候移动硬盘还接在电脑上,除了第一个分区外,后边的还可以正常挂载和读取,这说明内核还记得分区表。但是fdisk已经读不到 MBR 了。

怎么办呢?我不想把几百 G 的数据拷一遍,而且理论上肯定是能够把分区表给完整的找回来的——内核不是还记得么?上网只搜到/proc/partitions这个文件,其中的内容如下:

major minor  #blocks  name

   8        0  312571224 sda
   8        1   52436128 sda1
   8        2   52428800 sda2
   8        3          1 sda3
   8        5     512000 sda5
   8        6   41943040 sda6
   8        7  161281024 sda7
   8        8    3964928 sda8
  11        0    1048575 sr0
   7        0      71680 loop0
   8       16  312571224 sdb
   8       17    1048576 sdb1
   8       18  209715200 sdb2
   8       19   10485760 sdb3
   8       20   91320320 sdb4

只有分区的大小信息,而且单位是。我按移动硬盘的大小推算了下,这里的块大小是1KiB(关于块大小,真够混乱的。ls默认的也是 1K,但是dd却是 512B)。

光知道了块大小不行啊。我先试了试著名的 testdisk 工具。它搜索了好久,最终只找到了两个分区,于是被我否决了。又继续找分区的更多信息。/proc下看完了,我又去不怎么了解的/sys下看,发现其下有个block目录,里面正是系统已经识别的块设备!

进到出事故的sdb下,再进入sdb1ls一下,看到sizestart都在呢!cat出来各是一个整数。经过一番猜测和计算,可以确定其单位是512B,也就是一个扇区

好了,可以开始重建分区表了。当然,我可不想手工去算和写那64字节的二进制数据。试了试 Arch 安装时所用的cfdisk。它有基于文本的图形化界面,比较友好。可是新建分区时才发现只能输入以1000进制MB为单位的大小,而我需要分毫不差的按原大小分区。只好退出,试试文本交互的fdisk。在不断地按m键查看帮助的情况下,终于把分区重建好了:

  Device Boot      Start         End      Blocks   Id  System
/dev/sdb1            2048     2099199     1048576   83  Linux
/dev/sdb2         2099200   421529599   209715200   83  Linux
/dev/sdb3       421529600   442501119    10485760   83  Linux
/dev/sdb4       442501120   625141760    91320320+   7  HPFS/NTFS/exFAT

前面几个的大小比较整,我是按+1G等这样输入的。这里要注意下的是,G, M, K等单位是1024进制,而GB, MB, KB等单位是1000进制。最后那个给 Windows 留的 NTFS 分区不知道为什么并不是在磁盘的最后一个扇区结束的,我输入的是/sys/block/sdb/sdb4/size里写的大小。

p查看并确认分区表正确后,按w写入。然后使用partprobe命令通知内核更新分区表信息。

这步做完后,后边的三个分区就安全了。第一个分区是一些启动文件,我已经打算重新弄一遍了。实际上fsck.ext2跑完后也只是在lost+found里出现了一堆垃圾文件。mkfs.ext2重新格式化,却在安装 grub2 时遇到了问题。大致的错误信息是这样的:

/dev/sdb appears to contain a iso9660 filesystem which isn't known to reserve space for DOS-style boot.  Installing GRUB there could result in FILESYSTEM DESTRUCTION if valuable data is overwritten by grub-setup (--skip-fs-probe disables this check, use at your own risk)

中文消息是:

/dev/sda 中似乎包含一个不为 DOS 引导保留空间的 iso9660 文件系统。在此处安装 GRUB 可能导致 grub-setup 覆盖重要数据从而损坏文件系统(--skip-fs-probe 参数可以禁用这个检查,使用该选项风险自负)

这个「iso9660」文件系统就是我误dd过去的。使用grub-setup并加上--skip-fs-probe参数后依旧出错:

warn: Attempting to install GRUB to a disk with multiple partition labels or both partition label and filesystem.
error: embedding is not possible, but this is required for cross-disk install.

中文消息是:

警告:正在试图将 GRUB 安装至有多个分区标签的磁盘,或同时有分区标签和文件系统的磁盘。这样的操作尚未被支持。
错误:无法嵌入,但在跨盘安装时是必须的

加上--force参数也没有用。我尝试消除前446字节的数据,亦没有用。后来想起在使用fdisk分区时,第一分区的起始扇区必须大于等于2048。难道是这些扇区中的内容影响了 grub2 的安装?head -c 1024 > a然后用 bviplus 查看,发现果然如此,都看到那个已经不完整的 iso9660 文件系统的卷标了。果然给它 dd 掉:

sudo dd if=/dev/zero of=/dev/sdb seek=1 count=2047 bs=1b

再次尝试安装,一切顺利!

Category: Linux | Tags: linux 失误 数据恢复
2
23
2012
8

迁移到64位 Arch Linux

曾经因为不了解,所以一开始选择了32位系统;后来内存大了,ArchLinux 的内核没有编译PAE支持,只能用 3GiB 多的内存,不爽。朋友又给了这篇教程,于是略作准备就开始动手了。

可是,但我真正动手时,那篇文章已经被另一篇英文教程取代了。当时忘记了 MediaWiki 会记录所有的编辑历史,所以只好将就着看了。结果,重启时 udev 没跑起来,说找不到 librt.so。我当然不会就此罢休,花费了一些时间,不仅拯救了系统,成功迁移到了 64 位,而且确定了 ArchWiki 上那篇教程的错误。

先说这个错误吧。现在已经修正了。方法 2: 从正在运行的系统一节,在安装 busybox 的时候,现在是一个红色的警告框,暗示着这里曾经发生的悲剧——

警告: 不要现在安装 lib32-glibc 软件包。在执行命令 ldconfig 后,当你安装 linux(内核)时,生成的镜像文件中,librt.so 等库文件会在 /usr/lib32 目录下,启动的时候二进制文件不会在此搜索库文件,导致启动失败。

当悲剧发生后,我有些紧张地拿出移动硬盘,先是进入移动硬盘上的 Arch,胡乱安装了几个 64 位软件包并重新生成 initramfs,结果连 init 也执行不了了。无奈我又启动64位内核的 PartedMagic chroot 进去查看。但是,chroot 失败了:

sh: cannot open shared object file

后来我才知道,因为我安装lib32-glibc/lib/ld-linux.so.2发生冲突,我覆盖了;后来卸载 32 位的 glibc 时,它被删除了。于是,动态链接的 32 位程序没有动态库加载器了。但其实还是有办法的,因为被误删的只是个软链接。

/usr/lib32/ld-linux.so.2 /bin/ls

老猫的提示下,手动指定ld-linux.so.2运行成功。我尝试把它链接过去,这个 32 位与 64 位库混合的系统开始有些可用了。接下来按 Wiki 里的指示操作就可以了。不过,我没有重装全部的库,而是只装了标明 i686 架构的库。这就是我之前那个用 Haskell 写的脚本的目的。不过还是出了点小问题——虽然我为防止程序运行不了而在开始之前把软件包列表生成了一份保存起来,但我忘了我的 HOME 是加密过的。PartedMagic 里没有 eCryptfs 工具,而 Arch 里的那个又因为库的原因运行不了,囧死了。。。幸运的是,我在迁移之前把 Dropbox 弄好同步了一遍,这个软件包列表也同步了。通过 Dropbox 网页界面下回来就好了。

弄好重启之后,整个事情还没完——我从 AUR 里编译安装的那些包还没重装呢。边重装边研究新的 64 位系统,发现 gcc 还是可以编译出 32 位程序的,只是要装gcc-multilib而已。现在可好了,既可以运行 64 位的程序,又可以运行 32 位的。库依赖少的 C 程序编译个 32 位的出来也没问题。

对了,最后说一句,vnstat 这厮的数据库格式竟然是平台相关的!换成 64 位后它就不断报错,直到我删除了以前的数据库。

Category: Linux | Tags: arch linux
1
28
2012
5

使用 eCryptfs 加密主目录

本文根据回忆记述在 Arch Linux 上为某一新用户建立使用 eCryptfs 加密的 $HOME 目录并使之在登录时自动解密挂载的过程。大量参考了 Unknown and partly hidden 的 eCryptfs and $HOME 一文。

依赖的软件包:ecryptfs-utils。

加密目录

# mkdir -p /home/.ecryptfs/user/private
# chmod 755 /home/.ecryptfs
# chmod -R 700 /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user
# ln -s /home/.ecryptfs/user/private /home/user/.private
# chmod 700 /home/user

注意:最后一步原文使用的是500权限,这里改成了700

第一次挂载加密目录:

# mount -t ecryptfs /home/user/.private /home/user

eCryptfs 会询问一些加密的选项,其中 Cypher(加密方法)和 Key byte 可自行选择:

Key type: passphrase
Passphrase: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Cypher: twofish
Key byte: 32
Plaintext passtrough: yes
Filename encryption: yes
Add signature to cache: yes

一定要记住密码,虽然可能并不怎么会用到。

mount命令的输出中找到这次挂载使用的参数,经过一些变更,把类似于以下的设置添加到/etc/fstab中:

/home/user/.private /home/user ecryptfs rw,user,noauto,exec,ecryptfs_sig=XYZ,ecryptfs_cipher=twofish,ecryptfs_key_bytes=32,ecryptfs_passthrough,ecryptfs_fnek_sig=XYZ,ecryptfs_unlink_sigs 0 0

注意:在登录挂载时,noexecnosuidnodev将会是默认选项。这里加上exec选项来覆盖掉noexec,这样加密的 $HOME 中才支持执行可执行文件。

挂载时生成了/root/.ecryptfs目录。我们先在里边保存些文件:

# touch /root/.ecryptfs/auto-mount
# ecryptfs-wrap-passphrase /root/.ecryptfs/wrapped-passphrase
Passphrase to wrap: [输入加密口令]
Wrapping passphrase: [输入用户的登录口令]

现在,使用用户的登录口令可以从文件/root/.ecryptfs/wrapped-passphrase中得到 eCryptfs 的加密口令。即使加密口令很强,如果登录口令弱的话,文件信息还是会泄漏的。所以,得选个强的登录口令,不然就不要玩登录时自动挂载加密 $HOME 了。

或者,你也可以玩点有趣的,把这个wrapped-passphrase文件放在 U 盘里,只留下一个指向 U 盘里的此文件的软链接。然后配置好 U 盘自动挂载,就做成了个简单的「U 盾」!

好了,现在卸载 $HOME:

# umount /home/user

自动挂载

先把 eCryptfs 的那个在/root下的目录弄回来:

# mv /root/.ecryptfs /home/.ecryptfs/user
# chown -R user:user /home/.ecryptfs/user/.ecryptfs
# ln -s /home/.ecryptfs/user/.ecryptfs /home/user/.ecryptfs

接下来,创建一个挂载用的脚本,暂时叫它/home/profile.sh吧。它将被写到用户登录时的自动执行脚本中,如~/.profile,或者~/.zprofile,如果你用 Zsh 的话。

if [ -r "$HOME/.ecryptfs/auto-mount" ]; then
  grep -qs "$HOME ecryptfs" /proc/mounts
  if [ $? -ne 0 ]; then
    mv $HOME/.Xauthority /tmp 2>/dev/null
    mount -i "$HOME"
    cd "$HOME"
    mv /tmp/.Xauthority $HOME 2>/dev/null
    (
      systemctl --user daemon-reload
      systemctl --user default
    ) &
  fi
fi

注意到这里加入了对~/.Xauthority文件的处理,不然从图形界面登录时,执行挂载命令后,会因授权文件不见了而失败。之前把 $HOME 的权限设置成700也是为了这个。单纯地允许写~/.Xauthority不行,因为 xauth 需要创建临时文件以防止此文件同时被多个进程修改。

现在,我们需要在用户登录时自动 unwrap 之前创建的那个wrapped-passphrase文件。在/etc/pam.d/login中添加几行(注意顺序):

#%PAM-1.0
#...
auth                required        pam_unix.so nullok
auth                required        pam_ecryptfs.so unwrap
#...
password            required        pam_ecryptfs.so
#password           required        pam_unix.so sha512 shadow use_authtok
#...

好了,我们先手动试试:

# su user
$ ecryptfs-insert-wrapped-passphrase-into-keyring /home/user/.ecryptfs/wrapped-passphrase
Passphrase: [输入用户密码]
$ mount -i /home/user

如果正确挂载的话,接下来就可以开始建设你的新 $HOME 了,比如把你以前的各种文件复制过去,等等。注意不要在加密的目录内进行 BT 下载哦。你可以建立个/home/.ecryptfs/user/public目录然后软链接到 $HOME 内来使用。

我同时还修改了/etc/pam.d/slim,似乎这样才能在使用 slim 登录时也有效。

呃,还没有结束呢。得把之前的/home/profile.sh文件弄进来。这里演示时只是创建了一个新的.profile文件。如果你已经有了此文件的话,一定不要将其加密,而要将其与此挂载脚本合并。它只能不加密,否则挂载后会出现两个.profile(一个加密了的,一个未加密、passthrough 来的),从而导致一些问题。

# umount /home/user
# chmod 600 /home/profile.sh
# chown user:user /home/profile.sh
# mv /home/profile.sh /home/.ecryptfs/user/private/.profile
# ln -s /home/.ecryptfs/user/private/.profile /home/user/.profile

好了,到此一切结束。

Category: Linux | Tags: linux 安全
12
18
2011
7

在 Arch 上使用 PulseAudio

一直不怎么懂关于音频的配置,所以一直没管这方面,直到遇到小麻烦。我把gnome-volumn-control干掉之后,发现音量无法调到 100% 以上了,某些视频音量太小听不清。这才第一次正视音频配置。

其实也说不上有多么「正视」,因为我只是在 ArchWiki 上找了几条命令执行了下而已。

安装了以下软件包:

  • pulseaudio-alsa, 就一个配置文件,用处不明
  • pamixer-git. 命令行调节音量用的,Awesome 音量 widget 改用这个了
  • pavucontrol, 图形界面的音量调节工具。更新后的 Awesome 音量 widget 上点右键运行它,可以针对不同的程序进行调节

另外,在 mplayer 的配置文件中加了ao=pulse这行。

就这些了。

Category: Linux | Tags: Arch linux awesome 音频
12
5
2011
13

Awesome 调节音量不再依赖 GNOME

之前一直在用 gnome-sound-applet 来调节音量。今天终于脱离了它。

GNOME 越来越臃肿了。今天系统出了点小问题,查看日志时再次看到 dbus 报怨 NetworkManager 没有运行的错误。我是直接用的 ArchLinux 的network服务连网的,根本不需要 NetworkManager 掺和,可是自己要用 Empathy,而它奇迹般地依赖 NetworkManager。。。虽然试了下强制卸载,最后只成功pacman -Rdd几个其它的包,没能在保证 Empathy 能用的前提下干掉 NetworkManager,心中多有不甘。于是开始打 gnome-sound-applet 的主意。

gnome-sound-applet 这东西是 gnome-control-center 的一部分,其依赖了 15M 左右的奇怪 GNOME 组件。这个 applet 也很鸡肋,本来是显示和调节音量用的,结果黑黑的图标在我的灰色 Awesome 面板上并不容易找到,花了良久才习惯。另外就是,该图标本身不提供任何信息,查看音量时需要把鼠标悬停过去,心中多有不爽。好在今天这些问题一并解决了。

参考来源是 Awesome Wiki 的这个页面,不过这里的代码太老了,不能用,自己参照rc.lua的其它部分以及 wiki 和 reference 修改之后才能用。最终的效果是这样子的:

Awesome widgets 截图

你应该很容易能猜到最右边的就是音量控制了,因为它前边有个八分音符符号“𝅘𝅥𝅮”。鼠标操作很简单:单击切换静音,上下滚动调节音量。静音时百分号会被红色的“M”取代。

贴代码:

-- {{{2 Volume Control
volume_cardid  = 0
volume_channel = "Master"
function volume (mode, widget)
  if mode == "update" then
    local fd = io.popen("amixer -c " .. volume_cardid .. " -- sget " .. volume_channel)
    local status = fd:read("*all")
    fd:close()

    local volume = string.match(status, "(%d?%d?%d)%%")
    volume = string.format("% 3d", volume)

    status = string.match(status, "%[(o[^%]]*)%]")

    if string.find(status, "on", 1, true) then
      volume = '𝅘𝅥𝅮' .. volume .. "%"
    else
      volume = '𝅘𝅥𝅮' .. volume .. '<span color="red">M</span>'
    end
    widget.text = volume
  elseif mode == "up" then
    io.popen("amixer -q -c " .. volume_cardid .. " sset " .. volume_channel .. " 5%+"):read("*all")
    volume("update", widget)
  elseif mode == "down" then
    io.popen("amixer -q -c " .. volume_cardid .. " sset " .. volume_channel .. " 5%-"):read("*all")
    volume("update", widget)
  else
    io.popen("amixer -c " .. volume_cardid .. " sset " .. volume_channel .. " toggle"):read("*all")
    volume("update", widget)
  end
end
volume_clock = timer({ timeout = 10 })
volume_clock:add_signal("timeout", function () volume("update", tb_volume) end)
volume_clock:start()

tb_volume = widget({ type = "textbox", name = "tb_volume", align = "right" })
tb_volume.width = 35
tb_volume:buttons(awful.util.table.join(
  awful.button({ }, 4, function () volume("up", tb_volume) end),
  awful.button({ }, 5, function () volume("down", tb_volume) end),
  awful.button({ }, 1, function () volume("mute", tb_volume) end)
))
volume("update", tb_volume)

记得把 tb_volume 加到 wibox 里去。

这里是我的整个 Awesome 配置。

2013年3月11日更新:Awesome 3.5 版本语法变化较大,请到我的 github 上查看相关代码。

Category: Linux | Tags: arch awesome gnome Lua linux
11
9
2011
14

在 Arch 上启用 NTP 服务对时

一日即将按算好的时间外出。将系统挂起,出门时检查下手机,却郁闷地发现电脑时间又慢了!十几分钟啊。。。。

我的手机是和移动运营商的网络时间同步的,因此不出事故它是可靠的。看来电脑的时钟也应该同步下了,不然过段时间要手动调下。

毫不犹豫地在ArchWiki里搜索NTP,按wiki进行操作。这wiki写了那么长,其实只要对时的话操作很简单。

  1. sudo pacman -S ntp安装
  2. sudo rc.d start ntpd启动 daemon
  3. vim一下/etc/rc.conf,加入开机启动项中,同时把hwclock干掉

就这么三步。至于同步的时间服务器,默认的pool.ntp.org就挺好的。ntpd 启动后系统时间并没有马上改变,而是过了几分钟才在我不注意的时候悄悄同步准确了。

Category: Linux | Tags: linux arch ntp
10
31
2011
16

在 Linux 下交叉编译带 Python 3 支持的 gvim.exe

今天再一次在 yaourt 的输出中瞥见 mingw 这几个字符,好奇地看了下说明:A C and C++ cross-compilers for building Windows executables on Linux。这个不就是传说中的交叉编译器么?

试试看。以前自己在虚拟机里为 Windows 编译过很多次的 vim,要是能弄到真机下来编译效率应该会高很多。(不,我不是说虚拟机的性能差,而是 Windows 下跑 mingw 这种一堆进程的东西效率差。)

说干就干,几十 M 的 mingw-gcc 及其依赖下好,git archive all|tar x -C ~tmp/vim弄份崭新的 vim 源码,把以前在 win 下用的Make_ming.mak拷过来改改,设置CROSS=yes什么的,然后开始编译!然后很快就出错了,找不到编译器i586-pc-mingw32msvc-gcc。唔,我确实没有那个东东,只有i486-mingw-gcc,去把CROSS_COMPILE这个变量改了就好。然后再 make。这一次的结局是——找不到 Python/Ruby/Lua 的头文件、找不到它们中的符号……好吧,你这交叉编译器当然找不到它们,先注释掉好了。然后重新编译,很顺利地出来个 gvim.exe 了~拿到 Windows 虚拟机里跑一下,一切正常~

好开心,第一次玩交叉编译就成功了。不过还有些小遗憾:没有那些外部脚本语言的支持。这可怎么办呢?我上哪儿找用于交叉编译的库呢?光想是没有用的,还是得尝试。用 Linux 版的库肯定不行,那就试试 Windows 版的。先拿 Python 3 支持测试。把 Windows 下安装的 Python 3 文件夹复制过来,修改下路径,再 make。很正常地,我的第一次尝试总是不成功,好在也不是大问题:Make_ming.mak里写的 include 目录不对:

ifeq ($(CROSS),no)
PYTHON3INC=-I $(PYTHON3)/include
else
PYTHON3INC=-I $(PYTHON3)/win32inc
endif

看看自己的 Python 3 目录,这里的win32inc应该是include。改改就好了 ;-)

嗯,顺利成功了!接下来,把 Windows 版的 Ruby 和 Lua 也弄过来就不用再跑到虚拟机里去编译 Windows 版的 Vim 了!嗯,NSIS 也有 Linux 版,虽然是在 AUR 里。

最后,编译好的 gvim 还是在这里

Category: Vim | Tags: vim windows 交叉编译 编译 linux
10
14
2011
9

通过命名管道进行异步通信

需求是这样子的:一个程序要提供一个IPC接口,接收异步的命令。这个接口应该尽量简单,能像/proc下的文件那样通过写入数据来通信,所以我选中了命名管道。读取命名管道很简单,像普通文件那样打开然后读取就可以了。但这样做的问题是,在没有写者的时候open会阻塞。man 2 open下找到了两个标志位:O_ASYNCO_NONBLOCK。我被排在前面的O_ASYNC骗了,它只是读写时使用信号进行异步操作,open依旧阻塞。继续向后翻,才看到O_NONBLOCK,还特意注明了Neither the open() nor any subsequent operations on the file descriptor which is returned will cause the calling process to wait.

试了试,发现open并不像读写时那样在将阻塞时返回EWOULDBLOCK错误,而是返回了一个可用的文件描述符。既然文件描述符都有了,接下来自然毫无悬念地select了。完整的演示代码如下:

#!/usr/bin/env python3
# vim:fileencoding=utf-8

import os
import time
import select

fd = os.open('test', os.O_NONBLOCK | os.O_RDONLY)
while True:
  if not select.select([fd], [], [], 1)[0]:
    print('waiting...')
  else:
    got = os.read(fd, 1024).decode().rstrip()
    if not got:
      os.close(fd)
      fd = os.open('test', os.O_NONBLOCK | os.O_RDONLY)
    else:
      print('got', got)
Category: Linux | Tags: linux python fifo 异步

Mastodon | Theme: Aeros 2.0 by TheBuckmaker.com