Monday, March 31, 2008

[Programming] Threads (3) - joinable or detached thread

Last time we've seen it's very easy to create a new thread in Linux by using the POSIX thread API functino pthread_create(). But wait, that example code has a bug. How does Linux schedule those two threads, namely, the main thread and the new created thread?

The answer is Nothing! If the new created thread terminates before the main thread, everything is ok. But it's very possible that the main thread stops first, especially when you are creating a new thread to do some time-cost calculation or operation. In this case, the memory used to passing parameters to thread function will be deallocated when the main thread is done, while the created thread may still be accessing this block of memory.

So we need a mechanism to avoid this. The POSIX thread standard provides a function called pthread_joint(). It's similar to the wait() function that a parent process used to collect child processes. With pthread_join(), the main thread will wait until the new created thread terminates, then the main thread returns. In addition, the pthread_join() also provides a way for the main thread to read return values from the created thread.

The prototype of pthread_join() is
int pthread_join(pthread_t thread, void **thread_return);

pthread_join() looks good but it doesn't suit for all cases. Sometimes we may just want the main thread to call a new thread, but don't need the main thread to wait for the return of the new thread. For example, we create a second thread to print some documents. It's not necessary to let the main thread wait, it can continue to serve other jobs while the print is onging. In this situation, we can make the thread detached instead of joinable. In order to do this, we need to set the second argument in the pthread_create() function, i.e. thread attributes.

As we did in the previous blog, setting thread attributes to NULL means the default attribute (thread is joinable) is used. If we need detached thread, we can use pthread_attr_init() function to initialize it, then call pthread_attr_setdetachstate() to set the attribute as detached. Indeed, POSIX thread provides a set of functions to set different attributes to a thread, such as scheculed, inherited etc. But for most applications, the detached attribute is typically of interest.

A typical program setting thread attributes to detached is as follows:

{
...
pthread_attr_t threadAttr;
pthread_t thread;

pthread_attr_init(&threadAttr);
pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED);
pthread_create(&thread, &threadAttr, thread_function, (void *)thread_arguments);
pthread_attr_destroy(&threadAttr);
...
}

Thursday, March 20, 2008

[Programming] Threads (2) - Thread Creation

In Linux/Unix, threads are implemented using POSIX standard API (known as pthreads). All functions and data types related to threads are declared in the header file .

To create a thread, use the pthread_create() function. The prototype of pthread_create() is
#include <pthread.h>
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);


The function is not hard to understand
1) the first argument is used to store the thread ID of the new created thread
2) the second argument is a pointer to a thread attribute object, you can use NULL as default or call pthread_attr_init() to initialize the attribue you want
3) the third argument is a pointer to the thread function, i.e., the function used for the new created thread. Don't be scared by the format, it's just an ordinary function pointer.
4) the last argument is used to pass parameters to the thread function, i.e. the thread function uses this passed value as its arguments

We'll use a simple example to show how it works. The code with comments is attached below



/** simple_thread.c */
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


void * thread_function(void * dumb);
int main()
{

// pthread_t is the thread type defined in POSIX thread
pthread_t thread_id;

int res;

/** create the new thread
the new thread ID is stored in thread_id
the thread's attribute is set to default(NULL)
it will call the thread_function()
nothing is passed to the thread_function
*/

res = pthread_create(&thread_id, NULL, &thread_function, NULL);
if (res!=0)
{

perror("Thread creation failed.");
exit(EXIT_FAILURE);
}

return 0;
}

void * thread_function(void * dumb)
{

printf("the new thread is created.\n");
return NULL;
}







Notice, since pthread functions are not included in the standard C library, you should add the pthread library yourself

cc -o simple_thread simple_thread.c -lpthread

Wednesday, March 12, 2008

[Wireless] New contender in Mobile TV - WiMAX

The latest news about WiMAX is that NextWave plans to introduce Mobile TV over WiMAX, which is currently named as MXtv. It will support up to 30 frames/second of video, and provides up to 45 mobile TV channels with 300kbps in 10 MHz. Nextwave will develop this MXtv with its partner Huawei Technologies USA and Alcatel-Lucnet. The news said they will demonstrate the technology at CTIA Wireless 2008 next month in Las Vegas.

In recent years, IP TV gains a lot of attention. In the wireline domain, some technologies have been proposed to provide the "Final Mile" data stream transmission for the consumer's premises, such as Passive Optical Network, Ethernet in the First Mile over copper. In the wireless domain, Mobile TV can be supported by two-way cellular network (such as 3G) or one-way dedicated broadcast network (such as satellite). Now WiMAX becomes a new platform for Mobile TV. No matter which one dominates or some of them share the market, the new Multimedia era is running to use.

[Programming] Threads (1)

Another important concept in multi-tasking operating system is threads. We have learned that a program can fork a child process to do a different thing. However, at times the overhead of creating a new child process is heavy, especially for some embedded systems which only have limited resources for use. The fact behind this is that a new copy of the original process will be created when forking, such as the memory space and file descriptors. That's what we mentioned before that child process is schedueled independently of the parent process.

