2017年7月19日 星期三

linux - IPC (inter-processes communication) Shered mempry(共用記憶體)

IPC (inter-processes communication) 顧名思義,processes 之間溝通的管道&機制。
  • Shared memory   程式間可以共享memory
  • Message Queue  程式間傳送資訊最簡單的方法
  • Semaphore 管理資料的存取
  • PIPE 
  • Unix Socket


Shared memory:

主要的API
  • int shmget(key_t key, size_t size, int shmflg);
    創建或者獲取指定key值的共用記憶體;
  • int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    如果不再使用共用記憶體時,使用系統調用函數shmctl()將其撤銷,格式為:shmctl(shmid,IPC_RMID,0);
  • void *shmat(int shmid, const void *shmaddr, int shmflg);
    將該共用記憶體附接到自己的程式空間;
  • int shmdt(const void *shmaddr);
    斷開共用記憶體。

建立shared memory的程序:
  1. 先利用 shmget() 建立或是取得指定的KEY值的共用記憶體。
  2. 立用 shmat() 將共用記憶體接(映射到)到自己的程式空間裡。
  3. 已可以使用共用記憶體,可以存取到其他的資料中。
  4. 存取結束後,利用shmdt() 將共用記憶體空間段開。
  5. 若不在使用此共用記憶體,可以利用shmctl() 將其空間刪除。shmctl(shmid,IPC_RMID,0);
Example - 1
shared memory read
// read shared memory example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSZ 2048

int main(int argc, char *argv[])
{
 int shmid;
 int ret;
 key_t key;
 char *shmadd;

 key = ftok(".", 2012);
 if(key==-1)
 {
 perror("ftok");
 }
 system("ipcs -m");
 /*打開共用記憶體*/
 shmid = shmget(key, BUFSZ, SHM_R|SHM_W);
 if(shmid < 0)
 {
 perror("shmget");
 exit(-1);
 }
 /*映射*/
 shmadd = shmat(shmid, Null, 0);
 if(shmadd < 0)
 {
 perror("shmat");
 exit(-1);
 }
 /*讀共用記憶體區資料*/
 printf("copy data from shared-memory\n");
 printf("data = [%s]\n", shmadd);
 /*分離共用記憶體和當前進程*/
 ret = shmdt(shmadd);
 if(ret < 0)
 {
 perror("shmdt");
 exit(1);
 }
 else
 {
 printf("deleted shared-memory\n");
 }
 /*刪除共用記憶體*/
 shmctl(shmid, IPC_RMID, Null);
 system("ipcs -m");
 return 0;
}


Example - 2
shared memory write

// write shared memory example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define BUFSZ 2048

int main(int argc, char *argv[])
{
 int shmid;
 int ret;
 key_t key;
 char *shmadd;

 key = ftok(".", 2012);
 if(key == -1)
 {
 perror("ftok");
 }
 /*創建共用記憶體*/
 shmid = shmget(key, BUFSZ, SHM_R|SHM_W|IPC_CREAT);
 if(shmid < 0)
 {
 perror("shmget");
 exit(-1);
 }
 /*映射*/
 shmadd = shmat(shmid, Null, 0);
 if(shmadd < 0)
 {
 perror("shmat");
 _exit(-1);
 }
 /*拷貝資料至共用記憶體區*/
 printf("copy data to shared-memory\n");
 bzero(shmadd, BUFSZ);
 strcpy(shmadd, "data in shared memory\n");
 return 0;
}

2017年3月22日 星期三

OS - Critical Section 、race condition

Critical Section
這一詞作業系統這門課,常常聽到的名詞之一,有此可見它的重要性也是不容忽視的,首先要介紹他的概念:

到底什麼是Critical Section?它是指當多個thread可能同時存取的記憶體、變數或函數的情況,它的作用是用於在多執行緒環境中保護資源,而通常這種要受保護的程式區段稱為 Critical Section 。

至於為什麼要保護這個區段呢呢?因為在程式裡有可能有兩個 thread (可看成一個小小的function)同時存取一個global variable(全域變數)(或函數),這時後,因為程式的需要,thread 不想被其他程式中斷(不被其他thread插入影響),所以必須要一口起執行完畢,而該需要一口氣執行完的程式區段,所以需要設定
Critical Section,以保護目前執行的thread不被其他thread影響。

因此可以歸類出以下重點:

1.Critical Section是一程式區段, 而這個程式區段必須擁有某共用資源的權限才能執。

2.可以放心的執行 Critical Section 的程式碼,絕不會有其他的 thread 同時執行你所在的code。

