Sunday, April 12, 2015

QEMU - Virtio - NetworkManager on Fedora 21

QEMU is a generic and open source machine emulator and virtualizer. It takes care of networking, disk interfaces, managing memory and all other resources in order to run a guest OS inside a host OS.
Virtio  is a virtualization standard for network and disk device drivers where the guest OS' device drivers know they are running in a virtual environment, and cooperate with the host OS in order to get high performance network and disk operations, giving most of the performance benefits of paravirtualization.
NetworkManager is a set of co-operative tools that make networking simple and straightforward. This setup uses NetworkManager to provide a full network stack to the guest OS.
This document is a simple tutorial to take full advantage of the benefits of the technologies listed above when creating a new QEMU machine. For this, we are using an up-to-date installation of Fedora 21.
  1. As root on the host OS, create the br0 bridge device with the following persistent configuration:
    /etc/sysconfig/network-scripts/ifcfg-br0
    DEVICE=br0
    STP=yes
    BRIDGING_OPTS=priority=32768
    TYPE=Bridge
    BOOTPROTO=shared
    DEFROUTE=yes
    IPV4_FAILURE_FATAL=no
    IPV6INIT=no
    NAME=br0
    UUID=<paste the output of uuidgen>
    ONBOOT=yes
  2. Whitelist the br0 bridge device for use with qemu:
    /etc/qemu/bridge.conf
    allow br0
  3. As the normal user who will run qemu, create the disk image of the guest OS on the host OS:
    $ qemu-img create -f qcow2 guest.qcow2 40G
  4. Define the options for qemu; you may want to save them to a script:
    OPTS="-enable-kvm -drive file=guest.qcow2,if=virtio,aio=native -m 4G --machine pc,accel=kvm:xen:tcg -cpu host -soundhw hda -name GuestOS -vga std -smp cpus=1,cores=2,threads=2 -netdev tap,helper=/usr/libexec/qemu-bridge-helper,id=tap0 -device virtio-net-pci,netdev=tap0,id=nic0"
  5. Run the guest OS installer disk image with the virtio drivers appropriate to it:
    qemu-system-x86_64 $OPTS -drive file="guestOSinstaller.iso",index=2,media=cdrom -drive file=virtio-drivers.iso,index=3,media=cdrom -boot d
  6. After installation, run the guest OS:
    qemu-system-x86_64 $OPTS -boot c

Wednesday, June 22, 2011

Recover deleted files from Linux when processes are running

This morning I was running a big simulation and I decided to rsync my files through the computers at the office. It was late when I realized that I forgot to exclude the simulation's log file, and so this file was deleted from one of the running machines.

When a file is deleted, its name disappears from the file system, but the information is still being saved as long as the simulation is running. So in order to recover the information, I had to identify the PID of the simulation:

$ pgrep larva
18218

Then I had to look for the files opened by the program:

$ ls -l /proc/18218/fd
0 -> /dev/null
1 -> /home/facorread/prog/larva.log (deleted)
2 -> /home/facorread/prog/larva.log (deleted)
3 -> /home/facorread/prog/larva.csv
5 -> pipe:[288212]

Now I can save the log to another file or just read how the program is going using commands such as

$ tail /proc/18218/fd/2
Thanks to Michael Stutz for his remarks on file recovery.

Thursday, February 10, 2011

Disable Incognito Mode in Google Chrome

There are times in which a systems administrator may want to disable the Incognito mode of the browser. Unfortunately, such lockdown cannot be done using a configuration option. The only way I have been able to do so is at the source code level.
Before configuring the source code for compilation, apply this patch; then be patient while Chromium is built.
The patch has been tested with Chromium-9.0.597.94.

Sunday, July 25, 2010

KDE suspend, hibernate workaround for Gentoo on Lenovo U150

Hello, recently I upgraded my Gentoo packages to the following:


sys-auth/consolekit-0.4.1

sys-auth/policykit-0.9-r1

sys-apps/hal-0.5.14-r2

kde-base/powerdevil-4.4.5

kde-base/policykit-kde-4.4.5
With these, I lost the ability to suspend the laptop. I trield by directly communicating with HAL:

