2014年6月10日 星期二

The pthread_cond_signal() VS pthread_cond_broadcast()

上一篇說到了 pthread_cond_wati() , 現在說說 pthread_cond_signal() & pthread_cond_brocast() 吧 .

基本上這兩個 都是傳 condiction 給 ptherad , 讓 pthread 由 wait 狀態 wakeup 起來 . 不過有些差別 pthread_cond_signal() 只會讓一個 pthread wakeup (等待同一個 condition 的 pthread) ,至於是哪一個 pthread wakeup 呢 ? 目前我沒有去詳細 研究 , 不過這個signal 不能確定(指定)那一個 pthread , 所以使用上要小心點. 而 boradcast 是讓所有 pthread 都 wakeup 起來 .

所以依照所需選擇合適的function. 我通常都只有一個 ptherad 在等待 , 所以我都用 brocast , 確保我的 pthread 可以 wakeup .

下面有參考範例 ,



Reference :

    http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_76.htm
    http://publib.boulder.ibm.com/iseries/v5r1/ic2987/index.htm?info/apis/users_73.htm

PS. 目前比較忙沒有時間按照範例測試.

2014年6月4日 星期三

The pthread_cond_wait() and pthread_cond_timedwait()

最近在弄 power daemon ,需要使用大量 thread , 並且不能讓這些 thread " free running " , 所以再次研究一下 pthread_cond_wait() & pthread_cond_timedwait() !

先來看看 pthread_cond_wait() 的說明吧 ! (下列 藍色文字擷取於 man 說明)

pthread_cond_wait() 的動作會先取得 cond 的內容 , 然後 unlock 讓 ptherad_cond_signal() 可以更改 cond 的內容,接著再 lock , 並判斷是否需要繼續 ptherad , 如果沒有就 unlock 並停下 pthread, 相反就 lock 且接著 pthread 的內容.

        pthread_cond_wait(mutex, cond):
                  value = cond->value; /* 1 */
                  pthread_mutex_unlock(mutex); /* 2 */
                  pthread_mutex_lock(cond->mutex); /* 10 */
                  if (value == cond->value) { /* 11 */
                      me->next_cond = cond->waiter;
                      cond->waiter = me;
                      pthread_mutex_unlock(cond->mutex);
                      unable_to_run(me);
                  } else
                      pthread_mutex_unlock(cond->mutex); /* 12 */
                  pthread_mutex_lock(mutex); /* 13 */


我們再看看 pthread_cond_signal() 的動作吧.
首先會先 lock , 如果 lock 失敗會繼續等在 lock 階段, lock 成功會更動 cond 內容,並且叫醒 pthread. 然後 unlock 讓 pthread 可以繼續執行.


         pthread_cond_signal(cond):
                  pthread_mutex_lock(cond->mutex); /* 3 */
                  cond->value++; /* 4 */
                  if (cond->waiter) { /* 5 */
                      sleeper = cond->waiter; /* 6 */
                      cond->waiter = sleeper->next_cond; /* 7 */
                      able_to_run(sleeper); /* 8 */
                  }
                  pthread_mutex_unlock(cond->mutex); /* 9 */


說這麼多 ,來個範例吧 : 
下列兩個範例 , 一個是 pthread 本身 , 做完事情後等著 pthread_cond_signal() 來進行離開.
所以可以看到  pthread 執行時先 lock , 最後停在 pthread_cond_wait() . 等另外 function 
送出 pthread_cond_signal() , pthread 就會繼續執行,並且離開 pthread.
 

void *ps_event_handler ( void *argc )
{


   pthread_mutex_lock(&thread_mutex);

    /* Loop forever */
    for ( ps_thread_status = 1; ps_thread_status;  )
    {
          //---- TO DO.


            pthread_cond_wait(&thread_cond, &thread_mutex);
    
     }   /* End of for() */

   pthread_mutex_unlock(&thread_mutex);
  
   pthread_exit ( 0 );
}


