Saturday, May 30, 2020

Lockdown 4.0

Lockdown 4.0 is due to end tomorrow, May 31.

It is not yet clear if the Lockdown will be further extended.

But it is hard to imagine that a full opening up of country is going to happen anytime soon.

Lockdown 4.0 has seen an explosion in the number of daily new cases. Before this edition of lockdown started, the average number of new cases reported daily were around 4000-5000 mark. But in the last 14 days, we have seen an unprecedented spike. Now, we are seeing a daily increase of around 7000 to 8000 new cases. So the situation has only gotten worse.

Increase in number of new cases has a lot to do with increased testing. I read somewhere that some states have started testing asymptomatic contacts of positive cases. This may have led to the spike we are seeing.

Most new cases in my state of Uttarakhand, have been been related to those migrants who are now heading back from various places in the country.

With ease on restrictions on travel, more cases are likely to emerge in places where we were earlier witnessing a flattening of curve.

With only 4 or 5 states accounting for majority of the active cases in the country, a partial opening up of the country is possible.

I don't think states like Maharashtra, Delhi or Gujarat are going to ease restrictions any time soon.

India has broken into the top ten countries in the world with respect to the total number of Covid19 cases. In my opinion, this was bound to happen.

Most experts say that we are going to see the peak of infection rate in India sometime in July.

With the current doubling rate, I think we are easily looking at more than 5 lakh cases by then.

Everyone says that actual number of cases are way more than the number that we have right now. People who are actually getting tested are either contacts of those positive or those that are manifesting severe symptoms.

And figures from China and Italy show that asymptomatic cases make up around 70-80% of total number of cases. This leads us to assume that the actual number of active cases right now in the country are far greater.

It also means that transmission is happening as we speak and will increase as we go beyond Lockdown 4.0.

One can't imagine how this is going to end. We're hoping that either the Virus mutates and becomes less dangerous or sufficient number of people get infected and we somehow develop herd immunity.

Let's wait and watch.

Friday, May 29, 2020

Conquering Everest






On 29th May, 1953, two men conquered Everest. Mount Everest, standing majestically around 29000 feet above sea level is the highest mountain in the world.

And it is every mountain climber's dream. The ultimate challenge. To scale its peak once in their lifetime.

There have been many who have achieved this heroic feat, but at the beginning there were two.

Their name were  Edmund Hillary. and Tenzing Norgay


 


Did you know that Mount Everest was initially called Peak XV. Only in the year 1865, it was renamed as Mount Everest, after Sir George Everest who was the Surveyor General of India in 1840s.

It also has been given its name in various languages of land like  Nepali, Tibetan and Chinese.

Hillary and Norgay were 33 and 38 years of age respectively at the time.

Mount Everest has Nepal China border going through its summit. At the time of their expedition, they chose to scale the peak starting from the south-east side of the mountain, since the northern side was China and may have been closed at the time.

The attack of Locusts


Around a couple of months ago, we had heard of the locusts attacks happening in Africa. At that time, the experts had said that the locusts will eventually make their way to the Indian Subcontinent.

Since at that time, we were already grappling with onset of Covid-19, no one payed any real attention to this news.

But the time has come to take this seriously. The Locusts have arrived.


We are not dealing with the locusts for the first time. They have troubled humans since time immemorial.

There are mentions of locust plagues throughout history.

The first pictures of locusts wreaking havoc this time around came from Jaipur. This tweet shows some horrifying pictures.







Now the Locusts are making their way to Delhi and other parts of Northern India.

Let's see what happens.!!

Thursday, May 28, 2020

A Good Phone

Our lives have been transformed by phones in the last decade. They have become an essential part of our lives. We cannot live without them. 

Thus choosing a phone is no trivial task. 

Before you spend, what normally is a substantial amount of money, you need to decide which phone you will buy. There is no dearth of options. There are too many choices. This makes it more difficult.

To start with you need to choose which mobile universe you will belong to. I remember, a decade ago, there were many universes. Android was just starting. Nokia had championed Symbian. Blackberry was the choice of professionals. IPhone was exploding. Microsoft was jumping into the fray with it's own Windows line up.

When the dust settled, there were just two standing, Android and iOS.

Android backed by Google and iOS backed by Apple. Both giants.

So, before you even start to looking up the phones, you need to decide which Universe you shall join, Android or iOS.

If you choose iOS, then there's just one company that manufactures those phones and that's Apple. And you have to shell out a decent amount of money. Because these phones are expensive. There isn't much choice, but you do get good quality, better security and some status ( at least in India ) to go with it.

If you go for Android, then there are many options. There are cheap phones that won't cost more than 5k-6k rupees. There are mid range phones that range from 10k rupees to 50k rupees. And, there are high end, flagship phones that will make you poorer by more than 100k rupees.

Let's talk about attributes of a decent phone.

There is the issue of RAM and Internal Memory. A phone that won't make you cry while you wait for your favorite app to launch should have at least 4 GB of RAM today. 

If you plan to document your life through photos and videos then you must go for nothing less than 128 GB of internal memory. The more the better in my opinion.

Talking of photos and videos, how can one forget the most essential part of any phone today. The Camera. Or should I say Cameras.

There are a whole of cameras in a single phone today. There is front camera. For when you want to take your own photo - popularly known as taking a selfie. Front camera should be good. The selfies should be great. 

There is back camera. And sometime there are more than one of them. To make your photo quality even better. 

Go for a phone that has at least 32 mega pixel camera. The photos and videos should be great, or else how would they trend on social media.

Wait, there's more. The battery. 

What use a phone is if it's battery dies every 5 hours. A phone worth its salt should be able to survive at least 24 hours on a full charge. A good battery with more than 4000 mAH capacity will do.

Display and CPU are two other things that you may want to worry about. But in my opinion, they are more or less the same on all medium to high end phones.

The size of the phone also does matter. If you have small hands, it does become cumbersome to hold the bigger phones for an extended period of time. 

Unfortunately, there are no phones with good specs and small screen size available in market right now. 

Also the weight of the phone. Do checkout the weight of the phone while selecting. A heavy phone becomes annoying if you use it for a long time. A lighter phone is easy on your wrists.

Happy phone search.

Monday, May 25, 2020

Lockdown and Working from home

Covid-19 has forced most of us to work remotely. This sudden drastic change in our work life has made some of us rethink about the viability of working remotely over an extended period of time.

After slogging in offices for more than ten years, I had started to think that working from home would be the change I want in my work life. It would make my life more relaxed. It would do away with the excruciating commute. The noise at office, the constant interruptions from others and loads of other factors that hinder your focus - all of them would go away. It was like a dream.

When Covid-19 started and lockdown happened, I realized that maybe the long standing dream is coming true. Since everyone was now forced to work from home, this practice would become more mainstream.