dbus-send --system --print-reply --dest=org.freedesktop.Hal /org/freedesktop/Hal/devices/computer org.freedesktop.Hal.Device.SystemPowerManagement.Suspend int32:0
The program answered:

Error org.freedesktop.Hal.Device.UnknownError: error: org.freedesktop.Hal.Error: Could not determine whether caller is privileged

org.freedesktop.Hal.Device.Error
I don't have the clue why this is happening, so I found a workaround by adding the following to the local_start() function of /etc/conf.d/local:

sleep 3s && /etc/init.d/hald restart
I also had to make sure the suspension policies were enabled for the relevant users in PolicyKit through KDE's System Settings.

Update Oct 13, 2010 - Thanks to Motosauro's comments, I checked for the workaround in my current setup:

sys-auth/consolekit-0.4.1

sys-auth/policykit-0.9-r1

sys-apps/hal-0.5.14-r3 -apm

kde-base/powerdevil-4.5.2
The workaround is no longer needed.

Monday, July 19, 2010

Printing useful STL container information with GDB

GDB's "p variable-name" is the Swiss Army knife of the programmer, except when it comes to examine the contents of STL containers.

(gdb) p fieldInd
$110 = {
  <std::_Vector_base<unsigned long, std::allocator<unsigned long> >> = {
    _M_impl = {
      <std::allocator<unsigned long>> = {
        <__gnu_cxx::new_allocator<unsigned long>> = {<No data fields>}, <No data fields>}, 
      members of std::_Vector_base<unsigned long, std::allocator<unsigned long> >::_Vector_impl: 
      _M_start = 0x646c60, 
      _M_finish = 0x646cc8, 
      _M_end_of_storage = 0x646d20
    }
  }, <No data fields>}

This tutorial gives you useful information to have GDB print useful STL container information for you.
(gdb) pvector perm
elem[0]: $111 = 4
elem[1]: $112 = 2
elem[2]: $113 = 0
elem[3]: $114 = 3
elem[4]: $115 = 1
elem[5]: $116 = 5
Vector size = 6
Vector capacity = 6
Element type = unsigned long *

Wednesday, July 07, 2010

CAcert y Certicamara en Google Chrome / Chromium - Linux

Hola, hace algunos días no podía usar Google Chromium para ingresar a miplanilla.com, CAcert y otros servicios debido a que no contaba con los certificados digitales adecuados. Por el consejo de un amigo, e investigando una serie de sitios web, he compilado las siguientes instrucciones para agregar los certificados a Chrome:
wget http://www.cacert.org/certs/root.crt
wget http://www.cacert.org/certs/class3.crt
wget http://www.certicamara.com/downloads/safelayer/certicamara.crt
wget http://web.certicamara.com/media/69784/ac_offline_raiz_certicamara.crt
wget http://www.certicamara.com/ac_online_subordinada_certicamara.crt
certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org" -i root.crt
certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "CAcert.org Class 3" -i class3.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "TCu,Cu,Tuw" -n "Certicamara" -i certicamara.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "TCu,Cu,Tuw" -n "Certicamara CA Raiz SafeLayer 3" -i ac_offline_raiz_certicamara.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "TCu,Cu,Tuw" -n "Certicamara CA Subordinada SafeLayer 3" -i ac_online_subordinada_certicamara.crt
Para usar la herramienta certutil en Gentoo Linux, es necesario tener instalado el paquete nss con la opción utils. Para Ubuntu y otras distribuciones basadas en Debian, se debe tener instalado el paquete libnss3-tools. Más información en este blog.
Después de agregar los certificados, se pueden borrar los archivos crt.

Wednesday, March 31, 2010

Gentoo Linux on Lenovo u150

Hello, I just bought a Lenovo u150 with the following specs:

Intel CPU SU7300 @ 1.30GHz, 1300 Mhz, Core 2 Duo
RAM: 2GB
Video: 1366x768x60Hz, 11.6inches
Wireless: Intel WiFi Link 5100 AGN
LAN: Broadcom NetLink Gigabit Ethernet
Hard disk: 300 GB (320GB they say)
Bluetooth
Lenovo EasyCamera3

Here a summary of the steps I took to install Gentoo Linux on it.

I used UNetbootin to load the Gentoo Minimal Installation (amd64) iso image into a USB key, and booted the laptop with it. The first thing I noticed is the lack of the wired network interface, so I used the net-setup script to activate the wlan0 interface and connect to Internet without wires.