3.thread 會被 preempt 換其他的thread 執行, 但是想要進入 Critical Section 的thread 是不會被 排進schedule裡。

4.系統不保證進入Critical Section thread 的順序,但OS保證公平對待所有要進入的thread。

另外,還有一個值得注意的地方是,若要控制critical section執行之機制必須滿足下列三個要求:

(1)Mutual exclusion:不允許兩個以上的process同時在對應的critical section中執行。

(2)Progress:若沒有process在對應的critical section中執行,則控制的機制不能阻擋請求進入critical section之process進入critical section。

(3)Bounded waiting:控制機制必須使等待進入critical section之process在有限時間內進入critical section。

如果都無法同時滿足三個的需求,那Critical Section也無法發揮它的效用了。


race condition
它旨在描述一個系統或者進程的輸出依賴於不受控制的事件出現順序或者出現時機。此詞源自於兩個訊號試著彼此競爭,來影響誰先輸出。
舉例來說,如果電腦中的兩個行程同時試圖修改一個共享記憶體的內容,在沒有並行控制的情況下,最後的結果依賴於兩個行程的執行順序與時機。而且如果發生了並行存取衝突,則最後的結果是不正確的。



reference:os-ii部落格-critical sectionwiki_race condition

thread - pthread (POSIX Threads) linux 實作 基礎互斥鎖(Mutex)

在之前的blog (thread & process)中有稍微提到thread一些要注意的事項,而Mutex 就是可以有效的解決thread 同步的問題。
簡單來說 thread 最常遇到的問題就是共用資源分配的問題,當你設計multi-threads時有特別是利用到global variable (全域變數時),要特別注意同步及資源的問題(Race Condition、Critical Section),否則很容易對你自己create 的 thread 無法掌握,造成 random issues。

Mutex 常用 API

pthread_mutex_init() 初始化互斥鎖
pthread_mutex_destroy() 刪除互斥鎖

pthread_mutex_lock():占有互斥鎖(阻塞操作)
pthread_mutex_trylock():試圖占有互斥鎖(不阻塞操作)。即,當互斥鎖空閒時,將占有該鎖;否則,立即返回。
具體說一下trylock函數, 這個函數是非阻塞呼叫模式, 也就是說, 如果互斥量沒被鎖住, trylock函數將把互斥量加鎖, 並獲得對共享資源的訪問權限; 如果互斥量被鎖住了, trylock函數將不會阻塞等待而直接返回EBUSY, 表示共享資源處於忙狀態。

pthread_mutex_unlock(): 釋放互斥鎖

由 example-1 可以觀察到 mutex API 簡單的範例,創建兩個threads對sum從1加到100。前面第一個線程從1-49,后面從50-100。主線程讀取最后的加值。為了防止資源競爭,用了pthread_mutex_t 鎖操作。
example-1中主程式中有一段 sleep(1),可以觀察到若沒有加入讓他主程式delay 一會,得到的結果不會是預想的結果從1累加到100,這也是設計thread時必須考慮到的。


Example-1

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
typedef struct ct_sum
{   int sum;
    pthread_mutex_t lock;
}ct_sum;
void * add1(void * cnt)
{    
    printf("pthread add1 !!\n");
    pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
printf("pthread add1 get key!!\n");
    int i;
        for( i=0;i<50;i++){
            (*(ct_sum*)cnt).sum+=i;
}
    pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
    pthread_exit(NULL);
    return 0;
}
void * add2(void *cnt)
{
printf("pthread add2 !!\n");    
    int i;
    cnt= (ct_sum*)cnt;
    pthread_mutex_lock(&(((ct_sum*)cnt)->lock));
printf("pthread add2 get key!!\n");
    for( i=50;i<101;i++)
    {  
(*(ct_sum*)cnt).sum+=i;      
    }
    pthread_mutex_unlock(&(((ct_sum*)cnt)->lock));
    pthread_exit(NULL);
    return 0;
}
int main(void)
{   int i;
    pthread_t ptid1,ptid2;
    int sum=0;
    ct_sum cnt;
    pthread_mutex_init(&(cnt.lock),NULL);
    cnt.sum=0;
    pthread_create(&ptid1,NULL,add1,&cnt);
    pthread_create(&ptid2,NULL,add2,&cnt);
    //sleep(1);
    pthread_mutex_lock(&(cnt.lock));
printf("main proccess get key!!\n");
    printf("sum %d\n", cnt.sum);
    pthread_mutex_unlock(&(cnt.lock));
    pthread_join(ptid1,NULL);
printf("join ptid1\n");
    pthread_join(ptid2,NULL);
printf("join ptid2\n");
    pthread_mutex_destroy(&(cnt.lock));
    return 0;
}

