Why Docker is a Long Term Future for Platform?

What make Docker so popular and long-lasting?

  • Container is essentially OS level virtualization. Each application gets illusion of its own OS, having almost absolute control over it. Another advantage is that host OS knows about the container processes and hence can share its resources among hosted containers.
  • The concept of containers was started by FreeBSD, refined by Solaris and re-implemented by Linux.
  • Containers are better than two other levels of virtualization:
    • ABI/platform level, where application integrates with the platform (Google App Engine), doesn’t scale well.
    • Hardware level, where a virtual hardware runs the OS (e.g. virtual machines, hypervisors).
  • Docker containers run close to the real hardware, and host OS has knowledge of resource usage. Hence it’s an optimal sweet spot for virtualization.
  • Joyent SmartOS is built on OpenSolaris and provides Solaris features to Linux like Docker containers. It acheives that by allowing Linux APIs translated to Solaris APIs. Everything runs on the bare metal hence.
  • SmartOS containers get ZFS, Dtrace by default 🙂
  • SmartOS containers are very secure as they run in zones.

I hope to cover Smart OS design internals, Docker container on Linux details in next posts.

Reference

Advertisements

Linux Memory Management Tricks

Tips to Improve Dynamic Memory Performance

Instead of using memset() to initialize malloc()’ed memory, use calloc(). Because when you call memset(), VM system has to map the pages in to memory in order to zero initialize them. It’s very expensive and wasteful if you don’t intend to use the pages right away.

calloc() reserves the needed address space but does not zero initialize them unless memory is used. Hence it postpones the need to load pages in to memory. It also lets the system initialize pages as they’re used, as opposed to all at once.

  • Lazy allocation: A global(normal variable or a buffer) can be replaced with a static and a couple of functions to allow its access.

  • memcpy() & memmove() needs both blocks to be memory resident. Use them if size of blocks is small(>16KB), you would be using the blocks right away, if blocks are not page aligned, blocks overlap.
    But if you intend to postpone the use, you would increasing the working set of the application. For small amount of data, use memcpy().

  • To check heap dysfunctional behavior: $ MALLOC_CHECK_=1 ./a.out
    It’ll give an address related to each violation of dynamic memory routines.

  • Electric fence : Works very well with gdb

  • Libsafe: for libc routines

One More Reason to Avoid Ruby Language

Ruby is type unsafe language but it goes a step further and avoids checking dynamically too.

Consider this code

x = :abc
if x == 'abc'
  puts "Symbol and String are two different classes"
else
  puts x.class, 'abc'.class
end

# puts can print a symbol and string alike.
puts x

My Complaints

  • I’m new to Ruby. How could Ruby let a Symbol and String compare, in spite of being aware of their types? Like Python, it can throw an error.
  • How can puts print a Symbol as good as a String

References

Internals of Linux Process Signals

Linux Signals

  • A process in Linux is represented as task_struct.
  • A process A sends a signal to process B using system call kill() or kill -<sig num> <pid>.
  • Kernel updates the task_struct of the receiving process B. It changes the signal field with the passed value.
  • The scheduler checks the signals on a process before running the process. If the signal is set, it calls the signal handler.
  • A process can define its handlers for all signals except SIGKILL and SIGSTOP.
  • There is always a default signal handler for each signal.

References

Height of a Binary Tree: Recursion Unrolled & Explained


tags: >-
development, C++, tree, height, programming, recursion, internals, complete
flow
categories: development

Height of a Binary Tree is the longest path in the tree.

Code
int getHeight(node *root)
{
    if (root == NULL) {
        return -1;
    }
    int leftHeight = getHeight(root->left);
    int rightHeight = getHeight(root->right);

    return (max(leftHeight, rightHeight) + 1);     
}

Suppose the tree is as follows:

         [10]
          |
       [8]  [12]
              |
           [4] [5]

The recursion flow is as following:

1. getHeight(10)
2. getHeight(left of 10) ==> getHeight(8)
3. getHeight(left of 8) ==> getHeight(NULL)
4. getHeight(NULL) returns -1
5. leftHeight = -1 and flow goes back to line # 3
6. Now, it calls right of 8, getHeight(right of 8)
7. getHeight(NULL) returns -1 to rHeight
8. Both subtree of 8 are traversed and leftHeight = -1, rightHeight = -1
9. It compares both values and return the max + 1
10. max( -1, -1) + 1 = 0
11. The node 8 is left subtree of 10, and returns to line # 2.
12. At node 10, leftHeight = 0
13. Now, rightHeight is calculated by moving to right of 10.
14. getHeight(right of 10) ==> getHeight(12)
15. Similar to above, height at node 12 is calculated as 1.
16. At the end, node 12 returns its height at line #14.
17. Again we compare max (0, 1) + 1
18. The answer is 2   
Summary

Usually, all recursion steps are not visualized and just assumed to be working. This post tries to exhaust a recursion flow and help understand it better.

Notes on Dockerfile and Build Cache

Dockerfile is an instruction set to set up a new container. It looks like a BASH script that serially runs all the mentioned commands. The commands are predefined by Dockerfile syntax.

Unlike BASH script, Dockerfile runs and applies effects of a command to the output of the previous step. Each step of a Dockerfile creates, by default, a container which is kept hidden. You can list such ephemeral containers by running following command:

$ docker images -a 

All containers with <none> name are ephemeral.

Why Docker need Ephemeral Containers

  • Each ephemeral container acts as a cached output of a step in the Dockerfile.
  • Next container build would use the cached output instead of running the step again.

How it Works?

  • Each step starting from the from base checks if the next step has cached output.
  • The check is with the asked instruction and the instruction that was run by the cached output.
  • If instructions do not match, the cache is invalidated. The step is built normally.
  • To disable caching, provide the no-cache option.
    $ docker build --no-cache ...
    

Reference