Traditionally working from home was not seen as a viable option for large ( or even small) teams for various reasons. Let's look at some of them.


  • If a few members of a team work remotely while rest are working from office, it was thought that the remote workers would miss out on essential communications that happen in office informally. There may be important decisions taken or complex issues discussed in impromptu face to face interactions that the remote workers will never be a part of. Even if a summary of such decisions or discussions is later shared in mail with them, they may not get the same feel for it had they been present themselves.
  • Lack  of socializing for the employees. Apart from the work that one accomplishes, a large part of the work life today is about meeting people at work place, developing friendships with like minded people and developing long term relationships with people you meet during the course of your work. But with remote work, the majority of time you would spend would be at you home office or a co-working space with you attached to your laptop for most part of it. This may lead to development of a certain kind of fatigue due to lack of enough real life social interactions on a day to day basis.
But there were some benefits as well. 
  • Ability to focus to hard problems without any interruptions.
  • Save time and money that was earlier wasted on commute. 
  • You can live anywhere as long as you have a good internet connection
  • Working hours and their time can be flexible

Most people wanted to find a middle path. They would go to office only when needed and work from home when the work could be any without anyone's intervention.

This is the kind of flexibility most of us were looking for. I didn't anticipate what it would be like to be forced to work from home over an extended period of time.

That is what happened with the arrival of covid-19 and subsequent lockdown.  

Working from home doesn't seem that attractive anymore. 

This change of heart could be due to a few reasons. Let's look at them.


  • The need to socialize for us to have a stable work life may be more important than we initially thought.
  • Going to office and working there, makes it easy to leave the work related stress behind when you get back to home. The commute works as some kind of insulation layer between the two environments. 
  • Lack of experience with respect to setting up a home office. Getting the right kind of lighting, the right furniture etc. 
  • Maybe due to lockdown, most people have their families at home now. So the distractions haven't gone away. They have just changed their form. Now you could have to Kids/Spouse/Parents, who could be the one disturbing you.

This has been my experience along with that of some people that I have spoken to.

With Major companies like Google and Twitter now allowing their employees to work from home for the foreseeable future, other companies are likely to follow suit.

How will this change our work culture? The situation is still evolving and we are living in interesting times. 

Let's wait and watch how it turns out.

Friday, May 22, 2020

Dunning–Kruger effect in real world - Of Uber drivers, Torture Movies etc.

I recently came upon the study done by David Dunning and Justin Kruger in 1999, which describes a bias that people may have, that makes them overestimate or underestimate their competence at a certain task.

A person who has little knowledge in a particular field may tend to overestimate their competence or skill level in that field.

While on the opposite side, a person who has a fair amount of knowledge in a particular field may tend to underestimate their competence in that field.

Now, I am sure I have seen multiple examples in my life where this study seems to hold true. So must have you. 

Sometime back I took an Uber. The driver was a middle aged fellow, who looked like he had a lot of experience in cab business. But this guy drove rashly. I literally held on to the roof handle in the back seat the whole ride, fearing for my life, dreading that this guy would crash the car as soon as his luck runs out. I did try to persuade him to drive sanely, but to no avail. He mentioned something about him being the best driver in his driver friends group. When I heard his claim, the first thing that came to mind was that he must be delusional. At least his Uber rating did not seem to concur with his view of himself. I guess, that was Dunning-Kruger effect at play.

So this study seems to be intuitive and does seem to stand with some events I have experienced  or some people I have met. And to take it further, we may say that someone suffering from such a cognitive bias, will tend to screw up the task at hand most of the times.

Take the example of Indian Movies. There are some masterpieces made in Indian film industry that have gained the cult status of being called torture movies. 

A Torture movie is a movie that you would show a suspect to make him confess to his crime. In Indian Jokelore, a torture movie is employed by the police when even the third degree torture fails to get any information or confession from the suspect.

When you think of Torture movies, the first guy that comes to mind is Sajid Khan. This guy has such gems as Humshakals and Himmatwala to boast of. The situation here surely reeks of Dunning-Kruger effect.

Dunning-Kruger effect is evident in workplaces too. Especially in software industry here in India. I have seen plenty of technical leaders get into project management. There is some sort of unspoken code here in Indian SW Industry that to be successful, this is the path one must take. 

And given the herd mentality that most Indians live by, it's not surprising that most of the technical guys do take the next logical step and become project managers. 

Now some of these guys have no clue. They come into project management with confidence gained by working on the technical stuff for more than 8 to 10 years. But being good at technical stuff, doesn't naturally translate into being good at managing a team of humans and getting the best out of them, while still keeping them happy and productive over the long run.

Hence they end up making life miserable for their team. This is certainly not true for everyone out there, but its applicable on a large enough percentage that it deserves a mention here.

Well there must be more examples out there. Will write about them when they come to my mind. 

What other examples of Dunning-Kruger effect do you see in real life? Do share in the comments. Thanks. 

Thursday, May 21, 2020

Thoughts on Korean Series Signal



I recently finished watching the Korean series Signal on Netflix. It has sixteen episodes and each episode is around an hour long. It took me around 2 weeks to finish it.

The series is very good. Though I have had my fair share of Korean cinema, this was the first Korean series that I watched. I stumbled upon the series when I read this article on BBC website.

The show was engaging from the very beginning. There are enough twists and turns throughout the season that will keep you watching the series till the very end. The acting from everyone in the cast is top notch, especially from the three lead characters.

I would refrain from revealing the plot details or any spoilers in this blog post. The show must be watched without reading anything about it's story. That would make it more enjoyable.

This blog post is about the various things that I noticed about the Korean culture and way of life while watching this series.


Korean Names

In India and the I assume most of the western world, a person's name comprises of a first name, a middle name and a surname. When you call out someone's name in full, you would generally start with their first name and then call out their last name. Middle name is normally left out.

While watching this series, I noticed that in Korea the last name is called our first followed by the first name. For example, one of the lead characters has the name Park Hae-young. Where Park is the Surname and Hae-young the first/given name.

Other names in the series follow the same pattern.

Cha Soo-hyun where Cha is the surname and Soo-hyun is the first/given name.
Lee Jae-han where Lee is the surname and Jae-han is the first/given name.

Middle names are missing.

Reading up on wikipedia, I found that korean surnames ( or family name as it says in the wiki article ), are based on clans, which normally trace their origin to the place or region where it started. A single surname is associated with multiple clans. So, a two person with surname 'Lee' could belong to different clans, but I don't know how that is deciphered based just on the given name and the family name.

Another interesting thing that the article mentions is that as per census conducted in the year 2000, there were just 286 surnames being used at that time. The most common surnames are Kim, Lee and Park, which account for nearly half of the population. This frankly blows my mind since I am an Indian.

Considering the number of names we have here in India, which are based on a variety of things like religion, caste, sub-caste, place of origin, history, occupation etc, I wonder how the naming conventions reflect on the complexities and nature of the societies that use them.