void ps_thread_destory(void)
{
    ps_thread_status = 0;

    pthread_cond_signal(&thread_cond);      // force weakup thread , then stop itself.

    pthread_join(thread_ps_id, NULL);       // wait for thread end.

    pthread_mutex_destroy(&thread_mutex);
    pthread_cond_destroy(&thread_cond);
}


如果想要讓 pthread 暫停一段時間 後繼續執行 , 那就需要 pthread_cond_timedwait() 了.
範例如下 , 每一秒執行 for loop 一次.(紅色部分 是修改)
利用 pthread_cond_timedwait() return 的 value , 就可以判別是 timeout 還是被 signal 叫醒.


void *ps_event_handler ( void *argc )
{


   pthread_mutex_lock(&thread_mutex);

    /* Loop forever */
    for ( ps_thread_status = 1; ps_thread_status;  )
    {
          //---- TO DO.


            gettimeofday(&tv, &tz);
            ts.tv_sec  = tv.tv_sec + 1 ;
            ts.tv_nsec = tv.tv_usec*1000;
            retval = pthread_cond_timedwait(&thread_cond, &thread_mutex, &ts);


        if ( retval  == ETIMEDOUT)
        {
        //==== time out.

        //---- TO DO.


        }
        else
        {
        //==== Got signal.

        //---- TO DO.


        }
    
     }   /* End of for() */

   pthread_mutex_unlock(&thread_mutex);
  
   pthread_exit ( 0 );
}



這樣的架構下就可以控制 pthread 進行一些流程控制了.





2014年6月3日 星期二

EPERM error of settimeofday() , using capset() & capget() modify CAP_SYS_TIME.

最近發現 settimeofday() 沒有辦法執行 , 回報錯誤為 -1 (EPERM), 看一下 man 說明,發現沒有打開 CAP_SYS_TIME, 所以在 euid >= 1000 的時候不會允許設定系統時間. 
下列是 man settimeofday() 的說明:


       EPERM  The calling process has insufficient privilege  to  call  settimeofday();  
                     under  Linux  the  CAP_SYS_TIME  capability  is   required.

好吧 , 打開一下後門 ,  google 了一下,發現可以使用 capget() & capset來設定.
大致說明一下 CAP 的設定, 首先要先了解三個CAP 的等級.
A. effective => 有效的, 這個有設定就可以允許 操作相對應的東西 , 
                        例如 CAP_SYS_TIME 有設定就可以設定系統時間.
B. permitted => 這是允許的內容 , 如果這個資料內的 CAP_SYS_TIME 沒有設定 , 
                         那設定 effective 也沒用.
C. inheritable => 是否可被繼承 ,如果有設定 , fork() + exec() 的 子 process 也可以繼續
                          被允許對應的設定.

           typedef struct __user_cap_data_struct {
              __u32 effective;
              __u32 permitted;
              __u32 inheritable;
           } *cap_user_data_t;
 

來個實際例子吧, 目前子程序的 P (permitted) 是全部被允許的 , 
不過E(effective)沒有打開 ,利用下列的範例將其打開 , 就可以使用 settimeofday() 設定系統時間了.

        if (geteuid() > 0 )
        {
        //---- Not Root ,need turn on CAP_SYS_TIME.
                header.version = _LINUX_CAPABILITY_VERSION_1;
                header.pid = getpid();
                retval = capget(&header, &cap);
                if(retval)
                        goto capget_fail;

//                      printf("\t[%s] PID:%d E:%x P:%x I:%x\n", __FUNCTION__,
//                                      header.pid, cap.effective, cap.permitted,cap.inheritable);

                header.version = _LINUX_CAPABILITY_VERSION_1;
                header.pid = 0;

                if ( enable )
                        cap.effective   |=  (1 << CAP_SYS_TIME);
                else
                        cap.effective   &=  ~(1 << CAP_SYS_TIME);

                retval = capset(&header, &cap);
                if(retval)
                        goto capset_fail;
        }





