Thursday, May 2, 2013

Arch Linux on Raspberry Pi Running XFCE [Version 2]

I have created a one-liner to install XFCE on Arch Linux, Raspberry Pi. Find it in the last line.

I wrote about installing XFCE on Arch Linux running on Raspberry Pi but it seems that those instructions are no longer valid for the new version of Arch as hosted on Raspberry Pi download page. So below are the new instructions for installing XFCE on Arch, Raspberry Pi. Please note that initial steps are similar for the previous versions of Arch too.

First off download the latest Arch Linux ARM from Raspberry Pi downloads page and unzip it to extract the img file. Once you have the img file, you need to write this on a SD Card. You can use dd command or tools like ImageWriter. There are more options available on elinux page. Let us use dd command for now:
# dd bs=4M if=~/archlinux-hf-2012-09-18.img of=/dev/mmcblk0

No, cp command is not supposed to be used here because cp copies over the file system and we have to do something at much more lower level. In case you are wondering how I got the /dev/mmcblk0 bit, I just mounted the sd card and check the output of df -h command. If you are using a sd card of more than 2G memory, then I recommend using gparted or anything else and expand the size of the file system since by default it'll be just about 2G and rest of your space will go unused. Once you are done here, insert the sd card into your Pi and fire it up.
Now you can see the awesome black login screen. The default password for root user is 'root'. Login as root and create pacman, the Arch Package Manager, database.
# pacman-key --init

Some randomness would be helpful here. So hit ALT+F2 to go to another tty and execute some random commands like ls and echo and cd etc. Switch back to the previous tty by hitting ALT+F1 and wait till the initialization of db is done. Now you can update your repositories:
# pacman -Syu

 Let us install Xorg libraries first:
# pacman -S xorg-xinit xorg-server xorg-server-utils xterm
This will get us the basic X server and related dependencies

Next, we will install XFCE:
# pacman -S xfce4
The CLI will ask you if you want to install selected packages only. I choose to install everything since they looked bare minimum anyway but you can be choosy here.

Now we may need the display drivers:
# pacman -S mesa xf86-video-fbdev xf86-video-vesa

Also we will need a login manager. I use SLiM since it is lightweight:
# pacman -S slim

Next we need to enable SLiM and graphics user mode (systemd lingo for runlevel 5):
# systemctl enable slim.service
# systemctl enable graphical.target

And we have to create a .xinitrc in the user's home directory. This file reads X server configs and starts XFCE environment:
# vim ~/.xinitrc 

#!/bin/sh 

