由于守护进程在后台运行,为系统或用户提供某种服务,因此通常只需要一个运行实例就可以了,而且在大部分情况下只能有一个实例在运行。例如cron进程,若有多个实例在运行,那么各个实例都会根据crontab执行一份用户指定的任务,岂不是乱了套了?还有其他很多守护进程是设备相关的,而这些设备有很可能是非共享的,所以这样的守护进程也不能运行多个。
  文件锁和记录锁机制是一种实现守护进程单例运行的方法。如果每一个守护进程创建一个文件,并且在整个文件上加上一把锁,那就只允许创建一把这样的写锁,所以在此之后试图再创建一把这样的写锁就会失败,以此向后续守护进程的副本指明已经存在一个正在运行的副本了。守护进程终止时,这把锁将被自动删除。

1
2
3
4
5
6
7
8
9
10
int
lockfile(int fd) //~ try to lock the file, affecting errno when it fails.
{
    struct flock fk;
    fk.l_type = F_WRLCK;
    fk.l_start = 0;
    fk.l_whence = SEEK_SET;
    fk.l_len = 0;
    return (fcntl(fd, F_SETLK, &fk));
}
Tags: .

See this,

1
2
3
4
5
6
7
8
9
10
int
main()
{
    int x = -1,
        y = -1,
        z = 0;
    printf("%d\n", ++x || ++y && ++z);
    printf("%d, %d, %d\n", x, y, z);
    return 0;
}

  别说我无聊,没人(正常人)会写出这样的式子,但有些细节知道总比不知道的好。
  &&比||优先级高。
  但优先级和求值顺序是两码事。
  ||和&&的求值顺序都是从左到右。其它运算符只有三目运算和逗号运算符有特定顺序,而+ – * /等的操作数均无特定的求值顺序,即f(x) + g(x++)中那个函数首先被调用是未定义的。顺便提一句,函数的参数的求值顺序与其入栈顺序也是无关的,所以f(i++, i++, i++)的行为也是未知的。

Tags: .

  上一篇简要介绍了守护进程,现在就来实践一下,写一个自动关机的小程序。该程序可以守护进程的方式运行,当用户在一定时间(比如30分钟)没有鼠标和键盘操作后就会自动关机。
  这个程序利用了上篇文章中实现的daemonize函数,为程序创建了守护进程所需要的运行环境。
  由于需要同时监听鼠标和键盘操作,所以需要采用多线程的方式来实现。其中两个线程分别监视鼠标和键盘,一旦检测到相应动作(鼠标点击和移动、击键等),全局时间戳stamp(time_t)就会被设成当前时间。主线程每隔一定时间(比如1秒)检查stamp,若当前时间值(time(NULL))比stamp大30*60,则执行停机操作(使用system函数执行init 0命令,或者使用reboot函数)。
  需要说明的是,共享变量stamp需要互斥地访问。另外,对鼠标事件的监听是借助于对设备文件/dev/input/mice的读取(阻塞方式),键盘的监听借助于对/dev/input/event3的阻塞读取,但我猜想在不同机器上可能会是其它诸如event0,event5之类的文件。
  不足之处在于,无法对全屏模式进行判断,即是说,如果你全屏看一部较长的电影,可能会被关机……
  如果你有好的方法来实现本文的功能,还请不吝赐教!

Tags: ,.

  守护进程(daemon process,又称精灵进程),是一种运行在后台,不需要与用户进行交互的程序,其特性与Windows中的服务类似。Linux/Unix系统中运行着很多这样的进程,且很多此类进程的进程名都采用named的形式,比如httpd, vsftpd, inetd等。但这只是一种命名惯例,你完全可以采用其它的命名形式。

