Pages

Wednesday, June 27, 2007

Multi-threading in C in Linux




Today we shall talk about writing multi-threaded programming in C language in a Linux environment. Hope this will add value addition to all the novice, who are new to multi-threading in C, like me.

Here is my first multi-threaded C program, multi_thread1.c.

Step 1: Include these header files:
#include
#include
#include //for POSIX thread support

Step 2: The main program is here.
void *print_message_function( void *ptr );

main()
{
pthread_t thread1, thread2;
char *message1 = "Thread 1";
char *message2 = "Thread 2";
int iret1, iret2;

/* Create independent threads each of which will execute function */

iret1 = pthread_create( &thread1, NULL, print_message_function, (void*) message1);
iret2 = pthread_create( &thread2, NULL, print_message_function, (void*) message2);

/* Wait till threads are complete before main continues. Unless we */
/* wait we run the risk of executing an exit which will terminate */
/* the process and all threads before the threads have completed. */

pthread_join( thread1, NULL);
pthread_join( thread2, NULL);

printf("Thread 1 returns: %d\n",iret1);
printf("Thread 2 returns: %d\n",iret2);
exit(0);
}

void *print_message_function( void *ptr )
{
char *message;
message = (char *) ptr;
printf("%s \n", message);
}

Step 3: Compile this program

[kongkon@cadbury multi]$ gcc multi_thread1.c
/tmp/ccwbBUsp.o(.text+0x39): In function `main':
: undefined reference to `pthread_create'
/tmp/ccwbBUsp.o(.text+0x52): In function `main':
: undefined reference to `pthread_create'
/tmp/ccwbBUsp.o(.text+0x65): In function `main':
: undefined reference to `pthread_join'
/tmp/ccwbBUsp.o(.text+0x75): In function `main':
: undefined reference to `pthread_join'
collect2: ld returned 1 exit status
[kongkon@cadbury multi]$

Step 4: Got error, then just compile, do not link(with the library code).

[kongkon@cadbury multi]$ gcc -c multi_thread1.c
[kongkon@cadbury multi]$

Step 5: Good, got success. Now, why is it not building? The error says that
: undefined reference to `pthread_create'
meaning, there is a problem while linking with the library. Question number 1, have you provided the PATH to the library? Question number 2, what is the name of the library?
There is no issue with the path, because otherwise it won't even compile your hello world program, which just use stdio.h. So, the problem is the compiler does not know the library name. Hence the solution is :

[kongkon@cadbury multi]$ gcc -lpthread multi_thread1.c
[kongkon@cadbury multi]$ ls
a.out multi_thread1.c
[kongkon@cadbury multi]$


Step 6: Here you go! Run the program.
[kongkon@cadbury multi]$ ./a.out
Thread 1
Thread 2
Thread 1 returns: 0
Thread 2 returns: 0
[kongkon@cadbury multi]$

Bingo!
The first multi-threaded program in C in Linux environment ran successfully.

Let's introduce a sleep function after the thread creation, and before the thread join.
Now, see the output of the ps command, and what you will find is that, there will be two copies of a.out process. What does that mean?

Tuesday, June 19, 2007

C Questions: Lesson 4




Next 13 are here...
But why only 13? Because 13 is the Luckiest number of all. So 13.

Today we shall talk about core dumps.

Q1. What is a core dump? What are the obvious reasons of core dump?
A: Null pointer accessing, sharing violation.

Q2. What is the output of this code snippet. Why does it behave like this?

int a (int *p);

int
main (void)
{
int *p = 0; /* null pointer */
return a (p);
}

int
a (int *p)
{
int y = *p;
return y;
}


Ans:
This program will compile well, no complains. But while running you will get a segmentation fault, and depending upon the ulimit flag (in Linux) you will get core dump. The problem starts at this line:
int y = *p;
Here you are trying to assign a Null pointer. And thus the problem, leading to segmentation fault.
In Solaris, core dumps are managed by an utility called coreadm.

Q3. What's wrong with this program?
1  //melory leak in line number 11
2  #include 
3  #include 
4
5  static char *helloWorld = "Hello, World";
6
7  main()
8  {
9     char *mystr = malloc(strlen(helloWorld));
10
11     strncpy(mystr, helloWorld, 12);
12     printf("%s\n", mystr);
13  }


Ans: Memory Leak. And the problem is very difficult to detect at the first sight,
neither it gives any core dump. But the result you get is this:
[kongkon@cadbury ~]$ ./a.out
Hello, Worldñ
[kongkon@cadbury ~]$

How do you resolve this issue?
11     strncpy(mystr, helloWorld, 13);
That's it.

Q4. Let's analyze this code snippet.
#include 
#include 

void fn(char *str)
{
strcpy(str, "cow");
printf(" Looking for a = %s ?",str);
}

int main ()
{
char *s = "elephant";
printf(" Should print = %s",s);
fn(s);

return 0;
}

This code gives a segmentation fault, core dump, why?

Ans:
This code gives a seg-fault because you are trying to modify the string
that is passed from the main function in the fn function, which is illegal.
And that results the seg-fault.
If you comment the following line:
strcpy(str, "cow");
you will not get any error.

Well, enough of technical topics, let's laugh a joke:
ONE NIGHT 4 MBA STUDENTS WERE BOOZING TILL LATE NIGHT AND DIDN'T
STUDY FOR THE TEST WHICH WAS SCHEDULED FOR THE NEXT DAY.


IN THE MORNING THEY THOUGHT OF A PLAN. THEY MADE THEMSELVES LOOK AS
DIRTY AND WEIRD AS THEY COULD WITH GREASE AND DIRT. THEY THEN WENT UP
TO THE DEAN AND SAID THAT THEY HAD GONE OUT TO A WEDDING LAST NIGHT AND
ON THEIR RETURN THE TYRE OF THEIR CAR BURST AND THEY HAD TO PUSH THE
CAR ALL THE WAY BACK AND THAT THEY WERE IN NO CONDITION TO APPEAR FOR
THE TEST.

SO THE DEAN SAID THEY CAN HAVE THE RETEST AFTER 3 DAYS.THEY SAID THEY WILL
BE READY BY THAT TIME. ON THE THIRD DAY THEY APPEARED BEFORE THE DEAN.THE
DEAN SAID THAT THIS WAS A SPECIAL CONDITION TEST. ALL FOUR WERE REQUIRED
TO SIT IN SEPARATE CLASSROOMS FOR THE TEST. THEY ALL AGREED AS THEY HAD
PREPARED WELL IN THE LAST THREE DAYS.

THE TEST CONSISTED OF 2 QUESTIONS WITH TOTAL OF 100 MARKS.


Q .1. YOUR NAME .........................( 2 MARKS )
Q.2. WHICH TYRE BURST ...............( 98 MARKS ).
a· Front Left
b· Front Right
c· Back Left
d· Back Right
Q5. What will be your answer to the above question?
Q5. What is the difference between Solaris 9 and SUN OS 5.9?
Ans:
Engineering calls it Sun OS 5.9 and marketing calls it Solaris 9. Just like sunos
was called 5.6 and marketing called it solaris 2.6. Verification of this (proof), do
a uname -a you will see the OS as a 5.9 for your solaris 9 system. For more details
in this issue, please log on to: http://en.wikipedia.org/wiki/Solaris_(operating_system)

Q6. An the cpu, there is a fault register, somehow the system is working in this
case. One of the bits of the register is permanently set. Now, my problem is how to
know which of the 32-bit register being set?
Ans: Think about it yourself.

Q7. What does the following code signifies?
static int addr[68]={ [ABC]=A, [DEF]=D, [GHI]=G, [IJK]=K, };
where addr in static int, and ABC,A,DEF etc are some hexadecimal values. e.g
#define ABC 0x01
Ans:
It's just like initializing any array, but the tricky difference is we can assign
initial value at specified index/indices, all  other elements are initialized to
zero. e.g: static int addr[10] = { [3]=100 };

Q8. What is priority inversion?
Ans: When a medium-priority task preempts a lower-priority task at the run-time
using a shared resource on which the higher-priority task is pending. If the higher-
priority task is otherwise ready to run, but a medium-priority task is currently
running instead, a priority inversion is said to occur.

Q9. Why use _exit rather than exit in the child branch of a fork?
Ans:
The basic difference between `exit()' and `_exit()' is that the former
performs clean-up related to user-mode constructs in the library, and calls
user-supplied cleanup functions, whereas the latter performs only the
kernel cleanup for the process.

