Linux Application

git 技巧两则

今天有同事问git如何查被删除的文件记录,以前遇到过不记得了,回去查了查man手册。

找被删除的文件记录:git log —diff-filter=D

找被重命名的文件记录:git log -M 或者 git log —follow new_file_name

记录一下。

即将到来的一本书

Linux程序员有福气了!据内部消息透漏,明年上半年我们将会见到又一本砖头厚度的编程书!:)

呵呵,这本书目前甚至还没名字,只知道是关于Linux系统编程的,我真没开玩笑,确实如此,其作者Michael Kerrisk(Linux man page的维护者)提前透漏了这一消息。因为我给man page提交过补丁的缘故,所以也收到他的邮件了。

Michael花了几年的时间写的这本书,共有64章,大约1500页!书的内容简介可以在这里看到,64章目录可以在这里看到。各个章节的目录也可以在其博客的其它文章中找到。从他列出的目录来看,这本书覆盖面是非常全面的,IPC,Socket,Pthread等等,连Robert Love的那个《Linux System Programming》都比不上这个。感觉此书和我曾经推荐过的那本《Unix Systems Programming: Communication, Concurrency, and Threads》有些相似,不过比它还要详细,更重要的是这本书是完全关于Linux的。

此书的目标是既要是一本教程式的入门书,又要是一本手册式的参考书,要做到这点着实不容易。

我们天天读man page(注:并不是所有的man page都是由Michael提供的,不少是安装一个软件时安装上的,这里当然不包括那些),从中我们可以深刻体会到里面用词的精炼性。如果此书的措辞也可以达到那种水平,那无疑将会是另一个大的看点。

另,既然这本书还没有出版,如果你有什么主意或者建议,都可以直接给Michael发邮件告诉他。

期待此书!

命令行下获取page size

用C语言就不说了,很简单。问题是怎么用shell直接得到?我自己想了想,然后又搜了搜,发现了下面这四种方法:

/usr/bin/time -f %Z true

LD_SHOW_AUXV=1 /bin/true | grep PAGESZ

grep ^KernelPageSize /proc/self/smaps

python -c ‘import resource; print resource.getpagesize();’

前两个是google到的,后两个是我自己弄出来的。

我个人觉得最靠谱的是第2个,因为它既不需要依赖python或者perl,而且是从内核获取的。第1个很让人惊讶,因为打死我我也不会想到time这个命令下居然还有获取page size的功能。。。注意,这个time是外置的命令,而非shell内置的那个time。

准备换用docbook

最近在写《C语言编程艺术》时越来越对office感觉不满了,看着那排版是越来越丑了……想了半天决定换docbook,其实以前是想换LaTeX的,但是我这的中文环境一直没有搭好,在笔记本上还得重新弄,挺麻烦的,算了。换docbook带来的另一个好处是可以通过版本控制系统来发布了。虽然工作量大,但还是值得的。慢慢来吧。

下面是我在学习docbook的过程中发现的一些不错的教程,和大家分享:

DocBook: The Definitive Guide
DocBook XSL: The Complete Guide
DocBook 文件写作入门
Docbook 指南
DocBook 学习

还有就是关于图形界面的docbook编辑器,这里有一个列表,我试了几个,发现conglomerate还不错,挺简洁的;Quanta+和XXE比较庞大,很多东西不知道怎么用。。。

继续学习……

emacs PK vi

Harley Hahn的《Unix Unbound》一书中有介绍vi和emacs的章节。其中介绍vi的一章中提到:

HINT: If you are learning vi and you become temporarily
discouraged, take a break and try a little emacs. emacs
will seem so complex and impossible that you will feel a
lot better about using vi.
介绍emacs的一章中提到:
HINT: If you are learning emacs and you become temporarily
discouraged, take a break and try a little vi. vi
will seem so complex and impossible that you will feel a
lot better about using emacs.

我:囧。。。。。。。。。

Linux kernel ASCII art

Linux内核源代码里有两个比较好玩儿的ASCII art,一个sparc的panic里(见arch/sparc/kernel/traps_32.c),另一个在parisc的panic里(见arch/parisc/kernel/traps.c)。

