2011年3月4日 星期五

[Python] 判斷 function 是否為 thread-safe & 查看 bytecode

許青蛙問了我一個問題:「Python 的 list 是不是 thread-safe?」當下我先回顧一下 wiki 上的說法,因為很多東西我都忘了專業名詞 XD 我腦中頂多可以拼湊出 race condition,以及我總覺得 thread-safe 是要跟用法有關 XDD 看來我學習還真的很迷糊,還隨便找了個 deque 呼嚨一下 XDD 最後還是請教了隔壁 team 的 Python 高手。


得知重要訊息:


Python 底部用 bytecode 在執行的,因此可以去確認某個指令是不是一個 bytecode 指令。


而原先許青蛙的問題是在於 list extend 的用法,打算開多個 thread 去新增資料到一個 list 中,接著高手馬上就 demo 給我看:


>>> import dis
>>> dis.dis( "a=[] ; a.extend([])")
          0 STORE_GLOBAL    23357 (23357)
          3 FOR_ITER        15136 (to 15142)
          6 SLICE+2        
          7 STORE_GLOBAL    25902 (25902)
         10 SETUP_LOOP      25972 (to 25985)
         13 JUMP_FORWARD    10340 (to 10356)
         16 DELETE_NAME     10589 (10589)
>>> 


由此可得知 extend 被展開成數行 bytecode ,所以有 race condition 的機會。最後高手也點了我一下,在使用 threading 時可以多作一點設定,細節就請我去看這篇: Understanding Threading in Python


2011年3月3日 星期四

[電影] Every Child is Special 心中的小星星


上週末才看完 3 idiots,而我同事幾天前看完三個傻瓜後,就在網路上訂購了該導演前一部戲 - 心中的小星星。更多的電影資訊:Yahoo! 電影 - 心中的小星星。感謝同事的分享,這部片主要在講述著學生學習上碰到問題的故事,一樣令人感動。


原先看了前半段,因為看過 3 idiots 而感到拍攝的模是很相像冗長而感到有些小小的厭倦,但是到了後半段後,才讓我想起應該要多看看這部電影想表達的情感在哪邊,漸漸地越來越融入劇情。看了這部片也讓我回想起以前的事情,記得國中進入了一所競爭性高的學校,每次段考都可以看到大家的全校排名,當時全校約 650 人,我的成績約 450 左右,邁入國二後,因為物裡的上手,讓我的成績進到 150 名左右,也因為單科成績的進步才讓我想把其他科也學好,在這之前我大概只想回家玩 PC 或 TV Game 罷了。回想起來,自己的學習幸運很多,因為找到一個可以展現的科目,所以帶動了自己的學習。而這部電影也有從類似的角度著手,讓學生先享受單一科目的成功,進而激勵自己學習其他項目,不見得每科都要學好,但一定不要隨意放棄。


這部是印度電影,其中提到了許多學習上的競爭與目標,說穿了跟台灣很相近,印度人口跟密度都不低的!回想起過去唸書的經驗,國高中的學校大部分都只在意升學率,就連與學生最近的輔導室也是。我那年是新制度的聯考,我與一位同學分數很近,我因為新制度而可以進國立大學,另一位則是私立大學,當時一直被輔導老師說嘴,或許他的本意不是,但我還滿反感啦,另外,學測成績出爐後,一股腦兒地叫學生們趕緊申請或甄試上學校,這也覺得很不妥當,感覺都只是在拼升學率?殊不知甄試或申請上的人,沒有轉系的權利,又誰知道哪個科系是自己所喜歡的呢?就連畢業後幾年,回去跟導師閒聊,發現他們還是一直認為有竹科新貴,很容易發達等等的想法。也不能說他們完全錯誤,畢竟這就是生活經驗與價值觀吧。


呼,教育啊,真的很重要的,有時覺得照著前人腳步不見的是對的卻又別無選擇,但仔細經歷過後,就會想起那哲理:「見山是山,見水是水;見山不是山,見水不是水;見山仍是山,見水仍是水。」。有時也不必過於追求,生命自會找到出路,此刻的不完美也會增加這社會的亂度,成就社會容錯的能力呢,不也是種完美嗎!


希望大家都能碰到有緣人或是當別人的有緣人,無論多小的提攜也好,讓生活更加積極有活力!


[Unix] 簡易分析 Squid 產生的 log @ FreeBSD

squid_log