Does a lot of variety in names indicates the extent of stratification in the society? It seems so.   


Grey Characters

All characters in this series, barring one, were either good people/heroes or bad/villains. There were no grey characters. A character whom you are not able to figure out. Lack of such characters does make it easy for you while watching a drama series as this. You are easily able to identify and love some characters, while hate those that you know are up to no good.

But is real life so simple. Is everyone as bad to the bone or as good at heart as shown in this series?

Crying

There is a lot of emotional moments in this series. And there is a fair share of crying, not excessive though and well placed. What's interesting is that most of crying is done by men.

Having watched a lot of stuff from Hollywood and Indian film industry, we are used to men being shown as tough macho characters who wouldn't give a damn about crying.

I won't pass any kind of judgement until I have watched some more Korean series, but I would definitely like to see a trend related to this and how it dictates the kind of stories that Korean storytellers like to tell. How does it shape their culture, their identity and does the same feeling exists across the northern border.   


Well there are a lot of other things that I would like to discuss. Maybe I will write a follow up post.

Monday, June 10, 2019

Removing boilerplate from webpages using python

I am an avid Firefox web browser user and more often that not, when I am visiting a webpage containing an article, I end up clicking on the "Reader Mode" button in the address bar so that I can remove all the useless noise and just focus on the main content that the page has to offer.

A news article, like this one shown below

becomes like this after entering the reader mode.
As you can see, all the ads, boilerplate etc is gone and now I can focus on the actual content without straining my eyes to find the stuff that I visited the webpage for.


It turns out that one could easily write a python program ( python being my language of choice for such quick experiments ) to do the same thing.

We will use the python readability library to achieve this thing in our python program.

 Here is the program. Seems to work for me.



Friday, March 29, 2019

Using Regular Expressions in Go

Go language provides package regexp for making use of regular expressions in Go programming. The syntax of the regular expressions is almost identical to that supported by other languages like Perl, Python etc.

Let's start with the basics. We will try to use regexp package to find out if a given text contains any alphanumeric words. By alphanumeric words, we mean any word that is comprised of letters or numerals.

To do this, we will compile a regexp object out of a regular expression. Then we use this regexp object on input strings to check if they contain any alphanumeric words or not, and if they do, we print each one of them.

Here is a program for doing this.




Tuesday, March 5, 2019

Writing the simplest HTTP server in Go

To write a very simple, HTTP server in Go, that serves just a single static page, you just need to do the following.

  • Import the net/http package
  • start listening on a port of your choice using http's ListenAndServe
  • server requests using a HTTP ResponseWriter.

Here is the code for this:-
package main
import (
"net/http"
"log"
)

func viewHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello there..."))
}

func main() {
    http.HandleFunc("/", viewHandler)
    log.Fatal(http.ListenAndServe(":8087",nil))
}
Using http.HandleFunc we choose to handle incoming requests on '/' path using the viewHandler function. After this, we start a ListenAndServe() function to start handling incoming HTTP requests on 8087.

Go ahead and build this and then run the exe. Your server has started. Now, open the browser and goto http://localhost:8087/

You should see a 'hello there...'.

Go has a pretty powerful HTTP library that allows you to do a lot of things. We shall explore more in the future.


Friday, February 22, 2019

File Handling in Go - Part 2

In the Part 1 of this lesson, we learned how to open, read and write to files using Go Language. In this lesson, we shall learn about other file handling related functions provided by Go language.

Seeking

We often need to seek into a file to a particular point and then do read or write. For there is a seek() function provided by os package.


func (f *File) Seek(offset int64, whence int) (ret int64, err error)

 Seek() function takes two arguments, offset and whence. whence tells the function from where to seek and offset tells how much to seek. 
whence can have one of the following values:
  • 0 - Seek From beginning
  • 1 - Seek from current position in file
  • 2 - Seek from end
So, e.g.

Seek(0,0) is going to seek to the start of the file.
Seek(0,2) is going to seek to the end of the file.
Seek(10,0) is going to seek 10 bytes from the start of the file.

Let's look at a program that uses the Seek() function.


package main
import (
    "fmt"
    "os"
)

func main() {
    f,err := os.Open("file-1.txt")

    if err != nil {
        fmt.Println("Error in Opening file",err)
        os.Exit(1)
    }

    //Seek 10 bytes from the start of file
    offset,err := f.Seek(10,0);

    if err != nil {
        fmt.Println("Error in Seeking",err)
        os.Exit(1)
    }

    // Make a buffer to read data from the file
    buf := make([]byte,10)

    //Read some bytes from the file
    readbytes, err := f.Read(buf)

    if err != nil {
        fmt.Println("Error in reading from file",err)
        os.Exit(1)
    }

    //Print the data read from file
    fmt.Println("Data read from file from offset",offset,readbytes)
    fmt.Printf("%s\n",buf)


    //Seek to the start of the file
    offset,err = f.Seek(0,0)

    if err != nil {
        fmt.Println("Error in seeking to front",err)
        os.Exit(1)
    }

    // Read some bytes from the start of the file
    readbytes,err = f.Read(buf)

    if err != nil {
        fmt.Println("Error in reading from file",err)
        os.Exit(1)
    }

    fmt.Println("Data read from file from offset",offset,"Total bytes read",readbytes);
    fmt.Printf("%s\n",buf)


    //Seek to the end of file
    offset, err = f.Seek(0,2)

    if err != nil {
        fmt.Println("Error in seeking to end",err)
        os.Exit(1)
    }

    fmt.Println("File-Offset",offset)
}

So, I created a file called file-1.txt and ran the above program. Notice that, at the end of the program, we seek to the end of the file. If you attempt to read further from this point on, you should get a EOF error.

The one thing, that you need to remember while using the Seek() function is that the behavior of Seek() is unspecified if the file has been opened in APPEND mode. So you should avoid using Seek() when file is opened in APPEND mode.


File Stat - Getting to know about a file


The stat() function provides us with useful information about a file that already exists in your system.

func (f *File) Stat() (FileInfo, error)

Stat() function returns a FileInfo object that contains all the information about the file. 


FileInfo provides relevant information about the file.


type FileInfo interface {
 Name() string       // base name of the file
 Size() int64        // size of file
 Mode() FileMode     // file mode bits
 ModTime() time.Time // modification time
 IsDir() bool        // Is file a directory
 Sys() interface{}   // underlying data source (can return nil)
}


Here is a sample program, that shows the usage of stat() function.


package main

import (
    "fmt"
    "os"
)

func main() {
    f, err := os.Open("file-1.txt")

    if err != nil {
        fmt.Println("Err in Opening file",err)
        os.Exit(1)
    }

    finfo, err := f.Stat()

    if err != nil {
        fmt.Println("Err in file stat",err)
        os.Exit(1)
    }

    fmt.Println("Name of the file:", finfo.Name())
    fmt.Println("Size of the file:", finfo.Size(), "bytes")
    fmt.Println("Mode of the file:", finfo.Mode())
    fmt.Println("Is the file a directory:", finfo.IsDir())
}