reference:wiki_pthreadh's 手扎_pthread 立你斯學習紀錄-pthread mutex

2017年3月21日 星期二

thread - pthread (POSIX Threads) linux 實作 pthread 基礎

Pthread:
POSIX執行緒(POSIX Threads,常被縮寫為Pthreads)是POSIX的執行緒標準,定義了創建和操縱執行緒的一套API。

Pthreads定義了一套C語言的類型、函數與常量,它以pthread.h頭文件和一個執行緒庫實現。
pthread可以分為四類:
執行緒管理:例如創建執行緒,等待(join)執行緒,查詢執行緒狀態等。
互斥鎖(Mutex):創建、摧毀、鎖定、解鎖、設置屬性等操作。
條件變量(Condition Variable):創建、摧毀、等待、通知、設置與查詢屬性等操作
使用了互斥鎖的執行緒間的同步管理。

POSIX的Semaphore API可以和Pthreads協同工作,但這並不是Pthreads的標準。因而這部分API是以"sem_"打頭,而非"pthread_"。

而最基本的就是 Pthread 的管理,先從這個部分來做初學會比較容易理解。

Pthread  管理API(較常用):
pthread_create
int pthread_create(pthread_t *tid , const pthread_attr_t *attr , void *(*function)(void *) , void *argument)

這個Function的作用是用來產生一個Thread並執行附帶的Function,附帶有4個參數。

參數1. pthread_t *tid為pthread的指標,在使用Thread之前必須要先宣告一個pthread_t的變數。參數2. const pthread_attr_t *attr為該Thread的屬性,預設是NULL,如果沒有其他特殊的需求直接填入NULL即可。
參數3. void *(*function)(void *)為Function pointer,這邊要放入你要執行的Function。
參數4. void *argument為Function pointer所要帶的參數。

回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
example: pthread_create( &thread1, NULL , showmessage , message);

pthread_exit
void pthread_exit (void *value_ptr)
這個Function的作用是用來關閉一個Thread,附帶有1個參數。

參數1: void *value_ptr用來設定執行成功時該Thread會回傳的值,這個值可由pthread_join()這個Function來取得。

回傳值: 不會回傳任何值。
example: pthread_exit(NULL);

pthread_cancel
int pthread_cancel (pthread_t thread)
這個Function的作用是用來關閉一個指定的Thread,附帶有一個參數。

參數1: pthread_t thread為要關閉的Thread。

回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。
example: pthread_cancel(thread1);

pthread_join
int pthread_join (pthread_t thread, void **value_ptr)

這個Function的作用會暫停目前執行pthread_join的Thread,等到目標Thread執行完畢之後目前執行pthread_join的Thread才會繼續執行,附帶有2個參數。

參數1: pthread_t thread為要等待的目標Thread。
參數2: void **value_ptr用來取得目標Thread的回傳值。

回傳值: 如果執行成功則回傳0,如果執行失敗則回傳錯誤代碼。


可以從example 1的範例中來觀察如何創造管理pthread 實作上面提到的API。


example 1

#include <stdio.h>
#include <pthread.h>

void *show_message( void *ptr )
{
     char *message;
     message = (char *) ptr;
     int x = 5;
     for(x = 5 ; x > 0 ; --x){
        printf("%s \n", message);    
        _sleep(300);
     }
     pthread_exit((void *)1234);
}

int main(){
   pthread_t thread1;
   char *message1 = "Thread 1";
   void *ret;

   pthread_create(&thread1, NULL , show_message , (void*) message1);
   pthread_join( thread1, &ret);
   printf("return value from thread1 = %d\n",ret);
   system("pause");

   return 0;
}        


******************************************************************************
output:
Thread 1
Thread 1
Thread 1
Thread 1
Thread 1
return value from thread1 = 1234

reference:wiki-pthreadnikoung的網路日誌

2017年3月14日 星期二

Volatile (int char ...) & const (int char ...)宣告變數

Volatile 

宣告變數 Volatile(揮發) 顧名思義揮發性的變數宣告。

在了解Volatile 之前必須先搞懂一件事,就是processes中變數的處理方式,簡單來說當program 中宣告,當宣告一個變數後此變數會暫存在CPU 的cache中,以方便快速的運算,直到processes結束。