After creating partitions, compiling the kernel with this configuration and installing the basic packages as described in the Gentoo Handbook, the system rebooted smoothly.

Brightness

Brightness fixes have been published by zaius; based on his explanations I wrote this C++ program (just because I love C++), which just does the same but in a logarithmic fashion, which I find more appropriate for handling low levels of brightness.

Thanks to zaius for his research and ideas; I just hope the fix will be made at the kernel level very soon.

Earphone plug


The speakers do not mute when you plug your earphones in the laptop. I found a workaround by adding the following kernel parameter to grub.conf:

snd-hda-intel.model=olpc-xo-1_5

It may not look nice, you may loose access to the internal microphone, but the parameter works in keeping your music private for you. Keep up to date with information on these issue by subscribing to the Ubuntu Bug.

Update: linux-2.6.35 does not need the kernel parameter.

Thanks for reading.

I do not have any relationship with Lenovo or its trademarks.

Tuesday, March 09, 2010

C++0x tips: improve performance in code using STL containers

Hello, I am hungry for code performance, specially in my simulations involving entire cities full with people, mosquitoes, viruses, homes, flower vases, etc. So it has been frustrating to interrupt the code execution just to realize all it has been doing for hours is creating the cities. Let us examine how the preparation phase takes place:
class houseCls;
class personCls;
//...

class cityCls {
    private:
        vector<houseCls> houses;
        vector<personCls> pedestrians;
    public:
        //...
}

vector<cityCls> country;

int main() {
    for (int i=0; i<nCities; i++)
        country.push_back(cityCls(/* City construction arguments */));
}
On execution, a city is created in the stack, then it is copied into the country vector and then the city in the stack is destroyed. So most runtime is employed in getting the vector ready for simulation. Using an aggregate (array) is out of the question, because things must be dynamic and safety must be ensured in all aspects.
How about just creating the new city in the new space inside the vector? Previously the vector::swap() function helped accomplish this task, but implementation became confuse sometimes for the client programmer.

I took the opportunity to study the improvements of the newest C++ standard, C++0x. I learned about a great STL improvement in terms of memory management, performance, and overall, getting things done. It is the emplace_back() function, which does just what the above question was about.

Use the new C++ standard, at your own risk, taking into account it is evolving.
g++ --pedantic-errors -Wall -Wextra -std=gnu++0x source.cc
Make the following changes to your code:
int main(){
 ///Always reserve memory before starting
 country.reserve(nCities);
 for (int i=0; i<nCities; i++)
  country.emplace_back(/** City construction arguments, which do not need the explicit class name anymore */);
}
You can use explicit copy constructors and gdb to benchmark your code.

Thursday, February 25, 2010

SSH access to the computer at your office

There can be different ways to get connected to the computer at your office; most are grouped as VPN or VNC techniques; but they are quite a bloat when all you want to do is get connected to check how things are going up with that program you are running; under Windows you might get a crashed computer at the office, and that is bad thing if the program was running overnight.

You can use SSH alone to get connected from home, provided you have good knowledge of how SSH works (public key authentication and port forwarding), you have administrative access to the external computer and the router/modem, and your company firewall is not too restrictive. This post is about settings in which external computers cannot get connected to the computer at the office because the company firewall masquerades its IP address, or blocks external access to port 22 of the office computer. In these cases, the connection to the computer at the office needs a previously-set connection with port forwarding, as illustrated next:


The external computer must be configured to be visible from Internet. Configure it to have a static IP address and put it in the demilitarized zone (DMZ) of the router/modem, or at least as a virtual server. Refer to the router guide for instructions.