Alternatively one could simple use the Stat() function by providing the name of file. In that case, you don't need to open the file for a file object.   

    finfo, err := os.Stat("File-1.txt") 


There are a whole lot of other functions provided by os package, that you can find in Golang OS package documentation. Check it out and use the functions as per your need.

Feel free to comment on what is missing or could be enhanced in this lesson. 

Monday, February 18, 2019

Books worth reading

Being an avid reader has its advantages. You get to delve into worlds that only exist in someone else's imagination. You learn a lot from experiences of those who have put them down on paper for everyone else to read.
When I was a kid, I remember that I mainly read classics, from Charles Dickens to Arthur Conan Doyle.
There was a time when I got really fascinated by murder mysteries and I read almost all of Agatha Christie's Hercule Poirot books.
As I grew up, I started reading non-fiction books as well. 
When I was down or lost or just needed some inspiration, I sought help from some of the great self-help books out there.
Starting on a job after college took  a toll on my reading, but I did not give up on it altogether.
Last five years or so, I have really been into science fiction books. Recently finished the Three Body Problem Trilogy by Cixin Liu, and now moving onto Anathem by Neal Stephenson.

As a side interest, I would start compiling a list of books that I have already read or plan to read. This list would be regularly updated on this page and from time to time, I may also write about the books that I read.

Here is the list.

Science Fiction Books
  1. Animal Farm
  2. Snow Crash 
  3. The three body problem
 Non Fiction Books
  1. Thinking Fast and Slow
  2. Meditations
  3. The Selfish Gene

Saturday, February 16, 2019

File Handling in Go - Part 1

Go language provides os package for using operating system services and that includes file handling.

Let's go through a step by step process and learn how to open files and read and write to them in Go Lang.

Opening a file

Go provides two function calls, Open() and OpenFile(), to open a file. Here is the function declaration of these two:-

func Open(name string) (*File, error)

func OpenFile(name string, flag int, perm FileMode) (*File, error) 

Open() takes the name of the file to open as an argument and returns a file handle ( or object ) and error if there is any.The thing we have to keep in mind is that when we open a file using Open() function call, it opens in a read-only mode. So you can only read from the file and not write to it. To be able to write to a file, we would have to use the OpenFile() function.

To start with let's use the Open() call to get a hang of it. Here's the program for it.

package main
import (
    "fmt"
    "os"
)

func main() {

    filehandle,err := os.Open("hello.txt")
    if err == nil {
        fmt.Println("File Opened successfully", filehandle)
    } else {
        fmt.Println("Error :", err)
    }
}

So, I created a file named hello.txt in the same directory as my Go program and then ran this program. In the above program, we import the package os that provides us with OS interface. We call the Open() function and check for error. If there is no error, we print the file handle, which is basically some number.

Reading from a file

So that was a pretty basic example. Now let's try to read something from the opened file. For that we will use the following function call provided by os package.

 func (f *File) Read(b []byte) (n int, err error)


Read() function takes as an input an array of bytes, b, into which it will fill whatever it reads from the file. It returns the number of bytes it read from the file, n, and error if there was any.

Let's enhance the above program to read something from the file, hello.txt.

package main
import (
    "fmt"
    "os"
)

func main() {

    filehandle,err := os.Open("hello.txt")
    b := make([]byte, 20)

    if err == nil {
        fmt.Println("File Opened successfully", filehandle)

        n, err := filehandle.Read(b)

        if err == nil {
            fmt.Printf("Read Success. N=[%d], Text=[%s]", n, b)
        } else {
            fmt.Println("Read Error:",err)
        }

    } else {
        fmt.Println("Error :", err)
    }
}


So we added a byte array, b, that is allocated a space of 20 bytes. We called Read() function using the file handle that we received when we opened the file using Open(), and then we printed whatever we read from the file in case there was no error.

Writing to a file

So that was pretty simple, I hope. Opening and Reading from a file in Go is quite straightforward. Now, let's explore how to write to a file.

Remember that Open() function opens a file in read-only mode. To be able to write to a file, we need to use OpenFile() function. First, let's understand how to use the OpenFile() function.

func OpenFile(name string, flag int, perm FileMode) (*File, error)

The first argument of the OpenFile() is the name of the file that we would like to open.
Next is an integer called flag. flag can take one of the following values.

const (
 // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
 O_RDONLY int = syscall.O_RDONLY // open the file read-only.
 O_WRONLY int = syscall.O_WRONLY // open the file write-only.
 O_RDWR   int = syscall.O_RDWR   // open the file read-write.
 // The remaining values may be or'ed in to control behavior.
 O_APPEND int = syscall.O_APPEND //append to the file when writing.
 O_CREATE int = syscall.O_CREAT  //createnew file if none exists.
 O_EXCL   int = syscall.O_EXCL //used with O_CREATE,file must not exist.
 O_SYNC   int = syscall.O_SYNC   //open for synchronous I/O.
 O_TRUNC  int = syscall.O_TRUNC  //truncate file when opened.
)


So, if we would open a file for read-only purpose, the value of the flag will be O_RDONLY. If we open a file for only writing, then flag shall be O_WRONLY and so on.

As documentation above suggests, flag can only have either one of the following values O_RDONLY, O_WRONLY, O_RDWR. The rest of the values in the above block can be ORed with any of the first three, depending on the requirement.

For example,
If we wanted to open the file for Reading and writing, and we wanted that when we write to the file, the data should be appended to it, we would have the value of flag as
O_RDWR | O_APPEND. 
And if we wanted to want to open a file for writing and if the file does not exist we would like have it created, the value of the flag would be
O_WRONLY | O_CREATE
  

Next Argument, perm, is of type FileMode. This argument defines the kind of permissions that this file has and what kind of file it is. It is a 32 bit integer, where the least significant 9 bits, represent the standard unix permissions like rwxrwxrwx. So, while creating the file you can specify the kind of permissions that this file is going to be created with.

The Other bits in the FileMode have their own significance, but we will not cover them in this lesson.

So, now that we have understood each argument of the FileOpen() function, now let's write a short program, that will write some text to a file.

To write to a file, we shall use the Write() function.

func (f *File) Write(b []byte) (n int, err error)

The Write() function, takes a byte array as an argument and writes the contents of this byte array to the file. It returns the number of bytes written to file and error if there is any.

Here's the program that depicts usage of FileOpen() and Write().

package main
import (
    "fmt"
    "os"
)

func main() {

    f, err := os.OpenFile("gotest.txt",os.O_RDWR|os.O_CREATE,0777)

    if err == nil {

        fmt.Println("File Opened for writing successfully")

        b := []byte("Hello there")

        n,err := f.Write(b)

        if err == nil {
            fmt.Printf("Written %d bytes successfully",n)
        } else {
            fmt.Println("Error in Writing to file",err)
        }

    } else {

        fmt.Println("Error in Opening file", err)

    }
}

