Showing posts with label Linux. Show all posts
Showing posts with label Linux. Show all posts

Monday, May 05, 2008

perl 调用 localtime 的时区问题

测试用例:
{
local $ENV{TZ} = 'UTC+0';
print scalar localtime, "\n";
}
print scalar localtime, "\n";

@CentOS 4.2
Mon Sep 24 01:05:52 2007
Mon Sep 24 01:05:52 2007

@CentOS 4.4
Mon Sep 24 01:05:52 2007
Mon Sep 24 09:05:52 2007

Bug 的详情可以参考
http://rt.perl.org/rt3/Public/Bug/Display.html?id=26136
摘要:
* threaded perl calls localtime_r(3) instead of localtime(3)
* localtime(3) calls tzset(3), but localtime_r(3) does not

一些时间类的模块受此影响,比如 Date::Simple。

CentOS 4.4/ Red Hat Enterprise Linux 4 Update 4 已解决此问题,详情见 perl 的 changelog:
$ rpm -q --changelog perl
* Thu May 11 2006 Jason Vas Dias <jvdias@redhat.com> - 3:5.8.5-30.RHEL4

- fix errata testing issues:
o bug 161305 localtime_r_needs_tzset incorrectly not set on 64-bit arches
o generate perl headers correctly to avoid missing / unresolved headers

* Thu Apr 06 2006 Jason Vas Dias <jvdias@redhat.com> - 3:5.8.5-28.RHEL4

- fix bug 161305: call tzset() before localtime_r

而 perl 的 community patch 应用于 perl 5.8.8 发布之后,此后发布的新版本目前只有 perl 5.10.0。

综上,有以下数种对应解决办法:
1、如果 OS 为 CentOS 4.4 以下,使用 yum 直接升级 perl 即可。
2、其他 OS 可以查找是否有相应的 vendor patch。
3、自行编译安装禁用线程(-Uusethreads)的 perl。
4、自行编译安装 perl 5.10.0。
5、给受影响的模块打补丁,Date::Simple 的补丁这里有。

相关链接:
小心时区陷阱(一)
小心时区陷阱(二)

Friday, November 09, 2007

寻找被占用的硬盘空间