Let us use the name aaron for the external computer. For security, disable non-essential services (portmap, nfs, ftp) from aaron, and enable sshd so it accepts a reduced number of users (namely you and you alone); ensure strong passwords for these users, because Linux viruses are real; I have had the experience of being target of brute force attacks on my SSH servers all the time.
Insecurity in Linux comes from incompetent administrators. I definitely take measures to ensure the privacy and security of my computer, which is all open to Internet. For example,  besides the normal user account facorread, I created an unprivileged user account fabio, which will accept the incoming connection at aaron. The computer at the office will automatically try to connect to aaron as user fabio, so you need to set up public key authentication for this task. The following commands are what you need this set up, as user fabio:
fabio@aaron ~ $ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/fabio/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Your identification has been saved in /home/facorread/none.id_rsa.
The key fingerprint is: cc:dd:ff:3d fabio@aaron
fabio@aaron ~ $
cat .ssh/id_rsa.pub >> .ssh/authorized_keys
Save the public key in your USB memory stick:
fabio@aaron ~ $ cp .ssh/id_rsa.pub /media/usb1/fabio.pub
As root, improve security by disabling the shell for user fabio:
root@aaron ~ # usermod -s /sbin/nologin fabio
Configure aaron to listen at both the secure SSH port (22) and an insecure one (my favorite one is 11111), so you can test both connections from the office. In summary of all the above, add the following lines to your sshd_config file:
# Listening on two ports
Port 22

Port 11111
# Only allowed users
AllowUsers facorread fabio
# Restrictions for fabio
Match User fabio
    KbdInteractiveAuthentication no
    PasswordAuthentication no
When you restart your sshd service, you must notice the following when running netstat -tenpl as root:
tcp    127.0.0.1:22     0.0.0.0:*    LISTEN      sshd
tcp    127.0.0.1:11111  0.0.0.0:*    LISTEN      sshd
After this, you can check that ports 22 and 11111 are open to the Internet using a port scanner.
It is advisable to register your home computer with a free DNS service so you and the office computer always know the IP address of aaron through a host name of the likes of aaron.dyndns.org.  Modern modems/routers can connect to these services and keep them up to date about the IP address of your home. My modem is quite old, so I have created a script which uses ez-ipupdate to keep reporting the IP address to the dyndns service. Please adapt it to your needs and the particular modem you use at home:
#!/bin/bash


# Ensure only one instance of this script
[[ $(pgrep -o tellip) != $BASHPID ]] && exit 0


unset oldAddress
while true; do
newAddress=`curl -s --user admin:modemPassword http://modem/wancfg.cmd | grep isolate the IP address as reported by the port scanner`
if ! netstat -ei | egrep --quiet "addr:static address of aaron "; then
    echo "No interface has the static address to act as server. Waiting 1 minute."
elif [[ ( -z "$newAddress" ) || ( "$newAddress" == 0.0.0.0 ) ]]; then
    echo Waiting one minute before getting a valid address
elif [[ ( $newAddress != $oldAddress ) ]]; then
    echo "${oldAddress} -> ${newAddress} $(date)" &&
    oldAddress="${newAddress}" &&
    echo "address=${newAddress}
host=aaron.dyndns.org The host name you chose at the dyndns website
service-type=dyndns
user=dyndnsUser:dyndnsPassword" | /usr/sbin/ez-ipupdate -c - 2>&1
fi
sleep 1m
done
Ensure this script runs at all times; you might add this script to the list of local scripts you computer runs when you turn it on. Also, set good power management schemes, save on your energy bill when you leave the computer running alone at home.
Back at the office computer (name ezra), you must test both ssh connections and the public key you copied above:
facorread@ezra ~ $ cp /media/usb1/fabio.pub .ssh/
facorread@ezra ~ $ ssh -p11111 -i .ssh/fabio.pub fabio@aaron.dyndns.org
The authenticity of host 'aaron.dyndns.org' can't be established.
RSA key fingerprint is f6:7b:b5:07:43.
Are you sure you want to continue connecting (yes/no)? yes
Last login: Thu Jun 12 08:24:25 COT 2007
This account is currently not available.
Connection to aaron.dyndns.org closed.
facorread@ezra ~ $ ssh -i .ssh/fabio.pub aaron.dyndns.org
The authenticity of host 'aaron.dyndns.org' can't be established.
RSA key fingerprint is f6:7b:b5:07:43.
Are you sure you want to continue connecting (yes/no)? yes
Last login: Thu Jun 12 08:24:25 COT 2007
This account is currently not available.
Connection to aaron.dyndns.org closed.
"This account is currently not available" is the expected answer from the computer at home, because of the hardened security on the fabio account. If you succeed in the first attempt, you can connect to home using the insecure port. If you succeed in the second attempt, you will be able to connect using the secure SSH port, so you should configure the SSH server at aaron to listen only to port 22 as intended.
Use the following script to tell the computer at the office to connect to the computer at home and forward local port 22 to remote port 2222. This script uses the resolveip utility of the mysql package. By the way, if you find a better tool, please tell me. Installing mysql to use resolveip is like buying a hammer to kill a flea.
#!/bin/bash