Volatile 使用時機:
很多時候compiler 可以依據各種不同取向與需求而進行不同的最佳化,包括執行速度最快化、程式容量佔量最小化、針對某種硬體系統所能發揮運用的指令來最適化等。但是有時最佳畫會造成程式執行有誤,宣告volatile變數用來告訴編譯器(Compiler) 不要對該變數做任何最佳化操作,最常見的錯誤如以下兩種:

1, I/O

假設有一程式片斷如下

       U8   *pPort;
       U8   i, j, k;
     
       pPort = (U8 *)0x800000;
 
       i = *pPort;    
       j = *pPort;    
       k = *pPort;  

以上的i, j, k很有可能被compiler最佳化而導致產生
       i = j = k = *pPort;
的code, 也就是說只從pPort讀取一次, 而產生 i = j = k 的結果, 但是原本的程式的目的是要從同一個I/O port讀取3次的值給不同的變數, i, j, k的值很可能不同(例如從此    I/O port 讀取溫度), 因此i = j = k的結果不是我們所要的怎麼辦

用volatile, 將
       U8   *pPort;
改為
       volatile U8   *pPort;

告訴compiler, pPort變數具有揮發性的特性, 所以與它有關的程式碼請不要作最佳化動作. 因而
       i = *pPort;    
       j = *pPort;    
       k = *pPort;    
此三列程式所產生的code, 會真正地從pPort讀取三次, 從而產生正確的結果

2. Global variables in Multithread Program 

這是在撰寫multithread program時最容易被忽略的一部份
此原因所造成的bug通常相當難解決(因為不穩定)

假設有以下程式片斷, thread 1 & thread 2共用一個global var: gData
        thread 1:                                thread 2:                              
                                                                                       
            ...                                      ....                              
            int  gData;                              extern int gData;                  
                                                                                       
            while (1)                                int  i, j, k;                      
            {                                                                          
                ....                                 for (i = 0; i < 1000; i++)
                gData = rand();                      {                                  
                .....                                    /* A */
            }                                            j = gData;                    
                                                         ....                          
            ....                                     }                                