PS. 這段 function call 我想要打開 CAP_SYS_TIME 設定系統時間後就關閉(我提供一個特別的 SO API , 所以使用者還是不能 設定 系統時間,除非透過這個 API .
       所以先判別是否為 root (利用 EUID) , 如果不是就可以enable / disable . 如果是 root , 就不理會(root 本來就可以)





2014年5月26日 星期一

Linux Message queue IPC.

最近因為工作關係 , 需要使用一個 IPC (一對多 ) 的方式來控制 , 並且 server 需要有 blocking 的動作 (等待 server 發訊息在開始動作 ) , 本人又不想使用 sock (傳說中比較耗支援 ) . 

比對並且找了一下後發現 Message queue 比較適合 . 

msgrcv(); 如果不要加上IPC_NOWAIT , 就可以達到 blocking 的動作, 這樣就不會佔用太多CPU Resource .

msgrcv(); 也有一個參數 msgtype , 這個參數會對 queue 上的 masg 進行篩選 (type 相同才會接收) , 這個也可以達成一對多的訊息傳遞. 


PS. msgtype = 0 , 所有訊息都收 , msgtype = xx , 只接收 type 為 xx 的message, msgtype = - yy , 接收 type 低於 yy 的訊息 (msgtype 是一個  long 的變數,所以都是數值, 這個數值不能是負數 ) .


使用上其實很簡單 , 大致份為 四個操作 function .
A. msgget() 
    建立或取得 message queue.

B. msgsend()
   傳訊息資料到 message queue.

C. msgrcv()
   從 message queue 取得訊息資料.

D. msgctl() with IPC_RMID.
   最後不用 message queue 的時候, 將 這個 message queue 刪除 (distore).


不多說, 來幾個 example code 吧 ! 

A.msgget() 
  給個特別(識別)的 key . 
  msgflg 帶入 權限或是 IPC_CREAT (如果不存在就建立).
  最傳回 handle id . 

 msgflg = IPC_CREAT | 0666;
 m_msq_id = msgget((key_t)MSQ_CMD_KEYID, msgflg);

B. msgsnd()
   我的使用上是對多個 server 進行播送 , 所以使用的 IPC_NOWAIT.
   其中 data內的 mtype 需要設定這個 message 的type,如上面所敘述就可以讓
   msgrcv 依照type 接收.

 
 retval = msgsnd(m_msq_id, data, sizeof(struct msgbuf), IPC_NOWAIT);


C. msgrcv() 
 這邊需要注意type 參數,就如同上面敘述,可以 0 , xx or -yy .
 最後一個 參數是 flag , 可以有 IPC_NOWAIT 等. 
 我需要 blocking , 所以帶 "0" .
  

rcv_siz = msgrcv(m_msq_id,data,sizeof(struct msgbuf),type,0);


D. msgctl()
  這個有很多控制參數可以用, 不過我只使用最後 distore 的功能.
  範例如下:


retval = msgctl(m_msq_id, IPC_RMID, (struct msqid_ds *)NULL);


 

Reference:
 下列這個網頁有詳細的說明和一些 example code .

http://www.cs.cf.ac.uk/Dave/C/node25.html


2014年5月21日 星期三

Booting time.

最近在最佳化系統工作 , 主要 force on booting time .
後來發現 , 第一個程式 loading 完到執行 main() 卻要花很久 .
實驗範例如下 :
int main(int argc, char **argv)
{
 .......
 printf ("xxxxx \n );
 .........
 .........
}

發現 printf 大約要 2 秒後才會列印出來 , Why ?? 花很多時間在 loading library 嗎 ?

(當然我的 cpu 只有 133MHz , 所以才會花這樣久 , 如何測量呢 ? 可以利用 bootchart 搭配來測量 , 雖說不是很準, 不過可以看到有所 delay time )


結果發現 ,只要在 .so file 的 code 內加入下列這樣的宣告 Function .
就會在 loading .so 的時候去執行 ,如果這樣的 function 花費很多時間處理 , 當然 running main() 的時間也會延後 .

__attribute__  ((constructor)) void test_font_init(void)
{
 ..........
}

PS. 主要是宣告 constructor這個屬性 , 有關這個 屬性的詳細內容 , 使用方式 ,可以去 google 找一下,一堆說明 .

2014年4月21日 星期一

bootchart in busybox .

最近在檢視開機時間 , 記憶中有一個 bootchart 可用.

先來看看目前 bootchart 發展到如何 , 有哪些 tools 可使用.下列網站說明的很清楚,也有很多資源可以使用.

http://elinux.org/Bootchart

A. 觀看一下目前工作的機器.發現使用的 busybox 已經有內建 bootchart 了.
    事情好辦多了 , 接著開始產生 bootchart 資料吧.

B.基本上應該將kernel command line 的 init=./init 改為 init=/sbin/bootchart .
   不過我只想觀看一下rcS 啟動後有哪些 task 號時間.
   所以就在 rcS 的第一行加上 "/sbin/bootchart init & " 這樣就可以了.

 C.產生的資料為 bootchart.gz , 並且放在 /var/log 中.
    解開 bootchart.gz 會發現一堆文字檔案,很難關看booting 過程中的訊息.

 D.下列網址(bootchart2) 提供了一個可以將這些文字檔案轉成 png file 的 utility.
     來安裝吧 ..... ! 

 https://github.com/mmeeks/bootchart

   1. 下載 source code.
   2. 依照說明文件, 只要進行 " make "  即可 (Fedora 20 i686 ).
   3. 將 target 上的 /var/log/bootchart.gz 上傳回 Fedora (我慣用 fedora )
   4. [jeff@...]$ /home/<your bootchart dir>/bootchart-master/pybootchartgui.py bootchart.tgz
    5. 就會產生 bootchart.png file 了.

 E. bootchart 圖片就不在上傳了, 網路上都有很多 .



2014年4月11日 星期五

Printf 不能輸出到 console ....

最近工作上發現 , 有使用者的程式使用 printf 來 debug , 不過卻沒有輸出 .
網路上有說使用 fprintf + stdout or stderr 等方式 . 不過在我們的環境下卻沒有辦法 .

最後 , 我使用 暴力方式 解決了 !!

首先 , 先了解 console 是哪個 tty . 然後在 程式內 直接 fopen  tty , 接著用 fprointf 的方式直接寫過去就可以了 !!

雖然這樣的方式不好 , 不過先可以用 , 有時間在研究一下正規方式 .


============================

#define AP_DEBUG_ON

#if defined (AP_DEBUG_ON)

 #define    DEBUG_MSG(S,A...)        fprintf(ttyfd,"==== [%s]:[%d]:" S "\n",__FUNCTION__,__LINE__,## A)

#else

 #define    DEBUG_MSG(S,A...)        do {} while (0)

#endif


FILE *ttyfd ;

int main(int argc, char **argv)

{   
    ttyfd = fopen("/dev/ttyS0", "w");

...................................
DEBUG_MSG("flag:%d -> %d ",flag,flag ? 0 : 1 ); 
}




2014年3月25日 星期二

Using "systemctl" enable smb.service in Fedora 20

最近在使用 Fedora 20 發現 每次開機 samba 都不通 , 需要執行一次 system-config-samba 才能動作.


google 一下 , 發現主要是 smb.service 沒有啟動 , 我知道舊版的使用 setup 指令來設定啟動.
Fedora 20 就不是使用這樣的方式 , 而是使用 systemctl 來啟動.


所以安裝好 system-config-samba 並設定好 samba 後需要利用 systemctl 來啟動. 指令如下:


[jeff@localhost ~]$ su -c 'systemctl enable smb.service '