In order to realize multi-tasking with light overhead, some new concepts are introduced to multi-tasking programming, such as light-weight process and threads. Sometimes people just call threads as light-weight processes for the sake of simplicity, although they are not the same thing (for details, you can refer to Light-weight process in wikipedia).

So in the view of the resource usage, the new created thread does not copy the original system resources used by the creating thread. It shares the same memory space (heap), global variables, file descriptors, signal handlers and other system resources as the original. However, threads have their own stack space.

If we take a look at the thread in the view of implementation, a thread exists within a process, or more precisely, a thread is a sequence of control within a process. Indeed, a process has at least one thread of execution (also called primary or main thread). This primary thread can create additional threads, and all these threads run the same program in the same process, but they may execute a different part of the program at any time.

Tuesday, March 11, 2008

[Programming] Processes (3) - Signals

A Signal is a special message sent to a process to respond to some conditions. Some examples of the conditions are: illegal instructions, broken pipe, termination etc. Those signals are defined in the header file - singal.h. They are all with prefix SIG. The following table is a list of some signals.

Signal NameDescription
SIGILLIllegal instruction, indicates the program's stack is corrupted
SIGINTTerminal interrupt from keyboard by pressing Ctrl+C
SIGKILLEnds a process immediately, which cannot be caught or ignored
SIGPIPEAttempts to write on a broken pipe
SIGQUITQuit from keyboard
SIGTERMRequests a process to terminate


For a full list of signals, you can find it by typing the command below:

% man 7 signal

Generally speaking, signals can be used in the next three situations:
1) send a signal to processes in response to some errors, such as SIGBUS(bus error), SIGSEGV(segmentation violation)
2) send a signal to another process, such as SIGTERM(terminate), SIGKILL(kill the process)
3) send a command to a running program using user-defined signals, such as SIGUSR1

When a process receives a signal, it will take some actions immediately - either to call another function or ignore it (ignore is also a kind of action). When it calls another function, the current executing program will be paused and the new function will be executed. When the function returns, the paused program will resume again.

You can aslo set or modify the actions to be taken on receipt of signals by using function sigaction().

[Programming] Processes (2) - Zombie processes

Last time we mentioned that we can use fork() to create child processes. However, you should keep this in mind: the child process is schedulued independently of the parent process. It implies that you cannot predict which process will execute before or after another, namely, the order of the occurrence of multi processes cannot be forecast. This situation also occurs in multi-threaded programming.

Therefore, some advanced, even sophisticated mechanisms must be used in mutlitasking programming to control the programs running in the desired order.

For example, in some cases you may want the parent process to wait until its child processes have completed. This can be done using the wait family functions, such as wait(), waitpid(), wait3() etc.

However, we need to pay attention to the occurrence of "Zombie" processes. When a child process terminates, its association (such as exit status) with its parent does not vanish immediately, it is still in the system. In this case, this terminated child process becomes a "Zombie" process. It is the responsibility of the parent process to clean up the zombie child processes. The wait() function we mentioned above can do this clean up work.

If you have too many zombie processes in the system, you may suffer slower performance since they consume resources. So you need to avoid zombie processes. Fortunately, when the parent process exits, a special process called init() with process ID of 1 will do the clean up work automatically for you.

You may ask why we don't notify the parent process when a child terminates. Great, you make it! In Linux, child process can send signals to parent when it terminates. We'll touch it in the next post.

Sunday, March 9, 2008

[Programming] Type casting problem in function's argument

Recently someone asked a question of C programming on a discussion board: he has some codes which handle a fixed point array by passing the name of the array to a function, for example:


/** Code 1 */
// declaration of the function
void f(int *pt);

// an array of fixed point integer
int A[10];

// call the function
f(A);




The above codes work successfully. Now he tries to extend the code to a floating point array, as follows:

double B[10];
f((int*)B);

The compiler didn't complain any error, but the output result is something weird. For example, run the following codes on gcc or visual c, you suppose to get output as 3, but the result is -266631570,



/** Code 2 */
#include <stdio.h>
void test_fun(int* px);
int main()
{
double a[10];

a[0] = 3.14159;
test_fun((int*)a);
return 0;
}

void test_fun(int *px)
{
printf("%d\n", *px);
}







We all know that we can type-cast a double variable to an integer variable like this:




/** Code 3 */
double d=3.14;
int c;
c=(int)d;




And we can pass an array to a function by the array's name if they are in the same type (as the above code 1). But why cannot we do the similar thing for passing a floating point array's name to a funciton which accepts fixed point variables?

The answer to this question is: floating point and fixed point numbers are stored in different formats. For floating point number, it conforms with IEEE754. For example, for a 32-bit computer, a single-precision binary floating point number is stored in the format of sign(1 bit) + exponent (8 bits) + fraction (23 bits). So if you simply pass the array's name of floating point numbers as function's argument to an array that accommodates fixed point numbers, the function will read the 32-bit floating point number in fixed point format. The compiler doesn't complain because, yes, you can read it. But the result cannot be what you expect.

The solution to this question may be: 1) cast each element of the floating point array to a fixed point array first, then call the function; 2) re-write the function to support floating point, such as using templates...