如何在AIX中编译Perl

如何在AIX中编译Perl

AIX默认是不带编译器的,所以如果要自己编译Perl,需要先安装编译器。开源的gcc自然可以算是最佳选择。

在64位平台的AIX中,如果选择使用gcc来编译perl源代码,默认情况下是编译成32位的版本。这样在编译DBD::Oracle的时候也需要选择正确的32位库,否则无法编译成功。也可以选择将Perl编译成64位的,这样在64位平台上应该更方便些,很多依赖库的路径使用默认即可。

编译成32位Perl

$./Configure -des -Dprefix=/opt/perl  -Dcc=gcc
$make && make install

编译成64位Perl

$./Configure -des -Dprefix=/opt/perl  -Dcc='gcc -maix64'
$make && make install

注:gcc在linux下64位编译选项为-m64,在HP-UX下64位编译选项为-mlp64。不同平台下需要编译64位程序,选用不同的选项即可。为了确认编译后的版本,可以使用-V选项运行perl:

$./perl -V
Summary of my perl5 (revision 5 version 10 subversion 1) configuration:

  Platform:
    osname=aix, osvers=5.3.0.0, archname=aix-64all
    uname='aix dbtest 3 5 00cad8cf4c00 '
    config_args='-des -Dprefix=/opt/perl -Dcc=gcc -maix64'
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=define, use64bitall=define, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='gcc -maix64 -maix64', ccflags ='-D_ALL_SOURCE -D_ANSI_C_SOURCE
-D_POSIX_SOURCE -DUSE_NATIVE_DLOPEN -fno-strict-aliasing -pipe
-maix64 -DUSE_64_BIT_ALL',
    optimize='-O',
    cppflags='-D_ALL_SOURCE -D_ANSI_C_SOURCE -D_POSIX_SOURCE
-DUSE_NATIVE_DLOPEN -fno-strict-aliasing -pipe'
    ccversion='', gccversion='4.2.0', gccosandvers=''
    intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=87654321
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=8
    ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='gcc -maix64 -maix64', ldflags =' -Wl,-brtl -Wl,-bdynamic -Wl,-b64'
    libpth=/lib /usr/lib /usr/ccs/lib
    libs=-lbind -lnsl -ldbm -ldl -lld -lm -lcrypt -lc
    perllibs=-lbind -lnsl -ldl -lld -lm -lcrypt -lc
    libc=/lib/libc.a, so=a, useshrplib=false, libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_aix.xs, dlext=so, d_dlsymun=undef,
ccdlflags='-Xlinker -bE:/opt/perl/lib/5.10.1/aix-64all/CORE/perl.exp'
    cccdlflags=' ', lddlflags='  -Wl,-b64 -Wl,-bhalt:4 -Wl,
-G -Wl,-bI:$(PERL_INC)/perl.exp -Wl,
-bE:$(BASEEXT).exp -Wl,-bnoentry -lc -lm'

Characteristics of this binary (from libperl):
  Compile-time options: PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP USE_64_BIT_ALL
                        USE_64_BIT_INT USE_LARGE_FILES USE_PERLIO
  Built under aix
  Compiled at Dec  1 2009 19:48:22
  @INC:
    /opt/perl/lib/5.10.1/aix-64all
    /opt/perl/lib/5.10.1
    /opt/perl/lib/site_perl/5.10.1/aix-64all
    /opt/perl/lib/site_perl/5.10.1

如何让Perl脚本同时只运行一个实例

用Perl写了一些监控脚本,放在crontab中调度执行。有时候会发现一个脚本运行时间过长,会同时跑起多个实例,因此有必要为脚本加上控制,只运行一个实例运行。

最简单自然的想法,在脚本中检查并创建一个空的lock文件,脚本结束时再删除。通过判断文件是否存在的方式来判断脚本是否已经运行。不过这样做有个bug,如果脚本运行过程中异常终止,lock文件没有正常删除,就会导致脚本无法再运行。

空的lock文件不行,那么考虑在lock文件中加入一点内容,比如进程的PID号,然后通过检查该PID号的进程是否还在运行,就能避免上述bug了。在CPAN上有很多现成的模块能够完成上述功能,如File::LockfileFile::PidProc::PID::File 等。

下面是File::Lockfile的一个示例,非常简单:

#!/usr/bin/perl -w
use File::Lockfile;
# lock文件位于/tmp目录,名为test_file_lock.lck
my $lockfile = File::Lockfile->new('test_file_lock','/tmp');
# 检查脚本是否已经运行,如已运行则退出
if ( my $pid = $lockfile->check ) {
  print "program is already running with PID: $pid";
  exit;
}
#更新lock文件
$lockfile->write;
# 脚本逻辑
sleep 30
#删除lock文件
$lockfile->remove;

通过查看File/Lockfile.pm的源代码可以看到,判断lock文件中记录的进程是否已经运行,简单的通过kill 0,$pid即可实现。所以即使不用上述模块,自己实现也是非常容易的。

Perl自定义模块的路径包含问题

Perl模块是重用代码的好方法,但是在调用自定义模块时的路径问题困扰了我许久。之前一直都是通过在代码中直接将自定义模块所在的绝对路径写入到@INC数组来解决的,以下示例,加入perl脚本放置在/opt/perl/bin,而自定义模块放在/opt/perl/lib目录:

BEGIN {
    push (@INC,'/opt/perl/lib');
}

或者

BEGIN {
    unshift @INC,'/opt/perl/lib';
}

或者

 use lib '/opt/perl/lib';

使用绝对路径比较麻烦,如果将程序迁移到另外的安装目录,就需要去更改所有的脚本。而直接在use lib中使用相对路径,如use lib ‘../lib’;,测试发现手动执行bin目录下的perl脚本是可以的,但是放到crontab里去跑就找不到模块了。于是想先找到bin目录的路径,然后通过相对路径跳转到lib目录,在《Perl Cookbook》终于找到了我想要的,使用FindBin模块就能实现梦想了:

use FindBin qw($Bin);
use lib "$Bin/../lib";

使用Perl发送邮件

在之前的监控系统中,邮件系统配置为只能本地调用mail命令发送,这样如果要在每台数据库服务上发送邮件就比较麻烦,需要通过ssh远程调用邮件服务器上的发送脚本。如果要发送文件,还要先将文件复制到邮件服务器上。

随着机器数量的增加,这种方式越来越不灵活。因此希望能通过传统的smtp的方式在任何主机上都能发送邮件,于是花了点时间研究了一下sendmail,根据这篇文章很轻松的让sendmail跑了起来:

修改sendmail配置模板/etc/mail/sendmail.mc

define(QUEUE_DIR, `/var/spool/mqueue/q*')dnl
TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
dnl DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl
DAEMON_OPTIONS(`Port=25, Name=MTA')dnl

第一行是新增,支持多个队列
第二三行去掉注释
第四行加上注释
第五行新增

根据模板生成配置文件

m4 /etc/mail/sendmail.mc > /etc/sendmail.cf

建立多个队列的目录

cd /var/spool/mqueue/
mkdir q1 q2 q3 q4 q5 q6

启动sendmail

/etc/rc.d/init.d/sendmail start

若要随机自启动sendmail,修改一下chkconfig设置即可

chkconfig --level 35 sendmail on

测试了一下发送邮件成功:

mail -s "test mail" NinGoo@test.com < /tmp/test.log

CPAN上搜索了一下,Mail:Sender模块发送邮件比较方便,支持多附件,语法也比较简单。需要安装如下两个组件:

MIME::Base64
Mail::Sender

发送邮件的perl示例代码如下,为便于查看邮件发送的详细情况,这里开启了debug模式:

#!/usr/bin/perl -w
use Mail::Sender;
open my $DEBUG, ">> /tmp/mail.log" or die "Can't open the debug file: $!\n";
$sender = new Mail::Sender
 {smtp => 'smtp_server',
   from => 'send@mail.com'};
#发送普通邮件
$sender->MailMsg(
 {to => 'NinGoo@test.com',
 subject => 'test mail',
 msg => 'hello word',
 debug => $DEBUG
 });
#发送带附件的邮件
$sender->MailFile(
 {to => 'NinGoo@test.com',
 subject => 'test mail with attached file',
 msg => 'hello word',
 file => '/tmp/test.txt'
 debug => $DEBUG
 });

经过测试,在邮件服务器本机发送成功,但在远程主机上则失败了,通过debug的log文件,发现错误如下:

>> 550 5.7.1 < NinGoo@test.com>... Relaying denied. Proper authentication required.

因为是内部监控使用,可以配置远程发送到特定的域的邮件无须认证,修改/etc/mail/relay-domains文件,每行加入一个目标域,如taobao.com。

修改配置后注意重启sendmail

/etc/rc.d/init.d/sendmail restart

发送正常的log如下:

>> 250 2.0.0 n69DbJwt011946 Message accepted for delivery