我想拿来用用,放到shell里,就把它们改成了shell语句:
[bash]
echo -e ‘ |/ _ |/n “@’”‘“‘/,. `@”n /| \/ |\n __U/n’

echo -e “ \ ^^n \ (xx)___n (__)\ )\/\””
U ||——w |n || ||n”
[/bash]

这样每次启动shell我就可以看到这个图像了,见我的截图:


sparse 的一个 bug

今天用sparse的时候遇到一个bug:

error: unable to open ‘stddef.h’

简单地搜了搜,发现debian上已经有人报告,最新的sparse的git树里已经解决了这个问题,可是Fedora还没有。于是只好自己编译,git-clone之后 make && make install PREFIX=/usr 就可以了。希望Fedora也尽快更新sparse。

解决 Fedora 10 键盘问题

升级到Fedora 10后遇到一个很奇怪的问题:敲Page Up键会输入/,而Page Down键,End键和四个方向键全不能用了。很是诡异,以前从来没遇到过,而且Fedora 9时还是好好的呢。

我找了半天,最后发现,我虽然已经到Fedora 10了,但xorg-x11-drv-evdev包居然还是FC9的!而且还有两个不同版本的!!那很明显,问题应该就在这里了。直接升级它,不行,因为FC9的一个比FC10的还新。于是只好卸载了再装新的,可是因为依赖性关系,要卸载得关联一堆,怎么办呢?强制安装新的,然后再卸载旧的:

sudo rpm —force -ivh Download/xorg-x11-drv-evdev-2.0.7-3.fc10.i386.rpm
sudo rpm -e xorg-x11-drv-evdev-2.0.8-1.fc9.i386

这样就搞定了。

BTW,我在安装这个之前还修改了xorg,把keyborad对应的那部分中的Driver改成:

Driver “evdev”

貌似这似乎也是必须的,我不确定。

Linux下的console和terminal

consoleterminal是很容易让人迷惑的两个概念。根据wikipedia上的定义,小型计算机的console应该就是键盘加显示器;而terminal则是输入数据进去,和显示数据来源的设备,通常是一个计算机系统。

Linux下的console除了真实的硬件设备外,还有virtual console,也就是你按alt+Fn或者alt+ctrl+Fn切换到的东西。所谓虚拟就是这些console共享同一个真实的设备,只有一个活动的console才显示在前面。这些console对应的设备是:/dev/ttyN,其中1 ≤ N ≤ 63。而/dev/tty0则是指向当前的terminal;/dev/console是指向当前console,但它现在并不是对/dev/tty0的符号链接。更多可参考console(4)。

/dev/tty是另一个特殊设备,它指向控制终端(controlling terminal)。如果某个进程的控制终端是/dev/tty3,那么/dev/tty就指向/dev/tty3了。控制终端是什么概念?它是一个进程的某个属性,是依附带该进程上的终端。比如我们在某个终端下输入ctrl+C,那么它控制的前台进程就会收到SIGINT,而后台进程会收到SIGTTIN或SIGTTOU ,如果它们读写该终端的话。被同一个终端控制的所有进程被称为一个会话(session),会话的领导就是创建改会话的进程,其子进程也会被该终端控制。所以,1) 需要交互的命令行程序通常会从/dev/tty这个设备进行读写;2) Unix后台进程都需要在fork之后调用setsid(2),3) 需要加O_NOCTTY,当你open一个可能是终端的文件时。

另外,想要确定/dev/tty究竟是指向哪个设备,可以调用TIOCCONS ioctl。参考tty(4)。

下面是另外一个概念——伪终端(pseudo-terminal),根据pty(7)的介绍,伪终端一对虚拟设备,提供端到端双向通信的通路,一端称为master,另一端称为slave。在slave那端看到的和在真实终端看到的效果一样。所以伪终端一般被ssh等网络登录程序使用。历史上,有两套伪终端接口,一个是Unix 98伪终端,另一个是BSD伪终端。

BSD提供的接口很简单:/dev/pty[p-za-e][0-9a-f] 是master; /dev/tty[p-za-e][0-9a-f] 是slave,它们都是配好对的。这样看起来很简单,但对程序员来说不容易,要找到一个合适的终端需要一个个从头尝试。所以这种方式已经被遗弃。而Unix 98伪终端则完全不同,它始终使用/dev/ptmx作为master复制设备,然后在每次打开它的时候才得到一个master设备的fd,同时在/dev/pts/目录下得到一个slave设备。这样编程就相对容易了,根据pts(4)介绍,需要三个新的API: ptsname(3),grantpt(3)和unlockpt(3)。我们可以通过一个实例看一下如何使用:

(以下代码摘自netvirt)
[c]
char mptname = “/dev/ptmx”; / master pseudo-tty device */
//…
void
getmaster()
{
struct stat stb;

if ((master = open(mptname, O_RDWR)) >= 0) { /* a pseudo-tty is free */
    (void) ioctl(0, TCGETS, (char *)&b);
    (void) ioctl(0, TIOCGWINSZ, (char *)&size);
    return;
} else {                /* out of pseudo-tty's */
    perror(mptname);
    fprintf(stderr, gettext("Out of pseudo-tty'sn"));
    fail();
}

}

void
getslave()
{
char slavename; / name of slave pseudo-tty */

grantpt(master);        /* change permissions of slave */
unlockpt(master);            /* unlock slave */
slavename = ptsname(master);        /* get name of slave */
slave = open(slavename, O_RDWR);    /* open slave */
if (slave < 0) {            /* error on opening slave */
    perror(slavename);
    fail();
}
ioctl(slave, I_PUSH, "ptem");    /* push pt hw emulation module */
ioctl(slave, I_PUSH, "ldterm");        /* push line discipline */

(void) ioctl(slave, TCSETSF, (char *)&b);
(void) ioctl(slave, TIOCSWINSZ, (char *)&size);

}
[/c]
然后我们再来看一下glibc中对ptsname(3)的实现:

(源文件sysdeps/unix/sysv/linux/ptsname.c)
[c]

define _PATH_DEVPTS "/dev/pts/"

char *
ptsname (int fd)
{
return __ptsname_r (fd, buffer, sizeof (buffer)) != 0 ? NULL : buffer;
}

int
__ptsname_r (int fd, char *buf, size_t buflen)
{

if (ioctl (fd, TIOCGPTN, &ptyno) == 0)

numbuf[sizeof (numbuf) - 1] = '';
p = _itoa_word (ptyno, &numbuf[sizeof (numbuf) - 1], 10, 0);

memcpy (
stpcpy (buf, devpts), p, &numbuf[sizeof (numbuf)] - p);
[/c]

我们可以看出,实际上是调用ioctl TIOCGPTN,通过内核,而Linux内核又是通过devpts这种文件系统实现了这一切:

$ mount | grep devpts
devpts on /dev/pts type devpts (rw,gid=5,mode=620)

这样我们终于把一切搞清楚了。:-)

fakeroot vs sudo

1. sudo是一个setuid程序,而fakeroot不是。

2. sudo真正地给与root权限,而fakeroot不会,它只会欺骗程序看起来像是以root身份执行得到的效果。

3. fakeroot最初是为了创建debian包使用的,Fedora上也有fakeroot包,但不知为何找不到其man手册……

4. fakeroot的文档中提到:

fakeroot works by replacing the file manipulation library functions
(chmod(2), stat(2) etc.) by ones that simulate the effect the real
library functions would have had, had the user really been root. These
wrapper functions are in a shared library /usr/lib/libfakeroot.so*
which is loaded through the LD_PRELOAD mechanism of the dynamic loader.
[…]
Internally, faked keeps track of all files which have had operations
done on them that the user would not normally be able to do, in order to
fake the effect later.

For example, if you create a device node, faked will actually create an
empty file, but remember that it was a device node (along with the
relevant details) so that if you subsequently do an “ls -l”, you’ll see
it as a device node.
所以fakeroot并不会对静态链接的程序起作用。

5. fakeroot使用了很多trick,不信的话查看其源代码(看看里面那个awk脚本!)。