2010年8月11日 星期三

Concurrent Programming

何謂 Concurrent Programming ??

所謂的 Concurrent Programming 簡單的講就是一個多工的程式, 這種形式的程式設計有個好處就是當程式 Block 的時候,程式還能 繼續做其他的事,通常 Block 的情況最常發生於網路程式或是一些和 I/O 做處理的程式,一般 Concurrent Programming 最常用的方法就是 用 fork() 還有 thread 這兩種功能,下面將討論這兩種方法。

關於 UNIX 下的 fork()

在 UNIX 中有所謂的 fork() 功能,這個功能主要目的是產生兩個 一模一樣的 process 在電腦中同時執行,藉由這種方法可以做出 Concurrent Programming 的效果,這種方法在 UNIX 一直都被廣泛的應用在各種不 同的應用程式上。

#include &ltstdio.h>

void main(void)
{
  int pid,i;

  pid = fork();
  if (pid == 0) {
     for (i=0;i<44;i++) {
       printf(" I am a child process I say %d\n",i);
       sleep(1);
     }
     exit(1);
   }
  else if (pid > 0) {
     for (i=0;i<40;i++) {
       printf(" I am a parent process I say %d\n",i);
       sleep(1);
     }
  }
  else if (pid < 0)
 printf(" Sorry .....I can't fork my self\n");
}
上面就即是一個簡單的 fork() 用法,執行上面的程式後, 可以藉由印出的字串看到多工的效果,fork() 還可配合 exec() dup() pipe() 做出和外部程式結合使用的效果。再來就解釋一下 fork() 是怎麼動作的:
原來的 process
   +---------+
   |  Data   |
   +---------+
   |  Code   |---> PC
   +---------+

  執行 fork() 之後

  原來的 process                    fork 出來的 process
   +----------+  複製 Data Code 到     +----------+
   |  Data    | ===================>  |   Data   |
   +----------+                       +----------+
   |  Code    |---> CP = CP+1;        |   Code   |----> CP = CP+1
   +----------+                       +----------+
由上圖可以看出原來的 process 和 fork 出來的 process 不論是變數、程式 碼都是一模一樣的,唯一不同的是 fork() 的返回值,parent process 的返回值 是 child process 的 PID ,而 child process 的返回值是 0 ,因此在程式實 作上就以 fork() 的返回值來判斷那一個是 parent 那一個是 child 。

關於 thread

thread 是最近較為流行的一種趨勢,thread 可分為兩種:user-level 和 kernel-level,這兩種方法各有其優缺點,以下是他們的比較:

The advantages of kernel-level threads over user-level threads are:

- supports multiprocessors;

- possibly smoother scheduling, since the same scheduler (the kernel's)
  handles both threads and other Unix processes;

- no special action needed to make blocking system call suspend only
  the calling thread, not all threads of the process (user-level
  threads usually rely on a combination of select() and jacketing of
  C library functions for system calls);

- as a consequence of the above, more efficient I/O and
  timer-based operations (no need for extra select() and gettimeofday()).

The disadvantages are:

- more expensive context switches between threads, at least when blocking
  on conditions and mutexes;

- thread creation is more expensive.

                                                        --- 引自 LinuxThreads --
由上面的比較可以清楚的發現 kernel-level 是一個比較好的選擇, 也算是發展的趨勢吧,下面舉個在 Solaris 下的 thread 範例:

#define _REENTRANT
#include &ltthread.h>
#include &ltstdio.h>
#include &ltstdlib.h>

#define         NUM_THREADS     10
#define         SLEEP_TIME      10

void    *sleeping(char *);
int     i;
thread_t        tid[NUM_THREADS];

void main(int argc,char *argv[])
{
        for (i=0;i&ltNUM_THREADS;i++)
                thr_create(NULL,0,sleeping,"4",0,&tid[i]);
        while (thr_join(NULL,NULL,NULL) == 0)
                ;
        printf("main() reporting that all %d threads have terminated\n",i);
}

void *sleeping(char *sleep_time )
{
        printf(" thread %d sleeping %d seconds\n",thr_self(),atoi(sleep_time));
        sleep(atoi(sleep_time));
        printf("\n thread %d awakening\n",thr_self());
}
compile 的時候記得要加 -lthread 以這個程式和上面 fork 中所舉的例子來做 比較可明顯的發現 thread 所佔用的 Memory 較少,另外需要注意的一點是,thread 和 fork 是可以結合使用的,結合使用時可分為兩種:一種是 fork 的時候將 parent process 全部複製;另一種是只有複製 parent process 中呼叫 fork() 的 thread。

How to use simple speedtest in RaspberryPi CLI

  pi@ChunchaiRPI2:/tmp $  wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.py --2023-06-26 10:4...