Opening a new tab in an existing GNOME terminal window

Use RoxTerm instead – 'roxterm --tab' opens a new tab in an existing terminal window, with no hack described here.

GNOME terminal has great support for tabbed terminal sessions. You can simply open a new tab by pressing CTRL+SHIFT+T and it’s very convenient.

However, it seems like there’s no straightforward way to ask gnome-terminal command to reuse an existing window and add a new tab there. I tried various options like --tab, but they didn’t work as I expected. I just want to keep only one terminal window in my desktop, but it looks like there’s no command line option that does the job.

So, I wrote a shell script that adds a new tab to an existing GNOME terminal window when there is already a running instance. It also launches a new terminal window if necessary:

# Path: /usr/local/bin/gnome-terminal
if [ "x$*" != "x" ]; then
  /usr/bin/gnome-terminal "$@"
  pgrep -u "$USER" gnome-terminal | grep -qv "$"
  if [ "$?" == "0" ]; then
    WID=`xdotool search --class "gnome-terminal" | head -1`
    xdotool windowfocus $WID
    xdotool key ctrl+shift+t
    wmctrl -i -a $WID

This script looks for an existing gnome-terminal window, sends CTRL+SHIFT+T key event there, and raises the terminal window. Please note that xdotool and wmctrl are required to run the script. They should be available in most Linux distributions.

It’s just a band-aid solution – I hope I can get rid of this script from my system when the next release of GNOME terminal is out.

내가 하는 일은 세상을 (어떻게) 바꾸는가

스스로 최선을 다하게 되는 데는 여러 가지 이유가 있기 마련이다. 나는 주로 내가 하는 일이 이 세상을 어떻게 바꾸게 될 것인가에 대해 주로 생각한다. 그리고 어느 시점까지는 그런 생각을 통해 많은 부분에서 성공적인 시간을 보내 왔던 것 같다.

그런데 최근의 여러 가지 일들을 통해 이 주제에 대해 재고하게 될 기회가 생겼다. 여러 가지 일들이라고 해서 그렇게 특별한 것은 아니다. 주로 업무상의 의도치 않은 일시적 태만이라든지, 학습에의 가볍고 일시적인 강박이라든지, 사소한 일들이 주를 이룬다. 물론 미나 프로젝트를 떠나면서 사색의 시간을 좀 더 할당받았기 때문이라는 큰 이유도 있겠지만.

소프트웨어 개발을 포함한 대부분의 일은 세상을 바꿀 수 있다. 그것이 아무리 하찮을 지라도 세상을 바꾼다는 것은 의심의 여지가 없다. 하지만 동시에 대부분의 일들에 있어 우리의 행위가 세상을 어떻게 바꿀 지에 대해서는 논란의 여지가 크다는 것도 의심의 여지가 없다. 내가 작성한 소프트웨어로 크루즈 미사일 을 제어할 수도, 해저 자원 탐사 로봇을 제어할 수도 있다. 그뿐인가. 크루즈 미사일이 세상을 좋게 바꾸느냐 그렇지 않느냐는 더 큰 논란의 여지를 낳는다.

이런 관점에서 보았을 때, 솔직히 지적 유희나 이윤의 추구를 위한 일이 덜 가치있다고 말할 만한 논거가 잘 떠오르지도 않는다. (참, 그런데 그 가치라는 건 또 어떻게 평가하지?) 그렇게 생각이 꼬리에 꼬리를 물다 보면 세상을 바꾼다는 말이 생각보다는 별로 흥미 진진한 일만은 아닌 게 아닌가 싶어진다. 그렇다고 해서 딱히 스트레스를 받는다거나 하는 것도 아니다. 하지만 뭔가 김빠지는 것만은 틀림없다.

좀 더 현실적인 문제로 돌아갈 필요를 느낀다. 그래서 더 학습에 대해 강한 욕구를 느끼는지도 모르겠다. 하지만 미완의 사색에는 찜찜한 구석이 있다.

Performance Comparison between NIO Frameworks