In the above program, we opened a file "gotest.txt" with flag O_RDWR | O_CREATE and file permission as 0777. We wrote a byte array containing the text "Hello there" to the file. We check for error as always before declaring success.

Well, that is it for this lesson. We shall see other File Handling related functions provided by os package in the next lesson.

Part 2 of this lesson is now available.

References:

Friday, February 15, 2019

User input in Go language

Taking a user input in Go language is easy. Let's go through a few examples on how to do this.

We shall use the function provided by the fmt package to take user input.

Reading an Integer from console

Look at the example program below. Here we ask the user to input an integer and we use the Scan function of the fmt package to store the input into a integer variable. Then we go on to print it's value to confirm that our program worked correctly.

package main

import "fmt"

func main() {
    var a int
    fmt.Printf("Enter an integer -> ")
    fmt.Scan(&a)
    fmt.Println("You entered:", a)
} 


Reading Text from console
 
Now, let's say that you need to read a text from the console. In that case we can use the function provided by the package bufio.

Here is an example:

package main

import (
    "fmt"
    "bufio"
    "os"
)

func main() {
    var mytext string
    var err error

    txtReader := bufio.NewReader(os.Stdin)

    fmt.Println("Enter some text ( Press Enter when done ) ")

    mytext, err = txtReader.ReadString('\n')

    if err == nil {
        fmt.Println("Here's what you wrote: \n", mytext)
    } else {
        fmt.Println("Error: ", err)
    }

}
As you can guess already, in the above example, we use the function NewReader of the package bufio, to return us a reader object that will help us read from the standard input i.e. the console.

We then use the ReadString function of the reader object and tell it that the input will terminate with a newline character.

We check for error and then we print the input text.

In the above example, the reader object returned by the function call bufio.NewReader()  has a read buffer of default size, which is 4096 bytes. If the text to input from console is larger than 4096 bytes, then we can use bufio.NewReaderSize() API to specify the number of bytes you desire to input.

Monday, February 4, 2019

Understanding Pointers - Part 2 - Usage and Pointer Arithmatic

In Part 1 of this lesson, we explored the definition of pointers. We also saw how to declare pointers of various types and how to use * operator to print the values stored at an address assigned to a pointer.

Having made ourselves familiar with basic pointer syntax, we can now go ahead and start looking into how pointers are used in day to day programming and while doing this we shall also learn another key concept related to pointers called Pointer Arithmatic.

We shall do this by going through a series of example programs. I would encourage you to type each example program in your favorite editor and compile and run the program.

This will make you more and more comfortable with pointers as we go forward.

Have a look at the program below:-

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int *ptr = NULL;
    
    ptr = (int *) malloc ( sizeof(int) * 10 );
    
    if ( NULL == ptr )
    {
        printf("Unable to allocate memory, exiting\n");
        exit(EXIT_FAILURE);
    }
    
    printf("Starting Address of allocated memory space => 0x%x\n",ptr);
    
    ptr = ptr + 1;
    
    printf("Address after jumping one ahead => 0x%x\n",ptr);
    
    ptr = ptr - 1;
    
    printf("Address after jumping one back => 0x%x\n",ptr);
    
    free(ptr);
    
    exit(EXIT_SUCCESS);
}

In the above program, we have done a lot of new things that you didn't encounter in the first lesson. Let's go through this program line by line and understand what's happening.

  1. We declare a integer pointer 'ptr' and assign it a value of NULL. In the first lesson we didn't assign any of our pointers to NULL value to start with. But we should have as it is considered a good practice.
  2. Then we use the dynamic memory allocation function, malloc(), to allocate a memory space of size of 10 integers. If malloc() is successful, it shall return us a pointer which shall be stored in the pointer variable 'ptr'. As expected, the value in ptr is a memory address. This memory address is the starting address of the memory space allocated to us. The total length of this allocated space is the size of 10 integers, which on most machines is going to be 40 bytes - 4 bytes for each integer.
  3. The next block of code, we do some error handling. We check if malloc() was successful in allocating memory or not. If malloc() fails for any reason, it shall return a NULL value. It is always recommended that we check the return value of malloc() to see if we have really been allocated any memory or not. If malloc() does return a NULL, we exit the program after printing an error message.
  4. Next we print the value of the ptr. This shall be the starting address of the block of memory allocated to us.
  5. Next, we add 1 to ptr. This is the interesting part and our first leap into the world of pointer arithmatic. Normally if we add 1 to an integer variable, we shall get a value that is incremented by one. e.g. if I have an integer that stores a value 2 and I add 1 to it, I will get the value 3. But with pointers something different happens. In our case, when we increment 'ptr' by one, it jumps to the next integer address in the memory space. So after executing this line of code, ptr shall now be storing the address taken up by the next integer in our allocated memory. This address shall be 4 more in value than the starting address, because as we have already discussed, an integer takes up 4 bytes of space. 
  6. Next we print ptr. This, as we understood, in the last point, shall be the address of next integer in the allocated memory. It's value shall be 4 more than the starting address of allocated memory.
  7. Next we subtract 1 from ptr. We saw that when we added 1 to ptr, it jumped one integer ahead. Similarliy, if we subtract 1 from ptr, it should jump 1 integer back. That's what happens. ptr will jump back 1 integer and and shall now again be pointing to the starting address of allocated memory, where it was pointing originally.
  8. Next we print the value of the ptr.
  9. Next, we call the free() function to release the allocated memory. This is again a very important step. You should release any dynamically allocated memory as soon as you have no more use for it.
  10. We exit the program.
Here is the output that I get when I run the above program:

Starting Address of allocated memory space => 0xa32a98

Address after jumping one ahead => 0xa32a9c

Address after jumping one back => 0xa32a98

Note, than after jumping the pointer one step ahead, we get an address that has a value 4 greater than the starting address. This indicates that an integer is allocated 4 bytes on my system.

In the last program, we dynamically allocated some memory from heap and showcased pointer increment and decrement operations. We can also use pointer arithmatic operations on memory declared on stack or some global or static memory. And this is actually how pointer arithmatic is most commonly used.

Let's check out another program where we use memory allocated on stack.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int arr[5] = {1,2,3,4,5};
    int *ptr = NULL;

    ptr = &arr[0];
    printf("Value stored at Address [0x%x] => %d \n", ptr, *ptr);

    ptr++;

    printf("Value stored at Address [0x%x] => %d \n", ptr, *ptr);


    ptr++;

    printf("Value stored at Address [0x%x] => %d \n", ptr, *ptr);


    ptr++;

    printf("Value stored at Address [0x%x] => %d \n", ptr, *ptr);


    exit(EXIT_SUCCESS);
}