在thread 2的for loop中, 聰明的compiler看到gData的值, 每次都重新從memory load到register,
實在沒效率, 因此會產生如下的code(注意,tmp也可以更進一步的用register取代):
       tmp = gData;
       for (i = 0; i < 1000; i++          
       {                                  
           /* A */
           j = tmp;                    
           ....                          
       }                                  
也就是gData只讀取一次, 這下子問題來了, 說明如下:
thread 2在執行for loop到j = gData的前一列(A)的時候(假設此時gData=tmp=5),
被切換到thread 1執行在thread 1的while loop中透過gData = rand(), 對gData做了修改(假設改為1), 再切換回thread2執行
繼續執行 j = gData, 產生j = 5的結果
但是正確的結果應該是 j = 1
怎麼辦 => 也是用volatile

在thread 1中, 將
        int  gData;
改為
        volatile int  gData;
 
在thread 2中, 將
        extern int  gData;
改為
        extern volatile int  gData;

Const

用來定義常數 (constant) ,凡是以 const 宣告後的變數 (variable) 設定初值後,都不能重新指派新的值。一般來說大家會把他解讀為『常數』,但是這樣的解釋好像有點點不足,應該要解讀為 read-only 比較恰當。

const 與 #define 又有甚麼不同呢?

#define =>可建立巨集 Macro, 或宣告常用常數使用是一種pre-compile header
會在程式編譯成機械語言前先編譯好所以程式在執行時會較快 較省空間(但也有說法 現代的編譯器用define or const所編出來的code品質是相當的)

const =>宣告常數使用(且是一個可定址的常數資料)
常數欄位和區域常數不是變數,可能無法修改。

常數可以是數值、布林值、字串或 null 參考。 請勿建立用來表示想隨時變更之資訊的常數。

reference:
C/C++中的volatile使用時機

linux- time setting ( gettimeofday()、localtime() )



gettimeofday()

Introduction
  • C語言中可以使用 gettimeofday() 來得到時間,精準度可達到微秒。
Function Prototype

  • #include<sys/time.h>
  • int gettimeofday(struct  timeval*tv,struct  timezone *tz )

Description
  • 目前的時間用tv 指標回傳,目前的時區則存放在tz 所指的結構中。
Data Structure

struct  timeval{
       long  tv_sec;/*秒*/
       long  tv_usec;/*微妙*/
};

struct  timezone{
        int tz_minuteswest;/*和greenwich 時間差多少*/
        int tz_dsttime;/*type of DST correction*/
}

在gettimeofday() 中參數 tv tz 皆可為NULL,設定為NULL 則不回傳其結構。

函數執行成功後返回0,失敗後回傳-1。

Example-1

#include<stdio.h>
#include<sys/time.h>
#include<unistd.h>

int main()
{
        struct  timeval    tv;
        struct  timezone   tz;
        gettimeofday(&tv,&tz);

        printf("tv_sec:%d\n",tv.tv_sec);
        printf("tv_usec:%d\n",tv.tv_usec);
        printf("tz_minuteswest:%d\n",tz.tz_minuteswest);
        printf("tz_dsttime:%d\n",tz.tz_dsttime);
}

說明:在使用gettimeofday()函數時,第二個參數一般都為空,一般都只是為了取得當前時間,而不用取得timezone的數值。


Example-2

#include<stdio.h>
#include<sys/time.h>
#include<unistd.h>
  
int delay(int time)
{
    int i,j;
    
    for(i =0;i<time;i++)
        for(j=0;j<5000;j++)
            ;
}
  
int main()
{
        struct  timeval start;
        struct  timeval end;
        
        unsigned  long diff;
        gettimeofday(&start,NULL);
        delay(10);
        gettimeofday(&end,NULL);
        diff = 1000000 * (end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec;
        printf("thedifference is %ld\n",diff);
        return 0;
        
}

測試呼叫delay()函數所需執行的時間(單位為微妙)




localtime()

Introduction
  • localtime() 可以轉換 time()轉換成 year, month, day, haurs, secends等單位。
  • 有效範為為自Jan 1 1970 00:00:00 至 Jan 19 2038 03:14:07
Function Prototype

#include<sys/time.h>
struct tm *localtime(const time_t *timer)

Description
  • localtime() 以指向日曆時間的指標當作參數,將此日曆時間轉換成結構 tm 的表示方法,並回傳表示此結構 tm 。
Data Structure
struct tm {
 int tm_sec; /* Seconds */
 int tm_min; /* Minutes */
 int tm_hour; /* Hour (0 - 23) */
 int tm_mday; /* Day of month (1 - 31) */
 int tm_mon; /* Month (0 - 11) 0:元月*/
 int tm_year; /* Year (減去1900後之值) */
 int tm_wday; /* Weekday (0 - 6;Sunday is 0) */
 int tm_yday; /* Day of year (0 -365) */
 int tm_isdst; /* Nonzero if daylight saving time is in effect. */
}
Example-1

#include <stdio.h>
#include <time.h>
int main(void)
{
    time_t t1 = time(NULL);    // 自 1970 年 1 月 1 日後經過了多少秒可看Example-2 範例
    struct tm *nPtr = localtime(&t1);   
    int year = nPtr->tm_year + 1900;
    int month = nPtr->tm_mon + 1;
    int mday = nPtr->tm_mday;
    int wday = nPtr->tm_wday;

    printf("今天是 %u 年 %u 月 %u 號星期 %u\n", year, month, mday, wday);
     
    return 0;
}

Example-2

#include <stdio.h>
#include <time.h>
int main(void)
{
    time_t t1 = time(NULL); 
    printf("自 1970 年 1 月 1 日後經過了 %d 秒....\n", t1);
     
    return 0;

2017年2月20日 星期一

Process & Thread

Process:

  1.  已經被 loading 到 memory 中,每一行code 都會被執行,且記憶體會隨著需求而有所改變稱之為「程序」,活的program。
  2. CPU 一次只能執行一個Process,所以若要執行多個processes 就需要「排程」(Scheduling)。


Thread:

  1. 在同一個Process底下,有許多自己的分身,就是Thread「執行緒」。
  2. 一個process 一次只能做一件事,在process下create threads,就能讓一個process一次做多件事。
  3. Thread 必須注意共用同一個process下的global variable,若兩個thread 同時對一個varible做運算,可能會造成結果有錯誤這就是「synchronization」的問題。
  4. Thread 也要避免「deadlock」的產生,deadlock 主要原因是一個thread 需要多個資源,而占用資源時還需要其他資源,占用資源也時也沒有例外處理的機制,導致多個thread 之間互相占用對方資源形成一個loop。 








linux - IPC (inter-processes communication) Shered mempry(共用記憶體)

IPC (inter-processes communication) 顧名思義,processes 之間溝通的管道&機制。 Shared memory   程式間可以共享memory Message Queue  程式間傳送資訊最簡單的方法 Semaphore...