收到系统邮件报警,有个分区的空间使用率已经超过90%
[root@localhost ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sda7 4.9G 4.3G 267M 94% /
/dev/sda1 99M 17M 77M 18% /boot
/dev/sda2 43G 8.8G 32G 22% /data
none 1.9G 0 1.9G 0% /dev/shm
/dev/sda6 4.9G 43M 4.6G 1% /home
/dev/sda5 4.9G 1.8G 2.8G 39% /usr
/dev/sda3 4.9G 1.5G 3.2G 31% /var

[root@localhost ~]# du -h -x --max-depth=1 /
8.0K /opt
2.0K /boot
5.7M /bin
4.0K /data
0 /proc
144M /lib
8.0K /initrd
11M /etc
8.0K /mnt
0 /sys
8.0K /selinux
2.2M /tmp
42M /root
8.0K /var
8.0K /srv
16K /lost+found
17M /sbin
16K /media
8.0K /usr
0 /dev
8.0K /misc
8.0K /home
4.2G /

[root@localhost ~]# ls -al /
total 174
drwxr-xr-x 24 root root 4096 Aug 16 01:23 .
drwxr-xr-x 24 root root 4096 Aug 16 01:23 ..
-rw-r--r-- 1 root root 0 Aug 16 01:23 .autofsck
drwxr-xr-x 2 root root 4096 Aug 23 04:02 bin
drwxr-xr-x 4 root root 1024 Aug 15 19:12 boot
drwxr-xr-x 5 root root 4096 Sep 20 16:41 data
drwxr-xr-x 9 root root 6540 Aug 16 01:23 dev
drwxr-xr-x 67 root root 4096 Nov 9 14:42 etc
drwxr-xr-x 9 root root 4096 Nov 9 14:42 home
drwxr-xr-x 2 root root 4096 Feb 22 2005 initrd
drwxr-xr-x 12 root root 4096 Oct 26 04:03 lib
drwx------ 2 root root 16384 Aug 16 03:43 lost+found
drwxr-xr-x 4 root root 4096 Aug 16 01:23 media
drwxr-xr-x 2 root root 4096 May 3 2007 misc
drwxr-xr-x 2 root root 4096 Aug 15 18:48 mnt
drwxr-xr-x 2 root root 4096 Sep 20 17:07 opt
dr-xr-xr-x 420 root root 0 Aug 16 10:22 proc
drwxr-x--- 6 root root 4096 Nov 9 14:42 root
drwxr-xr-x 2 root root 12288 Aug 23 04:02 sbin
drwxr-xr-x 2 root root 4096 Aug 15 18:43 selinux
drwxr-xr-x 2 root root 4096 Feb 22 2005 srv
drwxr-xr-x 9 root root 0 Aug 16 10:22 sys
drwxrwxrwt 4 root root 4096 Nov 9 22:36 tmp
drwxr-xr-x 16 root root 4096 Aug 15 18:45 usr
drwxr-xr-x 22 root root 4096 Aug 21 16:32 var

找来找去,找不到吃掉空间的文件,还是经过好友指点,学到了这个命令:
[root@localhost~]# lsof | grep deleted
logswatch 14767 zimbra 1w REG 8,7 1830440 384006 /tmp/logswatch.out (deleted)
logswatch 14767 zimbra 2w REG 8,7 1830440 384006 /tmp/logswatch.out (deleted)
......

意为列出已被删除,但仍被进程占用中的文件。然后就是处理(杀掉?)掉相关进程,释放文件,空间就回来了。

Sunday, December 10, 2006

小心时区陷阱(二)

测试用例
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece qw//;
use Date::Simple qw//;
use POSIX qw//;

sub show_info {
my $msg = shift;
my $hour = (localtime)[2];
printf "tz=$ENV{TZ}, tzname=%s:%s, hour=%2d # $msg\n", POSIX::tzname, $hour;
}

$ENV{TZ} = 'JST-9';
print "Date::Simple version is $Date::Simple::VERSION\n";
show_info('start');
my $date = Date::Simple->new;
show_info('after new');
$date->strftime("%Y-%m-%d\n");
show_info('after strftime');
POSIX::tzset;
show_info('after tzset');


输出结果
tz=JST-9, tzname=JST:JST, hour= 8 # start
tz=JST-9, tzname=JST:JST, hour= 8 # after new
tz=JST-9, tzname=UTC:UTC, hour=23 # after strftime
tz=JST-9, tzname=JST:JST, hour= 8 # after tzset


另外,关于 $ENV{TZ},提醒一下。
假设机器的时区设置为 UTC+9,不考虑非法的时区设置,那么根据值的不同,分别会是这样的结果
1.  not exists $ENV{TZ}        UTC+9
2.1 if ( $ENV{TZ} ) {...} UTC+0
2.2 else {...} UTC+9


Patch for Date::Simple 3.02
$ diff -r Date-Simple-3.02.orig Date-Simple-3.02
diff -r Date-Simple-3.02.orig/lib/Date/Simple.pm Date-Simple-3.02/lib/Date/Simple.pm
158a159
>
160,161c161,174
< local $ENV{TZ} = 'UTC+0';
< return POSIX::strftime ($format, _gmtime ($self));
---
> my $exists_env_tz = exists $ENV{TZ};
> my $tz = $ENV{TZ} if $exists_env_tz;
>
> $ENV{TZ} = 'UTC+0';
> my $result = POSIX::strftime ($format, _gmtime ($self));
>
> if ( $exists_env_tz ) {
> $ENV{TZ} = $tz;
> } else {
> delete $ENV{TZ};
> }
> POSIX::tzset();
>
> return $result;

参考链接
rt#23875

Tuesday, December 05, 2006

小心时区陷阱(一)

不知道大家有没有碰到过这样的问题,原本好好的当前时区时间突然变为 0 时区时间。

这个问题困扰了我很久,原因有二:
1、程序不是个单文件脚本
2、单元测试无法重现错误

后来在同事的帮助下,总算找到了问题所在

localtime(3) calls tzset(3), but localtime_r(3) not.
threaded perl localtime() calls localtime_r(3) instead of localtime(3)

来看一下 Date::Simple 的一段源代码

sub format {
my ($self,$format)=@_;

$format= $fmts{refaddr($self)||''} || $fmts{ref($self)} || $Standard_Format
if @_==1;

return "$self" unless defined ($format);
require POSIX;
local $ENV{TZ} = 'UTC+0';
return POSIX::strftime ($format, _gmtime ($self));
}

POSIX::strftime() 调用 tzset() 时看到的环境变量 $ENV{TZ} 是 UTC+0。在这之后就有问题了,如果使用 localtime(),因为 localtime_r(3) 并不会调用 tzset(),所以即使环境变量 $ENV{TZ} 已经恢复原值,localtime() 查看内部变量的时候,还是之前的 UTC+0。

至于为什么 localtime_r(3) 不像 localtime(3) 一样调用 tzset(3) 根据环境变量重设时区,我理解下来的主要原因是因为,它是线程不安全的。可能会出现下面这种情况

thread1
thread2
tzset()
tzset()
localtime()
localtime()

另外,关于环境变量 $ENV{TZ}存在(exists),但其值为空的时候,也会得到 UTC+0 的结果。

相关链接:
perlbug #26136