Here is the output that I get when I run the above program:-
Value stored at Address [0x61ff18] => 1
Value stored at Address [0x61ff1c] => 2
Value stored at Address [0x61ff20] => 3
Value stored at Address [0x61ff24] => 4

In the above program, we declare an array of 5 integers. We take a pointer to an integer at point it to the address of first element of the array.

Then, we keep incrementing the pointer by one and printing the address stored in the pointer and value stored at that address.

I hope you have got a little sense of how we can use pointers in our programs.

We shall elaborate on the uses of Pointer arithmatic more in the next part of this lesson.

Monday, December 17, 2018

Understanding Pointers - Part 1 - Basics

Pointer is variable type in C Programming language, that is used to store a memory address. Being able to store a memory address in a variable and then use it turns out be a great asset when you are doing low level programming.


Basics

A Pointer is a variable type that stores a memory address.

Note: When we say 'memory', we will assume we are talking about the RAM, where your program is loaded before execution.

On Memory and addresses.

Before going further, we need to get some minimal basic understanding of memory. This is crucial to successful understanding and use of pointers in C.

Let's say that you have a 4 GB RAM installed in your computer. There are (4 * 1000 * 1000 * 1000) bytes in your RAM. Now, each byte stores some information. To access information stored in a particular byte or to store some information in a that byte, the CPU needs to identify that particular byte somehow. And for that, we have a memory address. Each byte is assigned a unique number that is called its address, its memory address.

Normally, from a programming prespective, we just need to remember that memory addresses are a set of sequential numbers, starting at some number and ending at another. 

So, a pointer variable basically stores a number. A number representing a particular location in your computer memory.

Declaring a pointer

To declare a pointer variable in your code, you must first need to decide what kind of variable is going to be stored at that memory address.

For example, you could have an integer stored at that memory address. Or it could be a character. Or a float. etc.

So, to declare a pointer in your code, you must know what's going to be stored at that address in memory.

To start with, we will deal with integers.

Let's say I have a integer variable, var, and I have assigned it a value of 10. Like this:

int var = 10;

Now, var is stored somewhere in memory. To know the memory address where var is stored, we would declare a pointer variable that stores an address of an integer. Then we would assign the address of var to the pointer variable. Look at the code below:

int var = 10; // var declared and assigned a value of 10
int *ptr;    // a pointer variable 'ptr' declared
ptr = &var;   // Value of var's memory address stored in ptr

Let's understand, step by step, what we did here.
  • First, we declared an integer variable var and we assigned it a value of 10.
  • Then, we declared a pointer variable ptr using the * operator. Using this syntax tells the compiler that 'ptr' is a pointer variable, that will store memory address of an integer.
  • Then, we assigned the address of var to ptr, using & operator. & operator is used to access the location of an object in C.
Here's a complete program, to print the address of var, using a pointer variable.

#include <stdio.h>
int main()
{
    int var = 10;
    int *ptr;
    
    ptr = &var;
    
    printf("0x%x",ptr);
}


Here is the output that I get when I run this program:

0x48d51dac

Try running this program on your computer and you may get a completely different memory address printed. That's ok.

The exact memory address assigned to any variable in our program, depends on a whole lot of factors and is out of scope of this lesson. As long as you were able to get some memory address printed, we are on the right track.

Note, how in the above program, I printed the ptr using a %x in printf. There's no special reason behind doing this. It's just more of a habit to represent memory addresses as hexadecimals.
We could have printed the memory address as an integer too. But, in the rest of the lesson, I shall print memory addresses in hexadecimal format as a convention.

Practicing declaring pointers

Now, remember I said that to declare a pointer we must know what kind of variable is going to be stored in the memory address represented by the pointer. To declare a pointer that stores address of an integer, we declared it like this:

int *ptr;

If we read the above statement backwards, we shall get some idea of how to declare a pointer variable. Let's read this statement backwards like this:

Address stored in ptr points to 'int' or 'integer'. Meaning, if you will go to the address stored in ptr, you will find an integer stored there.

Let's try another declaration:

int *newptr;

Reading the above declaration backwards -> Address stored in newptr points to 'integer'.

Another one:
char *ptr;

Reading this backwards -> Address stored in ptr points to a 'character'.

Now, let's declare a pointer that stores address of a float number.

float *ptr; // Address stored in ptr points to a floating point.

Let's write a program to declare different kind of pointers:

#include <stdio.h>
int main()
{
    int a = 10;
    char b = 'A';
    float c = 3.14159;
    double d = 2.71828182;
    
    int * ptr1;        // address stored at ptr1 points to integer
    char * ptr2;       // address stored at ptr1 points to character
    float * ptr3;      // address stored at ptr1 points to float
    double * ptr4;     // address stored at ptr1 points to double
    
    ptr1 = &a;
    ptr2 = &b;
    ptr3 = &c;
    ptr4 = &d;
    
    printf("0x%x \n", ptr1);
    printf("0x%x \n", ptr2);
    printf("0x%x \n", ptr3);
    printf("0x%x \n", ptr4);

    return 0; 

}


Here is the output that I get, when I run the above program:

0x215c78f0
0x215c78ef
0x215c78f4
0x215c78f8

I would encourage you to type this program in your favorite editor and compile and run it on your machine. This will make you more comfortable with pointer notation and then we can starting looking ahead at how we use pointers in C programming.

Printing the value stored at a memory address

We have understood that a pointer stores a memory address. We can use the * operator, to print the value stored at the memory address in a pointer.

Let's see how to do this:

int * ptr;
int a = 10;
ptr = &a;

printf("%d \n", *ptr);

As we can see, the * operator is used to access the value stored at the memory address in a pointer.

Once we apply the * operator to a pointer, we get the value at that memory address and we can use it, just like we use any other value in our program. Like, assigning it to another variable.

int * ptr;
int a = 10;
ptr = &a;     // ptr now stores the address of a
int b = *ptr; // b now gets the value stored at memory address of a, which is 10

Here's the full program:

#include <stdio.h>
int main()
{
    int *ptr;
    int a,b;
    
    a = 10;
    ptr = &a;
    
    b = *ptr;
    
    printf("a=%d b=%d \n",a,b);

    return 0; 

}


Here's the output that I get for the above program:-

a=10 b=10


I would encourage you to try running each of the example programs shown in this lesson. That's it for this lesson.

In the next lesson, we shall see how pointers are used in C language to accomplish various tasks and also learn about pointer arithmatic.

Saturday, December 15, 2018

String tokenization in C

Let's say you have to write a C program to tokenize a string that contains a list of tokens separated by some delimiter, say a comma.

You could figure out your own algorithm for doing that, but usually a saner approach is to find a library function that already does that.

And C Programming language library does provide a function, called strtok that does exactly that. You provide it with a string and a delimiter string, and it helps you in splitting the string based on that delimiter.

So instead of writing our own function, we shall go forward and try to use strtok for our task. In doing so, we shall learn how to use it and other such functions.