使用 awk 跟 bash 寫了一隻簡單的 script ,用來分析 squid 產生的 log。不過也發現網路上還滿多分析的工具,但我只需要在 cmd mode 下呈現簡單的資訊,如某個 IP 連線次數、起始時間、最後使用時間,這樣的資訊而以,最後就想到用 awk 能很方便處理,因為 squid log 得格式很簡單,更正確來說,在我的需求面上都很簡單 XD 只要透過 awk 建一個 hash 來記錄 IP 出現的次數、使用時間等,就完成收工了,如:awk '{ if( ip_hash[$3]) ip_hash[$3]++ ; else ip_hash[$3] = 1; } END { for( ip in ip_hash ) print ip , ip_hash[ip]; }' access.log。然而,就突然想要複習一下 bash 提供多一點參數,不小心就跌入萬丈深淵啦,說真的乾脆直接用 Perl、Python 做就好了!不過某總管長輩說,在 FreeBSD 系統預設是沒有裝那種東西的,所以 bash 還是佔有一席之地啊。此程式僅適用於 FreeBSD 系統吧?至少 Ubuntu 因為 date 指令不適用,其他相關筆記:[Unix] 透過指令進行 date 與 timestamp 轉換 @ FreeBSD[Unix] awk 與 bash script 的使用 @ FreeBSD


此程式接受 3 個參數,必要的 access.log 檔案,兩個次要的參數是 -date 跟 -range ,其中 -date 是指定要從此刻時間分析,而 -range 是與起始時間相距的秒數,可用來限定最多分析到哪個時間點,例如:



  • 分析全部記錄:

    • # sh slog.sh access.log



  • 分析 2011-03-01 起的記錄:

    • # sh slog.sh -date "2011-03-01 00:00:00" access.log



  • 分析 2011-03-01 到 2011-03-02 之間的記錄:

    • # sh slog.sh -date "2011-03-01 00:00:00" -range 86400 access.log




冗長的程式碼:


#!/bin/bash