remotehost=aaron.dyndns.org


while true; do
    echo $(date) $(resolveip "${remotehost}" 2>&1 ) &&
   
# This line renews the home IP address at the known host list.
    sed -i "s/^${remotehost},[^ ]\\+/${remotehost},$(resolveip -s ${remotehost})/" .ssh/known_hosts 2>&1 &&
    ssh -i ~/.ssh/fabio.rsa -o "BatchMode yes" -R '*:2222:localhost:22' "fabio@${remotehost}" -N 2>&1
    sleep 1m
done
Also ensure this script runs at all times as the normal user facorread, even thought there might be a power outage at the company. Include it in the list of local scripts to run at startup (or as a startup cron job).
Go home and try to connect to the office through the forwarded port using the command:
facorread@aaron ~ $ ssh -p2222 localhost
This is the only command you will need from now on to connect to the computer at the office. Most of the time the connection is straightforward; sometimes you will get Connection refused notices, due to the IP address of aaron being changed and the automatic connection being renewed. Wait a minute and try again. For the impatient, the following command helps check whether the tunnel is open.
tcp    127.0.0.1:22     0.0.0.0:*    LISTEN      sshd
tcp    127.0.0.1:2222   0.0.0.0:*    LISTEN      sshd
tcp    127.0.0.1:11111  0.0.0.0:*    LISTEN      sshd
This set up takes time and some effort on your part; please comment on your success stories. I hope it is useful.
For convenience, I recommend using connection sharing and public key authentication for the normal user facorread as instructed in the SSH man page.

Tuesday, December 08, 2009

Code Syntax Highlighting in your Blog Posts

Do you want C++ highlighted code in your blog posts? If you use Kate, you can export the HTML-formatted version of your source code, if you have enabled the HTML export extension. Use File>Save as HTML. Open the resulting file in Kate and replace the spaces with &nbsp; so the spaces are well defined in your post.

Copy the code snippet form Kate into the raw HTML editor of your blog, and there is it. For examples, check below.

Debug class implementation with templates

Here is the implementation of a debug reporting system in C++, with the same interface as cerr but with the plus of implementing a static debug level in your code.
template<int compiledDebugLevel>
class DebugCls {
  public:    /// Sends an object to the standard error.
    template<class T>
    DebugCls& operator<<(const T& a) const;
    /// Allows to use manipulators on the object.
    errorCls& operator<<(ostream& (*a)(ostream&));
    /// Allows to use the object as a function.
    DebugCls& operator()(const char* msg) const;
};

DebugCls<1> debug1; ///< Debug object for level 1.
DebugCls<4> debug4; ///< Debug object for level 4.
DebugCls<6> debug6; ///< Debug object for level 6.
DebugCls<10> debug10; ///< Debug object for level 10.

Reasonable limit for new neighbor requests which have fallen beyond the available memory for a person.
const int debugLevel = 0; ///< Chosen debug level for the current build.

template<int compiledDebugLevel>
template<class T>
DebugCls<compiledDebugLevel>& DebugCls<compiledDebugLevel>::operator<<(const T& a) const {
  if (compiledDebugLevel < debugLevel)
  cerr << a;
  return *this;
}

template<int compiledDebugLevel>

DebugCls<compiledDebugLevel>& DebugCls<compiledDebugLevel>::operator<<(ostream& (*a)(ostream&)) const {
  if (compiledDebugLevel < debugLevel)
  cerr << a;
  return *this;
}

template<int compiledDebugLevel>
DebugCls<compiledDebugLevel>& DebugCls<compiledDebugLevel>::operator()(const char* msg) const {
  if (compiledDebugLevel < debugLevel)
  cerr << "Program error: " << msg << "\n";
  return *this;
}
The functions have been written outline in this example so you get an idea of the required template nesting. However, these functions are short, so you might as well inline them.

Lexmark Z43 printer and Kubuntu 9.10