Here is the function declaration of strtok function:

char *strtok(char *str, const char *delim);


From the function declaration above, we can see that strtok takes the string as its first argument and the delimiter/separator string as its second argument.
The first call to strtok shall contain the string to be parsed and the delimiter. This shall return the text between the start of the string and the first occurance of the delimiter string. To get the next token, we only need to pass a NULL as the first argument and the delimiter as the second. strtok maintains the context and knows it has to parse the same string that you provided earlier.

The following program shows an example.


#include <stdio.h>
#include <string.h>
int main()
{
    char str[50];
    char *token;

    strcpy(str,"abc,def,ghi");
    token = strtok(str,",");
    printf("%s \n",token);
    
    token = strtok(NULL,",");
    printf("%s \n",token);
    
    token = strtok(NULL,",");
    printf("%s \n",token);
    
    return 0;
}


In the above example, we have a string "abc,def,ghi" and we parse it into tokens abc, def and ghi.
Note, how we pass str as the first argument in the first call to strtok and NULL as the first argument in the subsequent calls. Here is how the output of this program is going to look like:

abc
def
ghi



strtok() returns a NULL if it is not able to find any tokens. We can use this property when there are unknown number of tokens to be parsed. We can keep calling strtok() in a loop after the first call, until it returns NULL.
#include <stdio.h>
#include <string.h>
int main()
{
    char str[50];
    char *token;

    strcpy(str,"abc,def,ghi,jkl,mno");
    token = strtok(str,",");
    
    while ( token != NULL ) 
    {
        printf("%s \n",token);
        token = strtok(NULL,",");
    }
    
    return 0;
}
Here is the output of above program:
abc
def
ghi
jkl
mno


We can also provide multiple delimiters to strtok, as shown below.
#include <stdio.h>
#include <string.h>
int main()
{
    char str[50];
    char *token;

    strcpy(str,"abc,def:ghi;jkl,mno");
    
    token = strtok(str,",;:");
    
    while ( token != NULL ) 
    {
        printf("%s \n",token);
        token = strtok(NULL,",;:");
    }

    return 0;
}
Here we pass a delimiter string which contains a comma, semicolon and a colon. strtok function checks for each of these and identifies a token if it finds any of these characters.
Output of above program:
abc
def
ghi
jkl
mno
Another thing about strtok that we need to know is that when it encounters more than one delimiters in succession, it considers them to be a single delimiter. Program below shows this scenario:
#include <stdio.h>
#include <string.h>
int main()
{
    char str[50];
    char *token;

    strcpy(str,"abc,,,def,,,,,,ghi");
    
    token = strtok(str,",");

    while ( token != NULL ) 
    {
        printf("%s \n",token);
        token = strtok(NULL,",");
    }

    return 0;
}
Output of the above program:
abc
def
ghi 
Also, if  a delimiter is encountered at the start or end of a string, strtok() ignores them, as shown below.
#include <stdio.h>
#include <string.h>
int main()
{
    char str[50];
    char *token;

    strcpy(str,",,,abc,,,def,,,,,,,ghi,,,,,,");
    
    token = strtok(str,",");
    while ( token != NULL ) 
    {
        printf("%s \n",token);
        token = strtok(NULL,",");
    }

    return 0;
}
Output of the above program:
abc
def
ghi 

There are a few common pitfalls that we should avoid while using strtok().

Never pass a constant char pointer as the first argument of strtok. That's because strtok changes the first argument internally, so passing a constant pointer shall result in a crash ( unless you have a signal handler implemented ).

Something like below will result in a crash:
#include <stdio.h>
#include <string.h>
int main()
{
    char *str = "abc,def";
    char *token;

    token = strtok(str,",");
    printf("%s \n",token);

    return 0;
}

Also, if want to maintain the string that is to be parsed for further usage, you should avoid passing the pointer to the string directly to strtok, as it changes its first argument internally. It's advisable to copy the string into a temporary buffer and pass that to strtok to get your tokens.

Next, strtok is not thread-safe. That's because it uses a static buffer internally. So, you should take care that only one thread in your program calls strtok at a time.

If you have a multithreaded program, then you should be using strtok_r function instead of strtok. strtok_r is a reentrant version of strtok. Let's understand how we can use strtok_r.

Here is the function declaration:

char *strtok_r(char *str, const char *delim, char **saveptr);


strtok_r has an additional third argument, saveptr, a pointer to a char pointer that is provided by the caller. strtok_r uses this saveptr to maintain context between subsequent calls for the same string. The value of saveptr should remain unchanged in all calls to the strtok_r for it work correctly.


Here is an example program showing strtok_r usage
#include <stdio.h>
#include <string.h>


int main()
{
    char str[] = "apple,orange,banana";
    char *saveptr;
    char *token;

    token = strtok_r(str,",",&saveptr);

    while ( token != NULL )
    {
        printf("%s \n",token);
        token = strtok_r(NULL,",",&saveptr);
    }

    return 1;
}
Output of the above program:
apple
orange
banana

Both strtok and strtok_r change their first argument, the pointer to the string supplied to it in the first call. As a programmer, you should be aware of this, while using strtok or strtok_r in your programs.


Now, let's say that you would like to know if there was no content between successive delimiters in the input string. With strtok you wouldn't be able to get this information, because it treats successive delimiters as one. For this task, its best to use strsep() function instead of strtok/strtok_r.
Here is an example of strsep usage:
#define TESTSTRING ("abc,,,def,,,ghi")
int main()
{
    char *str; 
    char *token;

    str = (char *) malloc(sizeof(char) * (strlen(TESTSTRING)+1));

    strcpy(str,TESTSTRING);
    
    token = strsep(&str,",");

    while (token != NULL)
    {
        if ( strlen(token) == 0 )
        {
            printf("No Content\n");
        }
        else
        {
            printf("%s\n",token);
        }

        token = strsep(&str,",");
    }

    return 0;
}
Here is the output of the above program:
abc
No Content
No Content
def
No Content
No Content
ghi 

Again with strsep, we have to keep in mind that it changes the pointer whose address is passed to it as the first argument.

With both strsep and strtok/strtok_r functions, its best to copy the string to be split into a temporary buffer and then use these functions for tokenization. By doing this we don't have to worry about changing the original string pointer, as it could have been passed to you from another function and you may not really know exactly which memory the string resides in or what the caller intends to do with it after you have returned.

So, as we can see, C library does provide us functions that help us with string tokenization, but we need to take care how we use them.

Wednesday, June 25, 2014

Circular linked lists - Attributes, Implementation and Uses

What is a Circular Linked List

A Circular Linked List is a linked list in which the last node in the list points to the first node in the list. This property makes it circular.

This is shown in the figure below - the last node in the linked list points to the head node of the list, making it circular.

A Circular Linked list