Most NIO frameworks can saturate 1 gigabit ethernet at some point. However, some frameworks can saturate the bandwidth with the smaller number of connections while others can not. The performance numbers of the 5 well-known open source NIO frameworks are presented here to help you figure out the excellence of Netty in performance.

Where’s the Graph?

If you are in a hurry, please scroll down to see the graphs first. You can also download the PDF document which contains detailed numbers and graphs.

What’s the Bottom Line?

Unlike usual expectations, NIO frameworks have different performance characteristics in spite of the fact that they are using the same NIO selector provider.

What’s observed is that the difference comes from the fundamental factors such as data structure and thread contention management, and those factors should never be overlooked.

Netty has succeeded to introduce the breakthrough in NIO framework performance with careful engineering, while retaining the flexible architecture.

Test Scenario

A simple echo server and client exchange fixed length messages one by one (i.e. synchronous ping-pong). The handler code, which sends the received data back in verbatim, is executed in a separate thread pool that each NIO framework provides.

The tests were run with different message lengths (64 ~ 16384 bytes) and different network configurations (loopback and 1 gigabit ethernet), to see how well each framework performs on various conditions.

Test Environment

  • Software
    • The test client has been written in Netty 3.0.0.CR5.
    • Echo server implementations
      • Netty 3.0.0.CR5
      • Other 4 open source NIO frameworks
        • Grizzly, MINA, NIO Framework, and xSocket
        • Used the latest milestone releases as of October 3rd, 2008
        • Excluded inactive projects (no release in 2008)
        • Framework names were anonymized in no particular order.
      • Thread pool
        • The number of I/O threads – the number of the CPU cores
        • The number of handler threads – 16
        • The default thread pool that each framework provides was used.
        • If the framework doesn’t provide a thread pool implementation which limits the maximum number of threads, Executors.newFixedThreadPool() was used instead.
      • Use of direct buffers was suppressed to avoid excessive memory consumption.
    • JRE – Sun JDK 1.6.0_07
    • JRE options – -server -Xms2048m -Xmx2048m -XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods
  • Hardware
    • Server (Hostname: Eden)
      • CPU: 2 x quad-core Xeon 2.83GHz, ‘performance’ governor
      • O/S: Linux (Fedora 9)
      • RAM: 6 GiB
      • NIC: Broadcom NetXtreme Gigabit Ethernet PCI express
    • Client (Hostname: Serpent)
      • CPU: 2 x dual-core Xeon 3.00GHz, ‘performance’ governor
      • O/S: Linux (Fedora 9)
      • RAM: 3 GiB
      • NIC: Broadcom NetXtreme Gigabit Ethernet PCI express
    • No switching hub was used to minimized possible network latency.
  • Common TCP/IP parameters
    • TCP_NODELAY was turned on. (i.e. Nagle’s algorithm was disabled.)
    • net.ipv4.tcp_tw_recycle has been set to 1
    • Used the default MTU (i.e. 1500 – no jumbo frame)

Test Result

Client and Server on the Same Machine (Loopback Device)

The test client and servers ran on the same machine, Eden. (If images are not showing up, please refresh. There are three graphs here.)

Size=128, Loopback
Size=1024, Loopback
Size=4096, Loopback

Client and Server on Different Machines (1 Gigabit Ethernet)

The test client ran in Serpent, and the servers ran in Eden. (If images are not showing up, please refresh. There are three graphs here.)

Size=128, 1Gb Ethernet
Size=1024, 1Gb Ethernet
Size=4096, 1Gb Ethernet

Running the Tests by Yourself

The test result should be always reproduceable. Please give us your feed back to improve the accuracy of the test result. The full source code is available at the Subversion repository:

svn co

All tests run by Ant. Enter ‘ant -p‘ to see the instruction.

유명인으로 산다는 것

누구나 생애 한 번쯤 생각해 보았을 것이다. 자기 자신이 (적어도 어렸을 적에) 대통령이나 연예인이 된다거나, (아주 조금 더 현실적으로) 이름난 엔지니어가 된다거나.