Hi. This post will present in detail the steps I took with the intent to fix a Kubuntu installation to use a USB Lexmark Z43 color printer.

The problem


Although this printer works fine under Windows (at least in the tests I did), this printer has been a source of trouble with Linux. In the current, updated Kubuntu installation, you can reproduce the following problem:
  1. In System Settings > Printer configuration > Lexmark (the latter is the name of the printer), click "Print test page."
  2. The Ubuntu test page is printed, up to a fraction of the Cyan color strip.
  3. The printer stops and the power light blinks at 0.5-1.5bps, in an irregular fashion. The computer does not report any problem with either communication or printing. The Form feed button of the printer does nothing.
  4. You press the power button, the printer takes a moment until the sheet comes out of the printer and it remains on.
  5. You cannot print anymore, and CUPS does not report anything. Frequently CUPS reports that the print job was finished. The syslog reports no problem.
 This problem does not depend on the following:
  1. The printer driver being either CUPS+Gutenprint or Gutenprint.
  2. The configuration of app-armor.
  3. Whether /proc/bus/usb is mounted.
  4. The debug level of the kernel: It still does not report anything useful.
I am so stuck with this printer, I hope somebody comes out with a solution. The problem persists with CUPS-1.3.11 in my Gentoo box.

Thursday, December 03, 2009

Threads and performance in scientific C++

I like the new C++ standard, with new, great features and tools for efficient programming. I cannot wait to try the new characteristics implemented in GCC.

My current programs make heavy use of threads; as many of you might know, threads tipically work with common variables and thread-local variables. The latter are only seen by the thread they are defined into, and they are implemented as a GNU extension to the ISO C++03 standard.

Thread-local variables in threads are a bit of a burden for the compiler, linker and ELF file format. Thread-local variables are ready for use with any number of threads, which means that they are not static.

I know that my simulations use two threads, because the running processors only have two cores. So for the sake of performance, I learned to use templates to implement static thread-local variables. Here is an example:

template<int iThread>
class ThreadLocal {
    protected:
    static int staticA;
};

template<int iThread>
int ThreadLocal::a;

template<int iThread>
class ClsA : public ThreadLocal<iThread> {
    private:
        typedef ThreadLocal<iThread> tLocal;
        int a;
};

template<int iThread>
class ClsB : public ThreadLocal<iThread> {
    private:
        typedef ThreadLocal<iThread> tLocal;
        int b;
        ClsA<iThread> embeddedA;
};

ClsA<0> A0;
ClsA<1> A1;

ClsB<0> B0;
ClsB<1> B1;

Each template instantiation has its own copy of the staticA variable. This certainly has given me good performance under GCC, but any comment on tests with different compilers is welcome.

A C++ trick: avoid #define

Hello, welcome to this space. I am a fan of C++; I use if for my scientific projects, with focus on high performance. So every now and then you will see a post about some trick for practical C++ programming.

Whenever you create a new program, the need for constants can arise; it is a common practice to use #define to solve the problem:
#define probability 0.75
You might be tempted to use #define because it only exists in preprocessing, so you are guaranteed memory is not allocated for the constant. Moreover, your definition can stand across source files by using #include. Your constant will also stand out of the rest of the code thanks to the syntax highlighting of your text editor.

However, using #define will prevent the compiler to help you check for the type safety and scope of the constant in your code. Here is an example inspired by Bjarne Stroustrup:

#define alpha 'a'
#define beta b[2]
class myCls {

int alpha;
int beta;
};

The g++ compiler will complain with
test.cc:8: error: expected unqualified-id before 'a'
So the bug about beta gets away and you might find yourself looking for the cause of some rare runtime bug months from now.
Your code must have room for improvement and evolution. It is not uncommon to have a simulation running OK but weeks later you want that precise constant to become a variable, so you want to repeat the program for a range of values. Given the preprocessor does not ensure type safety, it is likely that your program will not build when you finally switch from #define to const.
The computer is made to serve you, not the opposite. You should make all possible use of the power of your computer to help you check for type safety and overall consistency of the code you write. So take the opportunity of having the compiler give you a smoother programming experience by having const instead of #define. Use the compiler optimizations and learn about the cases in which const allocates space and cases in which it does not.