Uses of a Circular Linked List

  1. Circular linked lists are a natural option to represent something circular or a set of objects that form a loop. 
  2. In a circular linked lists you can have easy access to end of the list. This gives you flexibility to add or delete elements at both ends of the list in a quick manner. This attribute comes in handy in a variety of situations.
  3. Another attribute of a circular linked list is that a pointer to any node in the list gives you the ability to traverse the whole length of the list. This flexibility is not available in a normal single linked list where one needs the pointer to the head of the list to traverse the whole length of the linked list.
  4. An interesting feature of a circular linked list is that one can create two circular linked list out of a single circular linked list in constant time. This can be done by simply swapping the next pointers of the two nodes that would form the tail nodes of the newly constructed circular linked lists. In a similar manner, if you swap the next pointers of a last nodes of two separate circular linked lists, you are merging the two lists into a single circular linked list in a constant time operation.
  5. Circular linked lists can be normally used where a resource needs to shared among a number of users in a round robin manner. 
  6. A good example of this is how an operating system can schedule CPU resources among processes in round robin manner.
          Another example is a multi-player game where the chance to play the game passes around                     the players in round robin manner.

Linux Kernel Implementation of a Circular linked list

Linux kernel code uses circular linked lists extensively and so the circular linked list implementation in the linux kernel is a good case study in implementation and usage of a circular linked list.

Linux kernel implements a doubly linked circular linked list.

The definition of the list structure in linux looks like this 

struct list_head {
 struct list_head *next, *prev;
};

The list head structure shown above represents the link of a particular node in the list. To implement a circular linked list using this, one needs to embed this structure in another structure that would hold the information associated with the node.

e.g.
Let's say, you want to build a circular linked list in which each node would store an integer as information. Let's call this list circular_int_list. Using Linux Kernel's list_head, you would declare your list structure as follows


typedef struct circular_int_list
{
        int info;
        struct list_head list;
};

This structure represents a single node in the linked list. Linux implementation provides a number of functions to manipulate and use a linked list. Let's go through them one by one and at the same time performing certain operations on our circular linked list shown above.

So, let's say you want to start off by creating the first node in the list. The steps for this are:

  1. Allocate memory for the node
  2. Call linux Kernel's INIT_LIST_HEAD() function, passing it the address of the list element of your node. This function initializes the circular linked list. This means that the node is now the first element in the list. Since this is a doubly circular linked list, the node is also the last element in the list and the prev and next pointer of the list structure point to the node itself.

The code for above steps is given below:


/* Step 1
 * Declare the list and allocate memory 
 * for the first node
 */
struct circular_int_list *mylist;
mylist = (struct circular_int_list *) malloc(sizeof(struct circular_int_list));

/* Step 2
 * Call the INIT_LIST_HEAD() to initalize the list 
 */
INIT_LIST_HEAD(&(mylist->list));


The code for INIT_LIST_HEAD() is simple and is shown below:


void INIT_LIST_HEAD(struct list_head *list)
{
 list->next = list;
 list->prev = list;
}



Side Note: All the linux kernel code shown here can be found online and the relevant links are provided at the end of this post.

Now let's look at functions used for adding a new node to the list:
To add an new element to the list, list_add() function is provided. It's first argument is pointer to the new node that is to be added and second argument is pointer to the node in the list after which you want to add the new node. Point to note here is that you can provide any node in the list and the new node is going to be added after that node.
If you always provide the same node to insert after, you get insertion of the new nodes in stack fashion.

e.g.

list_add(&(newnode->list), &(mylist->list));


Implementation of list_add() function is given below:


void list_add(struct list_head *new, struct list_head *head)
{
 __list_add(new, head, head->next);
}

void __list_add(struct list_head *new,
         struct list_head *prev,
         struct list_head *next)
{
 next->prev = new;
 new->next = next;
 new->prev = prev;
 prev->next = new;
}


There is also a function list_add_tail(). This inserts the new node just behind the head. Use this function if you want to implement a queue.

For deleting a node from the list, there is list_del() function. The only argument it needs is the pointer to the node which you want to remove from the list.


list_del(&(newnode->list));


Implementation of the list_del() function is given below:


void list_del(struct list_head *entry)
{
 __list_del(entry->prev, entry->next);
 entry->next = LIST_POISON1; 
 entry->prev = LIST_POISON2;
}

void __list_del(struct list_head * prev, struct list_head * next)
{
 next->prev = prev;
 prev->next = next;
}



LIST_POISON1 and LIST_POISON2 are non null pointers that would result in page-faults in case somebody tries to access them.

Linux kernel provides many different ways in which you can iterate over a given list. Let's look here at one of them: list_for_each_entry().

If I wanted to print integer values stored as information in the list I have defined above, here is how I would use the list_for_each_entry macro to traverse the list :


struct mylist *listnode;

list_for_each_entry(listnode,&mylist, list)
{
        printf("%d \n",listnode->info);
}



As you can see, the code above is clean and elegant.

The list_for_each_entry() macro is implemented as shown below:-


#define list_for_each_entry(pos, head, member)    \
 for (pos = list_entry((head)->next, typeof(*pos), member); \
      &pos->member != (head);  \
      pos = list_entry(pos->member.next, typeof(*pos), member))

#define list_entry(ptr, type, member) \
 container_of(ptr, type, member)

#define container_of(ptr, type, member) ({   \
 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
 (type *)( (char *)__mptr - offsetof(type,member) );})


list_entry() macro used above gives back the structure that contains the element given to it as its first argument.  It internally uses the container_of() macro.

These are just some of the many different operations that linux kernel allows on its circular linked list. For those who revel in reading code, I would greatly recommend going through the entire list file.

Linux kernel implementation of circular linked list seems to have been made in such a manner that the list structure and list operations themselves remain an abstraction don't meddle with the information stored in each node.
The generic nature allows for the same list structure to be used in different types of situations without ever changing how the list operations are done.
You can embed the list_head structure in any structure and you have got a linked list, a circular one at that !

Linked lists are widely used in Linux code. You would find a single structure containing multiple list_head structures, indicating that a single node is part of multiple linked lists. This is quite good, since the information resides at only one place in memory while it is part of multiple linked lists.

e.g. The task_struct structure in linux kernel, the structure that stores all the information of a process, contains multiple linked lists inside itself. The kernel itself uses one of these lists to manage different tasks.

struct task_struct 
{
        .
        .

        /*
         * List of tasks 
         */
 struct list_head tasks;

 /*
  * children/sibling forms the list of my natural children
  */
 struct list_head children; /* list of my children */
 struct list_head sibling;  /* linkage in my parent's children list */
        .
        .
        .
};


We have looked at circular linked lists - its uses and one of its most used implementations in the software world. To those who are interested, I would strongly recommend browsing linux kernel code to discover more.

References:

  1. Code : Linux Kernel Code (version 3.9.8)
  2. Book : Linux Kernel Development by Robert Love
  3. Linked list Wikipedia Article