if [ -d /etc/X11/xinit/xinitrc.d ]; then
for f in /etc/X11/xinit/xinitrc.d/*; do
[ -x "$f" ] && . "$f"
done
unset f
fi
exec startxfce4

Also we need a ~/.bash_profile to execute startx to initiate the Xserver as soon as the user (root in this case) logs in.  :
# vim ~/.bash_profile
[[ -z $DISPLAY && $XDG_VTNR -eq 1 ]] && exec startx

That is it! Reboot and enjoy XFCE on Raspberry Pi.

To save you some time, I have combined these commands in a small shell script and put it on github (fork it). So now, to install XFCE on your Pi, you need to fire just one command:

curl https://raw.github.com/adimania/arch-desktop-environments/master/XFCE-Arch-RPi.sh | bash


Discuss this post on Hacker News.

Thursday, March 28, 2013

All About inodes, Hard Links and Soft Links

Open your terminal and fire "ls -i" and you will see that each file is associated with a number.
$ ls -i
2889973 users.sh 2889972 fedoraRepo.sh 2889970 sfs.sh
2889969 bigFile.sh 2889971 dbBackup.sh 2889714 tree-clone.py


Ever wondered what this number is?
Ever thought what happens when a file is deleted?
How does the system knows the owner of the file or it's last modification time?
What are hard links?
What is the difference between hard links and soft links?

I'll try to answer these questions and probably more but I want to stress on a point; every thing in Linux is a file including the directories and devices attached.
Also install a package called sleuthkit using yum or apt to obtain a tool called istat.

When a filesystem (considering ext3/ext4 for now) is created on a disk, a special data structure is created. We'll call this inode table (technically it is an array of structure). It is indexed from 1 to n where n is the maximum numbers of inodes in the filesystem. Details like maximum number of inodes are decided while creating the filesystem usually. We can run "df -i" to check out number of inodes used and available.
Now whenever we create a file or a directory, an unallocated inode number is assigned and this is where several details about the file or the directory is stored. POSIX standard requires inode to contain the following information (borrowed from Wikipedia):
  • The size of the file in bytes.
  • Device ID (this identifies the device containing the file).
  • The User ID of the file's owner.
  • The Group ID of the file.
  • The file mode which determines the file type and how the file's owner, its group, and others can access the file.
  • Additional system and user flags to further protect the file (limit its use and modification).
  • Timestamps telling when the inode itself was last modified (ctime, inode change time), the file content last modified (mtime, modification time), and last accessed (atime, access time).
  • A link count telling how many hard links point to the inode.
  • Pointers to the disk blocks that store the file's contents (see inode pointer structure).
Notice that this does not include the filename. Surprised? In fact inodes never hold that information. So an obvious question arises, when we open file, how does the system know what inode it is associated with? To understand this, we need to understand what exactly is a directory. As I have mentioned, everything in Linux is a file which implies that even directory is a file. Every directory consist of a data structure, first part of which holds an inode number and the second part holds the file name. So when we try to perform any operation on a file, a recursive traversal is performed to lookup for inode number against that file name and that is how inode is obtained.

Among the information contained in inode, a link count and size is maintained. When we delete a file the link count is decreased until it reaches zero where the size of the file is also set to zero. See the example:
# ls -i abc
2891791 abc

# istat /dev/sda5 2891791
inode: 2891791
Allocated
Group: 353
Generation Id: 3534721592
uid / gid: 1000 / 1000
mode: rrw-rw-r--
Flags:
size: 6
num of links: 1

Inode Times:
Accessed: 2013-03-29 01:16:46 (IST)
File Modified: 2013-03-29 01:16:46 (IST)
Inode Modified: 2013-03-29 01:16:46 (IST)

Direct Blocks:
127754

# ln abc def
# istat /dev/sda5 2891791
inode: 2891791
Allocated
Group: 353
Generation Id: 3534721592
uid / gid: 1000 / 1000
mode: rrw-rw-r--
Flags:
size: 6
num of links: 2

Inode Times:
Accessed: 2013-03-29 01:18:41 (IST)
File Modified: 2013-03-29 01:16:46 (IST)
Inode Modified: 2013-03-29 01:18:34 (IST)

Direct Blocks:
127754

# rm abc
# istat /dev/sda5 2891791
inode: 2891791
Allocated
Group: 353
Generation Id: 3534721592
uid / gid: 1000 / 1000
mode: rrw-rw-r--
Flags:
size: 6
num of links: 1

Inode Times:
Accessed: 2013-03-29 01:18:41 (IST)
File Modified: 2013-03-29 01:16:46 (IST)
Inode Modified: 2013-03-29 01:18:57 (IST)

Direct Blocks:
127754 


So the "num of links" increased when we created a hardlink using ln command by one. When we deleted the file using rm command, the "num of links" decreased by one. If we delete the def file then the count and size will be set to zero.

# istat /dev/sda5 2891791
inode: 2891791
Not Allocated
Group: 353
Generation Id: 3534721592
uid / gid: 1000 / 1000
mode: rrw-rw-r--
Flags:
size: 0
num of links: 0


Inode Times:
Accessed: 2013-03-29 01:18:41 (IST)
File Modified: 2013-03-29 01:30:10 (IST)
Inode Modified: 2013-03-29 01:30:10 (IST)
Deleted: 2013-03-29 01:30:10 (IST)

Direct Blocks:


This brings us to our next topic of discussion, what are hard links and what are soft links? Simply putting, hard link of a file holds the inode of that file where as the soft link of the file is just a reference to another file. If we delete the original file but have a hard link to it, then we can still access the contents using the hard link. If we delete the original file, the soft link is pretty much useless since all it did was to point to the original name which contained the inode. A crude way to depict what I am saying is below. See how both "Original Name" and "Hard Link" is pointing to the inode but "Soft Link" is not.
Original Name ---------> inode <--------- Hard Link
Soft Link ----------> Original Name -----------> inode

Now, soft links have their own importance. We cannot use hard links to point to files across different filesystems but with soft links we can. This comes really handy when you want to maintain one name regardless of version differences. See how /usr/bin/python actually points to another binary.

# ls -l /usr/bin/python
lrwxrwxrwx. 1 root root 7 Jan 9 22:45 /usr/bin/python -> python2



Honestly, there are many more creative uses of links. If you are interested then I recommend that you check out how BusyBox implements a lot of commands using a single binary.
(Hint: $0 is the name of the script which is passed as a variable to the binary)

Update: As mentioned by reirob on HackerNews, there is a particular case where deleting the file does not set the "num of links" to zero. This usually happens when there is a process which is writing to the file. I have encountered this a few times myself when I delete a log file but the server writing it hasn't been restarted.

Sunday, January 13, 2013

Arch Linux on Raspberry Pi Running XFCE

Instructions in this post are no longer valid. Please find the updated post here.

I recently got a Raspberry Pi from RS online store. I wanted one so bad and it took so long before I got to play with it that by the time I got it, I was pretty much drooling over it. I started off by installing Raspbian which worked out of the box (what fun it is! :( ). I then moved on to try Arch and the fun began. Arch Linux install guide at elinux is pretty good but it only helps you to get bare bones Arch up and running. After that you are on your own. So here I am going to discuss how I managed to get Arch up and running with XFCE, a login manager and a web browser.

First off, download the Arch Linux from Raspberry Pi downloads page. Raspberry Pi's processor is ARMv6 so you cannot just use any Arch variant. Once you are done with the download, you need to extract it and transfer the .img file to a sd card. Either use dd command for this or use a tool like ImageWriter. There are more options available. Check out elinux more choices. I'll use dd command here:
# dd bs=4M if=~/archlinux-hf-2012-09-18.img of=/dev/mmcblk0

No, cp command is not supposed to be used here because cp copies over the file system and we have to do something at much more lower level. In case you are wondering how I got the /dev/mmcblk0 bit, I just mounted the sd card and check the output of df -h command. If you are using a sd card of more than 2G memory, then I recommend using gparted or anything else and expand the size of the file system since by default it'll be just about 2G and rest of your space will go unused. Once you are done here, insert the sd card into your Pi and fire it up.

Now you can see the awesome black login screen. The default password for root user is 'root'. Login as root and create pacman, the Arch Package Manager, database.
# pacman-key --init
Some randomness would be helpful here. So hit ALT+F2 to go to another tty and execute some random commands like ls and echo and cd etc. Switch back to the previous tty by hitting ALT+F1 and wait till the initialization of db is done. Now you can update your repositories:
# pacman -Syu

Now first we will install the xorg libraries:
# pacman -S xorg-xinit xorg-server xorg-server-utils
This will install the X server and pull some common dependencies.

To install XFCE now, fire:
# pacman -S xfce4
It'll ask you to "Enter a selection" after giving some packages. I installed all of them since they looked quite necessary like Thunar and the top panel etc but you can be choosy if you want.

Is your GUI working? You may be missing display drivers. Install them:
# pacman -S mesa xf86-video-fbdev xf86-video-vesa

We still need a login manager. I used SLiM, the Simple Login Manager. Remember it is Pi, so we are trying to do everything lightweight.
# pacman -S slim

Reboot after this and you will be shown a GUI interface to enter your user id and password to login. Do that and open a terminal. We'll install a web browser now. You might be tempted to install Firefox or Chrome but remember, this is ARMv6 and none of the main stream browsers support this architecture out of the box. So either you can compile the binary from Firefox or Chromium code or install a browser like Midori or Arora. I installed Midori because I am more familiar with it.
# pacman -S midori

That is it. You got Arch in quite usable state with a working XFCE. Have fun!

PS: The memory footprint with XFCE up and running is about 140m for my Pi.

Monday, November 5, 2012

Testing Network And TCP Optimizations

This post is more like a "note to self" for certain TCP parameters which I usually modify (or plan to modify) on production servers.

Some good to know terms:
  • Round Trip Time (RTT): It is the time taken by a packet from source machine to reach destination and come back. You can use ICMP ping to get the RTT.
  • Latency: The time from the source sending a packet to the destination receiving it. This is often mixed with RTT. Clarify what you are talking about before interpreting anything.
  • Bandwidth Delay Product (BDP): It is the amount of data that can be in transit in the network or simply the product of link bandwidth and RTT.
Say you want to test your app or benchmark hardware you just bought then first thing you need to do is to add it into the network, even local network will do. Please avoid wireless network because RTT vary a lot for a wireless network and it becomes difficult to see of hardware is at fault or the wireless.

Adding Latency Or RTT Delay To The Network
If you are serious about testing hardware then you may need to test at various RTT/latency values to evaluate the experience of your customers from various locations across the world. To introduce this RTT delay you can use Network Emulator or simply netem and fire the following command:

tc qdisc add dev eth0 root netem delay 100ms

The command above will introduce a RTT delay of 100ms on eth0 interface. Now you can play around with it to check various values of RTT delays. When you are done, remove the delay by deleting the rule.

tc qdisc del dev eth0 root

A awesome tutorial of netem can be found at LinuxFoundation.org. Netem mailing list archives might help in debugging in several cases.

Server Setup For Testing
If you plan to test the hardware then I suggest running a simple and no-frills http server on your hardware like python single threaded server. Using scp for testing is not a good idea since openssh itself had some app level congestion controlling mechanisms. To run python single thread server, fire the following command on your terminal:

cd <doc_root_of_http_server>
python -m SimpleHTTPServer


Make sure a large file is present in the document root of the server and that curl or wget is present on the client. Do not use any browser or download manager to download from server.
Of course if you are testing your app then above thing might not be applicable to you. In that case setup the server and client depending upon your app.

Testing and Recording The Defaults
Recording defaults is important in case you need to revert anything. A full backup can be obtained easily by sysctl command:
sysctl -A > sysctl.bak

Now download the file without introducing any latency from the server. This is the default performance at 0ms added latency. Now let us start the serious testing and introduce latency. Add a RTT of 100ms and download the file using curl or wget and see the speed.

Various TCP Optimizations and Parameters To Check
I just found out during this experiment that new kernels have great settings for TCP, still cross checking won't hurt.

First and foremost get acquainted with /proc of you machine, specifically /proc/sys/net/ directory. I would also encourage you to do through the man page of tcp and understand the parameters.

The changes I am going to suggest depend heavily on kernel version and the distribution. If not done correctly, these changes can degrade networking performance or may harm your machine in any other way. You have been warned. You are on your own.
  • First of all we'll examine if TCP selective ack is turned on or not and turn it on if it is off. It is boolean so just set the right value to 1 and you are good to go:
    sysctl -w net.ipv4.tcp_sack=1
  • We need to make sure that TCP window can scale to utilize maximum buffer possible:
    sysctl -w net.ipv4.tcp_window_scaling=1
  • Fix the read and write buffers for tcp to an optimum value. It is an array of 3 values which defines minimum, default and maximum values of memory that can be utilized. Also note that this overwrites the values defined for generic (non-tcp) connections in the following files:

    /proc/sys/net/core/rmem_max
    /proc/sys/net/core/wmem_max
    /proc/sys/net/core/rmem_default
    /proc/sys/net/core/wmem_default


    Setting this is usually heuristic and depends largely on your network. Also with auto scaling on, it can scale up to the maximum value defined. Set it up by using the following command:
    sysctl -w net.ipv4.tcp_rmem='4096 87380 4194304'
    sysctl -w net.ipv4.tcp_wmem='4096 16384 4194304'

    Here default memory allocated to receive buffer for each TCP connection would be 87380 bytes  and can scale up to 4194304 depending upon the connection. I suggest that you experiment with the values a bit to find the most optimum combination.
    If you are doing non-tcp optimizations as well then set net.core.rmem_max, net.core.wmem_max, net.core.rmem_default, net.core.wmem_default as well to similar values.
  • Enable the TCP time_wait reuse. This would allow the reuse of connections that are in time_wait state. This generally increases performance if your machine is going to make a lot of short lived connections.
    sysctl -w net.ipv4.tcp_tw_reuse=1
  • Maximum number of concurrent connections can sometimes play a role in servers handling high traffic. This can be determined by dividing the difference of values in the file /proc/sys/net/ipv4/ip_local_port_range by the value in /proc/sys/net/ipv4/tcp_fin_timeout. For my system it is (61000-32768)/60 which turns out to be 470. You can increase the range of the ports or you can reduce the tcp_fin_timeout but experiment first before deploying in production.
There are a lot of other parameters that can be tweaked for higher performance. You can try all of them out but do not march straight into production servers with these tweaks. Experiment in your staging boxes first.

Wednesday, October 24, 2012

Common Linux Termination Signals

People issuing a kill -9 on production servers blindly should read this:

If you have ever used Linux terminal, I think you should know what Linux Termination Signals are. They are the signals that you usually supply with kill command when you are trying to kill a process. Depending upon the provided signal further actions can be taken.

  • SIGTERM: It is the default signal used by kill command. This signal can be handled by the process in the sense that process can choose to ignore this or do some specific action upon receiving this signal. It is like politely asking someone to do something.
  • SIGKILL: This is the signal sent when you supply -9 as argument to the kill command. This signal cannot be handled or ignored. Process has to die as a result of this signal. Usually this is the last resort thing. If there is a process which is not dying even after receiving a SIGKILL then you should report it to upstream. It is most likely some sort of bug in distribution or the app itself.
  • SIGHUP: This signal tells the process that user's terminal is disconnected somehow. Usually this happens when you ssh pipe gets broken or you face network connectivity issues etc.
You can get a list of available signals for your distribution by firing kill -l on terminal. For my Fedora 17 box there are 64 signals available. I think the number would be the same for the distributions following POSIX standard.

UPDATE: (As mentioned by @MarcusMoeller on twitter) SIGHUP is commonly used by daemons to reload config files.