하지만 조금이라도 남의 이목을 받아 본 사람이라면 유명인으로 산다는 것이 얼마나 힘든 일일 지 상상할 수 있다. 언제부턴가 내가 모르는 사람이 나를 이야기하고 알아본다는 신선한 충격은 잠시나마 즐거울 수 있겠지만, 근본적인 거리에 기인한 오해의 깊이는 어찌할 노릇이 없을테니까.

유명세는 그래서 그것을 감당할 수 있는 사람에게 주어져야 한다. 하지만 운명은 항상 우리를 예기치 않은 곳으로 데려가기 마련이어서, 나는 내가 설 그 곳에 지나친 스포트라이트가 나를 비추지 않기를 바랄 뿐이다.

… 물론 내 속의 속물 근성은 좀 다른 것을 원할 것 같지만!

Inserting Google Analytics Tracking Code using Shell Script

In most cases, you should be able to configure your CMS or blog template to insert the Google Analytics JavaScript tracking code at the end of every page in your web site.

However, you sometimes need to install the tracker code into static HTML pages which are not managed by the CMS, If you have a LOT of static HTML pages, then your fingers are in trouble.

Here’s the Bash shell script which inserts the Google Analytics tracker code into all static HTML pages. Please modify it before you run it, to meet your needs. Never forget to backup the original copy — it might work incorrectly for you:

# inject-google-analytics
TRACKER_CODE="<script type=\"text/javascript\">
var gaJsHost = ((\"https:\" == document.location.protocol) ? \"https://ssl.\" : \"http://www.\");
document.write(unescape(\"%3Cscript src='\" + gaJsHost + \"' type='text/javascript'%3E%3C/script%3E\"));
<script type=\"text/javascript\">
try {
var pageTracker = _gat._getTracker('$TRACKER_ID');
} catch(err) {}</script>"

find . -name '*' -delete
find . '(' -name '*.html' -and -not -name '*-frame.html' ')' | while read -r TARGET; do
  if grep -qiP "(<frameset|$TRACKER_ID)" "$TARGET"; then

  if grep -qiF '</body>' "$TARGET"; then
    perl -pi -e "s/(<\/body>)/\n${TRACKER_CODE////\/}\n$1/i" < "$TARGET" > "$"
    { cat "$TARGET"; echo; echo "$TRACKER_CODE"; echo; } > "$"
  if ! grep -qF "$TRACKER_ID" "$"; then
    echo "Failed to inject analytics script: $TARGET"
    cat "$"
    rm -f "$"
    exit 1
  mv -f "$" "$TARGET"

Netty 3.0.0.CR4 released with a Getting Started Guide

I wanted to write a decent guide book for the Netty project to help users get started with it very quickly, but it was not really easy to learn DocBook XML and to learn how to override several default DocBook stylesheet settings within a short period of time. Especially, I had no idea how XSL FO works when I saw it first time.

Finally, the first chapter of the Netty User Guide is now public. Other chapters are still to be written, but it’s better than having nothing. It’s available in HTML and PDF. Check it out now and give me some feed back!

Streaming Audio from Windows to PulseAudio Server

PulseAudio is a great network audio server for Linux. It allows me to stream audio between machines. However, the biggest problem with PulseAudio is that it doesn’t have a descent client implementation for Windows. There are a couple known workarounds such as using WinESD, but they are all pretty experimental. Also, the Win32 binary build didn’t work for me, perhaps because it’s somewhat outdated and buggy.

My first try was to use LineInCode and Netcat for Windows on the client side and to use module-simple-protocol-tcp on the server side. After adding -L "module-simple-protocol-tcp port=4712 rate=44100 format=s16le channels=2" option to the PulseAudio startup option, I was able to stream audio from Windows to PulseAudio server using the following command:

linco.exe -B 16 -C 2 -R 44100 | nc.exe <host> 4712

However, PulseAudio dies whenever the connection is closed because of a known issue of module-simple-protocol-tcp. I wrote some script that restarts PulseAudio whenever it dies, but I realized there’s a better solution – OpenSSH.

I have installed a Cygwin build of OpenSSH, and replaced Netcat with it:

linco.exe -B 16 -C 2 -R 44100 | ssh.exe <user@host> "cat - | pacat --playback"

This solution works without module-simple-protocol-tcp, and therefore there’s no need to restart the PulseAudio server on disconnection. Better security is another bonus. Instead, it requires a running SSH server. If SSHD is not running in the PulseAudio server, you can give it a little twist:

linco.exe -B 16 -C 2 -R 44100 | ssh.exe <user@otherhost> "cat - | pacat --server <host> --playback"

In case your Windows machine doesn’t have a sound card or it doesn’t allow you to capture the audio stream, you might want to use Virtual Audio Cable. It’s a commercial software, but it is worth a purchase.

You might also want to set up a passwordless login so you don’t have to enter the password every time you stream the audio.

영한 혼용과 똘레랑스

영어는 우리 생활 곳곳에 침투해 있다. 어떤 사람은 그것에 더 많이 익숙하고 다른 이는 그렇지 못하다. 나는 그 중간 즈음에 위치했다고 생각하고 있다. 그래서일까, 외국 생활을 오래 했다거나 외국계 회사에서 오래 일했다는 이유로 한국어 문장 속에서 영어를 혼용하는 것이 아직은 거북하게 들린다. 대표적으로 다음과 같은 것들 말이다:

  • Boss – 상사
  • Claim – 불만 사항
  • Expense – 비용
  • Industry – 업계
  • Meeting – 회의
  • Office – 사무소
  • Payroll – 월급 명세
  • Position – 직위
  • Post – 글
  • Report – 보고

수시로 문장마다 한자를 섞어 쓰는 마당에 무엇이 문제냐고 묻는다면, 그것은 아마도 익숙함의 차이 아니겠냐고 대답하겠다. 내 모든 글에서 가능한 글자를 전부 한자로 바꿔버린다면 거북해 할 사람들이 영어로 바꿀 경우보다 훨씬 많으리라고 예상할 수 있듯 말이다.

하지만 중간 즈음에 위치한 사람인 나는 혼란스럽다. 나도 모르게 섞어 말하고, 적절한 한국어 단어가 떠오르지 않는 일이 생긴다. 그러면서도 상대방에게서 그런 경우를 보면 왠지 거북하고, 그래서 내게서 그런 말이 나오면 부끄러울 때도 있다.

그런데 이것을 조금만 더 확장해 보면, 수학자가 각종 수학 용어를 섞어 일반인과 소통한다면 그것이 비록 영어가 털끝만치도 섞이지 않은 순수한 한국어라 할 지라도 거북하기는 마찬가지일 것이라고 쉽게 예상할 수 있다. 특히나 수학 기호나 미적분학 용어만 보면 멀미가 나는 사람이 대부분이라고 가정했을 때는 더욱 그렇다.

그러니까 거북함이라는 것은 어쩌면 그냥 ‘나한테 익숙하지 않아서‘인지도 모르겠다. 내가 모르거나 익숙하지 않은 말을 들었을 때 상대방이 왠지 잘난 체 하는 것 같다는 인상을 받는 것처럼 말이다.

이렇게 별의 별 것이 상대적으로 인식된다고 생각하기 시작하면 내가 남을 무제한적으로 배려해야 하는지, 아니면 그 반대인지 혼란스러워지기 시작한다. 이상적으로야 똘레랑스를 바탕으로 서로를 자유롭게 받아들여야 마땅하겠지만, 끼리 끼리 논다는 말이 좀 더 현실적이다. 결국에는 비슷한 취향의 사람들이나 서로 받아들일 수 있는 취향을 가진 사람들끼리 모이게 되는 것 같다.

그리고 이런 끼리 끼리 성향은 나이가 들 수록 고착화되고 새 친구를 사귀기 어렵게 만들어, 노년에는 세상을 떠나가는 친구들을 그리워하는 마음만 깊어지게 되는 것 아닐까, 어렴풋이 두려움을 깔고 생각해 본다.