守护进程的特性

  守护进程有种种特性,使之成为守护进程。
  运行周期较长。守护进程通常在系统启动时由/etc/rc*.d中的启动脚本或者用户在终端手动启动,在系统结束时停止或由用户手动停止。不像普通进程那样在用户退出终端后就被停止,守护进程会一直运行(下面会看到守护进程在启动后会脱离执行它的终端)。
  脱离其运行环境。守护进程没有控制终端。进程在创建时(fork)会继承父进程的PCB(进程控制块),因此会同时拥有父进程的许多资源和关系,比如打开的文件描述符、socket、环境变量、控制终端、进程组、登录会话等。而守护进程需要关闭这些资源、脱离这些关系。

Tags: ,.

  正常情况下,在终端编译执行该多线程程序,首先打印n(0),5秒后再次打印n(1),然后该进程正常退出。
  但,如果使用gcc编译此程序时,使用优化选项(比如-O1),再执行此程序。首先打印n(0),5秒后再次打印n(1),然后呢,然后就死循环啦!
  为什么呢?看一看汇编代码就知道了,使用gcc -O1 -S命令编译第一段代码,得到的汇编码:

1
2
3
4
5
6
7
main:
    movl    n(%rip), %eax #从内存中取n值至寄存器eax
.L3:
    testl   %eax, %eax # 与测试eax
    je  .L3 # eax0时跳转至标号L3处
    movl    $0, %eax
    ret

  真相大白了吧,那该怎么办?对,volatile,参看这里

Tags: ,.

  const在C,尤其是C++,是个老生常谈的问题,但这里不谈const具体有哪些特性,如何使用,而是说说const在C和C++中的区别。编译器,C使用gcc,C++使用g++,其它编译器(cl等)请自行验证。
  在我的印象中,const就是常量constant)。但这并不是真的。在C中,const仅仅表示其所修饰对象不可修改。常量和所谓“不可修改”有什么区别呢?C中,const int N = 10; 这样的语句声明了一个整型数据,声明之后你不能再为其赋值,此即为不可修改。但在C中,N却不是常量,而诸如372, 3.72, ‘A’之类才是常量,在C中定义常量通常使用#define。而在C++中,const int N = 10; 就会定义N为常量。
  怎么证明呢?你可能知道,定义静态数组,必须使用整型常量指定其大小,那咱们就用这个特性来验证上面描述的观点。

1
2
3
4
5
6
7
int
main(int argc, char **argv)
{
    const int N = 10;
    int a[N] = { 1, 2, 3 };
    return 0;
}
Tags: .

volatile

  可能很多人都没用过C/C++中的这个关键词,甚至不知道它的存在,本人以前也只是有所耳闻,但似懂非懂。
  这是一个类型修饰符,位置同conststatic等。一个使用volatile修饰的变量,比如volatile int i; 每次对该变量的直接引用,都会访问内存,而不是从寄存器中读取(如果其已经在寄存器中)。这样一来,volatile似乎没什么用处,反倒会使数据的读取相对变慢很多。但是,如果没有volatile,编译器可能会优化你的程序,使得数据从寄存器中读取,从而加快程序的运行,但如果这个变量是同其它进程/线程共享的,就可能造成数据的不一致。多线程情况下,你可以使用互斥机制来保证对共享数据访问的原子性。但是,在单片机等嵌入式环境中,硬件经常不会有这种互斥机制的支持,这时某些共享的数据(比如端口)就可能会产生不一致的情况。而使用volatile就会使编译器不对代码进行优化,每次对该变量的访问都会从内存中读取。
  下面通过观察使用volatile前后编译产生的汇编代码的不同,来加深对volatile关键词的理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[ ... ]
main:
[ ... ]
	call	foo # 调用函数foo,返回值保存至寄存器eax
	movl	%eax, 44(%esp) # 为i赋值
	movl	44(%esp), %edx # 读取i值
	movl	44(%esp), %ecx # 读取i值
	movl	44(%esp), %eax # 读取i值
	movl	%ecx, 16(%esp) # 参数k入栈,使用i值
	movl	%edx, 12(%esp) # 参数j入栈,使用i值
	movl	%eax, 8(%esp) # 参数i入栈
	movl	$.LC0, 4(%esp) # 格式字符串地址入栈
	call	__printf_chk
	movl	$0, %eax
	leave
	ret