if [ $# -lt 1 ]; then
echo "Usage> $0 [-date|-range] file"
echo " -date  \"2011-02-14 00:30:40\""
echo " -range \"86400\""
echo
echo "Other Info:"
echo " # date -j -f \"%Y-%m-%d %H:%M:%S\" \"2011-02-14 00:30:40\" \"+%s\""
exit
fi

#date_begin
#date_range
#date_end
#target_file

while [ -n "$1" ]
do
if [ "$1" = "-date" ]; then
shift
if [ -z "$1" ]; then
echo "ERROR: -date BeginDate "
exit
fi
date_begin=$1
elif [ "$1" = "-range" ]; then
shift
if [ -z "$1" ]; then
echo "ERROR: -range Offset"
exit
fi
date_range=$1
else
target_file=$1
fi
shift
done
#
# checking
#
if [ -n "$date_begin" ]; then
echo "[SYS] Data Begin Input: $date_begin"
date_begin=`date -j -f "%Y-%m-%d %H:%M:%S" "$date_begin" "+%s"`
echo "[SYS] Data Begin Timestamp: $date_begin"
fi
if [ -n "$date_range" ]; then
echo "[SYS] Data Range Input: $date_range"
date_end=$(($date_begin+$date_range))
echo "[SYS] Date Begin: $date_begin"
echo "[SYS] Date Finsh: $date_end"
fi
if [ -z "$target_file" ]; then
echo "ERROR: No file"
exit
fi
if [ -f "$target_file" ]; then
else
echo "ERROR: '$target_file' file not exists."
exit
fi

#
# awk profile
#
awk_profile="BEGIN{"
if [ -n "$date_begin" ]; then
awk_profile=$awk_profile"begin_timestamp=$date_begin;"
else
awk_profile=$awk_profile"begin_timestamp=-1;"
fi
if [ -n "$date_end" ]; then
awk_profile=$awk_profile"end_timestamp=$date_end;"
else
awk_profile=$awk_profile"end_timestamp=-1;"
fi


awk_profile=$awk_profile"}\
{\
record_time=\$1;\
record_ip=\$3;\
if((begin_timestamp==-1||record_time>=begin_timestamp)&&(end_timestamp==-1||record_time<=end_timestamp))\
{\
if(ip_hash[record_ip])\
{\
ip_hash[record_ip]++;\
time_end[record_ip]=int(record_time);\
}\
else\
{\
ip_hash[record_ip]=1;\
                        time_begin[record_ip]=int(record_time);\
                        time_end[record_ip]=int(record_time);\
}\
}\
}\
END\
{\
printf \"%20s %10s %30s %30s\\n\",\"IP\",\"Count\",\"Begin Time\",\"End Time\";\
for(ip in ip_hash)\
{\
                record_time_begin = 0 ; record_time_end = 0 ;\
\"date -j -f \\\"%s\\\" \" time_begin[ip] | getline record_time_begin; close(\"date -j -f \\\"%s\\\" \" time_begin[ip]); \
\"date -j -f \\\"%s\\\" \" time_end[ip] | getline record_time_end; close(\"date -j -f \\\"%s\\\" \" time_end[ip]); \
printf \"%20s,%10d,%30s,%30s\\n\",ip,ip_hash[ip],record_time_begin,record_time_end;\
}\
}"


awk_profile_path=`mktemp -t squid.awk.profile` || exit
awk_output_path=`mktemp -t squid.awk.output` || exit
echo "[SYS] Create awk profile: $awk_profile_path"
echo $awk_profile > $awk_profile_path


#cmd="awk '$awk_profile' \"$target_file\""
cmd="awk -f $awk_profile_path $target_file"
echo "[SYS] Execute: $cmd"
echo "[SYS] Save: $awk_output_path"
`$cmd > $awk_output_path` || exit
echo "[Result]"
cat $awk_output_path
echo 
echo "[SYS] Remove logs: rm $awk_profile_path $awk_output_path"


rm $awk_profile_path $awk_output_path


[Unix] 透過指令進行 date 與 timestamp 轉換 @ FreeBSD

記得每次要把日期轉成 timestamp 或是把 timestampe 轉成看得懂的日期時,這時候都是跑去 Google timestamp wiki,接著從那邊找到 web service ,直接跑去用查詢的,而最勤勞的情況就是寫 PHP 去轉 XD 


最近我忍不住了,先 man date 一下,但還是看不出個所以然,於是跑去問 Randy 總管,他馬上跟我說有啊,就用 date 即可,後來還真的發現 FreeBSD 上的 date 有支援,但 Ubuntu 10.04 上的 date 沒有對應的 -j 跟 -f 指令。



  • 將此刻的日期轉成 timestamp

    • # date "+%s"



  • 把指定的日期轉成 timestamp

    • # date -j -f "%Y-%m-%d %H:%M:%S" "2011-03-03 00:00:00" "+%s"
      1299081600



  • 把指定的 timestamp 轉成可讀日期

    • # date -j -f "%s" 1299081600
      Thu Mar  3 00:00:00 CST 2011




上述例子是在 FreeBSD 8.1-RELEASE amd64 的機器上執行。


[Unix] awk 與 bash script 的使用 @ FreeBSD

想說簡單分析一下 squid 產生的 logs ,於是就想到用 bash 與 awk ,不過,過程不是很順利,發現一些 script 使用上的問題,給他筆記一下。



























awk 與 bash script,在 FreeBSD 8.1-RELEASE amd64

#!/bin/sh
touch /tmp/access.log
echo "=== CASE 1 ==="
cmd="awk \"BEGIN{a=1;b=2;}{}END{}\" /tmp/access.log"
echo
echo $cmd
echo
dd=`$cmd`

=== CASE 1 ===


awk "BEGIN{a=1;b=2;}{}END{}" /tmp/access.log


#!/bin/sh
touch /tmp/access.log
echo "=== CASE 2 ==="
cmd="awk 'BEGIN{a=1;b=2;}{}END{}' /tmp/access.log"
echo
echo $cmd
echo
dd=`$cmd`

=== CASE 2 ===


awk 'BEGIN{a=1;b=2;}{}END{}' /tmp/access.log


awk: syntax error at source line 1
 context is
         >>> ' <<<
awk: bailing out at source line 1


#!/bin/sh
touch /tmp/access.log
echo "=== CASE 3 ==="
cmd="awk 'BEGIN{\
        a=1;\
        b=2;\
}{}END{}'
/tmp/access.log"
echo
echo $cmd
echo
dd=`$cmd`


=== CASE 3 ===


awk 'BEGIN{ a=1; b=2;}{}END{}' /tmp/access.log


awk: syntax error at source line 1
 context is
         >>> ' <<<
        missing }
awk: bailing out at source line 1



#!/bin/sh
touch /tmp/access.log
echo "=== CASE 4 ==="
cmd="awk \"BEGIN{\
        a=1;\
        b=2;\
}{}END{}\"
/tmp/access.log"
echo
echo $cmd
echo
dd=`$cmd`


=== CASE 4===


awk "BEGIN{ a=1; b=2;}{}END{}" /tmp/access.log


awk: non-terminated string BEGIN{... at source line 1
 context is
         >>>  <<<
awk: giving up
 source line number 2


#!/bin/sh
touch /tmp/access.log
echo "=== CASE 5 ==="
cmd="awk \"BEGIN{\
a=1;\
b=2;\
}{}END{}\"
/tmp/access.log"
echo
echo $cmd
echo
dd=`$cmd`


=== CASE 5===


awk "BEGIN{a=1;b=2;}{}END{}" /tmp/access.log



簡述一下,在 bash script 使用中,對於 awk 內部的敘述部分,要改用雙引號 " 而不是用單引號 ' ,另外則是敘述句裡頭不能有空白或Tab等。所以搞到最後我就改用 profile 的方式:


#!/bin/sh
touch /tmp/access.log
touch /tmp/awk.profile
awk_profile="BEGIN { \
        a=1;\
        b=2;\
}{}END{}"
echo $awk_profile > /tmp/awk.profile

cmd_out=`awk -f /tmp/awk.profile /tmp/access.log`