In the child branch of a `fork()', it is normally incorrect to use
`exit()', because that can lead to stdio buffers being flushed twice, and
temporary files being unexpectedly removed. In C++ code the situation is
worse, because destructors for static objects may be run incorrectly.
(There are some unusual cases, like daemons, where the *parent* should call
`_exit()' rather than the child; the basic rule, applicable in the
overwhelming majority of cases, is that `exit()' should be called only once
for each entry into `main'.)

In the child branch of a `vfork()', the use of `exit()' is even more
dangerous, since it will affect the state of the *parent* process.

Q10. Why do processes never decrease in size?
Ans:
When you free memory back to the heap with `free()', on almost all systems
that *doesn't* reduce the memory usage of your program.  The memory
`free()'d is still part of the process' address space, and will be used to
satisfy future `malloc()' requests.

If you really need to free memory back to the system, look at using
`mmap()' to allocate private anonymous mappings.  When these are unmapped,
the memory really is released back to the system.  Certain implementations
of `malloc()' (e.g. in the GNU C Library) automatically use `mmap()' where
available to perform large allocations; these blocks are then returned to
the system on `free()'.

Of course, if your program increases in size when you think it shouldn't,
you may have a `memory leak' - a bug in your program that results in unused
memory not being freed.

Q11.What is a zombie?
Ans:
When a program forks and the child finishes before the parent, the kernel
still keeps some of its information about the child in case the parent
might need it - for example, the parent may need to check the child's exit
status.  To be able to get this information, the parent calls `wait()';
when this happens, the kernel can discard the information.

In the interval between the child terminating and the parent calling
`wait()', the child is said to be a `zombie'.  (If you do `ps', the child
will have a `Z' in its status field to indicate this.)  Even though it's
not running, it's still taking up an entry in the process table.  (It
consumes no other resources, but some utilities may show bogus figures for
e.g. CPU usage; this is because some parts of the process table entry have
been overlaid by accounting info to save space.)

This is not good, as the process table has a fixed number of entries and it
is possible for the system to run out of them. Even if the system doesn't
run out, there is a limit on the number of processes each user can run,
which is usually smaller than the system's limit. This is one of the
reasons why you should always check if `fork()' failed, by the way!

If the parent terminates without calling wait(), the child is `adopted' by
`init', which handles the work necessary to cleanup after the child.  (This
is a special system program with process ID 1 - it's actually the first
program to run after the system boots up).
Q12.What will be the output of the following program :
int main()
{
int val=5;
printf("%*d",val,val);
return(0);
}
Ans:
Try this...
Q13. And finally: Tell me the o/p of this program.
int main()
{
main();
return(0);
}
Ans:
Segmentation Fault
-x-