[ ... ]
Tags: ,.

  现在再推荐一个类似的工具集Gow(GNU On Windows). 这是一个轻量级的Cygwin“替代品”,集成了130个Linux命令,可以由Windows Installer方便地安装,自动设定环境变量。同时,自动为Windows Explorer的右键菜单添加一个“open in cmd prompt”的菜单项。里面有特色的工具主要有:

  • 脚本解释器:bash, zsh ;
  • 归档、压缩工具:tar, gzip, bzip2等;
  • putty工具集: putty, psftp, pscp, plink等;
  • 下载工具:cURL, wget;
  • 编辑器:Vim;
  • 文本搜索/查看工具:grep, cat, less, head, tail等;
  • 文件(系统)相关工具:mv, cp, ls, rm, pwd等;

  另外,你可能注意到,Gow中并没有gcc(GNU Compiler Collections),所以,如果你需要这些工具,你可能还需要安装上面的cygnus。需要注意的是,这两个工具集的命令有交集,所以同时安装这两个工具集时要注意PATH中搜索路径的顺序,我更倾向于把Gow的bin放在前面。
  最后,如果你系统中已经装有Vim(通常为较高的版本),最好把Gow中提供的Vim(貌似是6.3)删除。

Tags: ,.

chmod命令介绍

  下面介绍如何使用chmod命令来设定和修改文件的权限位。
  首先,chmod命令本身的执行也有限制,普通用户只能修改自己的文件的权限位,超级用户可以使用chmod修改任意用户任意文件的权限。使用chmod的方法为chmod [option] mode file. 常用的选项就是-R,用来对整个目录及其子目录中的文件进行模式(权限)修改。mode可以由两种方式指定,一种是由字母表达式表示的相对修改方式,一种是以4位8进制表示的绝对方式。
  第一种方式:chmod [ugoa][+-=][rwxst] file. u指代user,g指代group,o指代other,a指代all。+-=分别执行指代增加、减少、设定相应权限(由后面的参数指定)。rwxst分别指代读、写、执行、set-user-id/set-group-id、sticky-bit粘滞位。整体上理解,就是对哪类用户(ugoa,为a时可省略)执行哪些权限(rwxst)的什么修改(+-=)。示例:

1
2
3
4
5
6
7
8
9
10
11
12
$ chmod u-x a.out 
$ ll a.out
-rw-r-xr-x 1 dutor dutor 7.1K 2010-08-08 13:33 a.out
$ chmod +t a.out 
$ ll a.out
-rw-r-xr-t 1 dutor dutor 7.1K 2010-08-08 13:33 a.out
$ chmod g=rs a.out 
$ ll a.out
-rw-r-Sr-t 1 dutor dutor 7.1K 2010-08-08 13:33 a.out
$ chmod a+x a.out 
$ ll a.out
-rwxr-sr-t 1 dutor dutor 7.1K 2010-08-08 13:33 a.out
Tags: ,,.

  这款插件叫Mumbless,思路来源于我以前使用的插件block-spam-by-math。刚刚写好,可能还不太完善,但以最近一周的使用来看,还未出现自动的垃圾留言,或许因为小众吧。如果你需要,可以点击此处下载Mumbless.zip。安装方法同其它插件。
  为了更有效地利用这款小插件,这里提供些建议:

  • Mumless.php文件中Mumbless类中的add_check_form函数中的keywords数组包含了候选的验证码,你可以根据自己的喜好增删改;
  • 验证码输入框可能会影响原有表单的tabindex顺序,你可以根据需要调整;
  • 验证码图片的大小、样式在keyimg.php文件中定义,必要时你也可以对某些参数进行修改。

  希望这个插件能为你节省些许删除垃圾评论的时间。

Tags: .