close
這次作業是要實作類似Gmail Notifier

的電子郵件通知程式,

顯示訊息告知我們有新信件進來

[說明]
本程式(名稱:mybiff,需背景執行)分為以下幾個部分:

1. 在程式裡我們必須使用fork()產生出parent 與child 兩個process。

2. parent 使用getlogin()查詢登入帳號,將查詢回來的帳號套用在 “/var/mail/帳號”
    並判斷如果 “/var/mail/帳號” 的size 變大表示有新信進來(其他情況不考慮),
    然後透過KILL 送SIGUSR1 給child 通知它有新信進入。

3. child 必須使用mask 機制,將除了SIGUSR1、 SIGUSR2
    以外之所有signal 封鎖(block)起來,然後使用sigsuspend()等待事件發生,
    當接收來自parent 送來的SIGUSR1 後,需在螢幕上列印出 “You have new mail !”

4. 在程式中要實作『parent 與child 不能同時寫檔』

    --程式需使用一個log 檔(log 檔名與方式不拘),方式為:當有新信進來時,
      parent 先寫,
child 後寫。
    -
-每當parent 檢查到有新信後,必須先在log 檔中寫入:
        “parent : discover new mail 年/月/日 時:分” 訊息,之後發SIGUSR1 給child,
        並在parent 中設定一個變數以決定可否
進行寫入(可寫入為YES,不可寫入為NO),
      並用迴圈不斷偵測變數是否改變。
    --child 收到SIGUSR1 後在螢幕上印出 “You have new mail !” 訊息,先在log 檔中寫入
      “child: You have new mail 年/月/日 時:分”,然後sleep(20)(即:睡20 秒鐘)再關檔,最後
      再發送SIGUSR2 給parent 通知它可以將變數改為YES。
    --在child 睡覺的20 秒間,寄信測試程式是否有實作出『parent 與child 不能同時寫檔』。

[實現方法]

1.  fork()   parent V.S child
  
    因為這次作業需要用到不同的process來做不同的事情

    我們需要用到fork()這個函式從原本程式一開始執行的process(parent)新生出一個procees(child)

    fork()函式的用法如下:

 
   #include <unistd.h>

        pid_t  fork(void);     //  pid_t 為一非負正整數的資料型別  就是每一個process都有他自己的pid

        Returns: 0 in child, process ID of child in parent, 1 on error

   所以當我們呼叫一次fork後 便要指定parent 跟 child做哪些事

   兩個process是同時進行 且無一定先後順序(除非利用傳送signal去block某一個process)

  使用格式大略如下:

  main(){
        if((pid=fork())==0){     
//child       pid為一pid_t型別的變數
            do something.... 
//child要做的事
        }
        else if(pid>0){  
//parent
            do something...  
//parent要做的事
        }
  }

2. (parent部份)檢查登入帳號是否正確 並檢查此帳號的信箱大小

    在一開始程式執行時 我們必須檢查目前登入的使用者帳號是否正確

    我們使用getlogin()來做這工作

  
  #include <unistd.h>

   char *
getlogin(void);

   Returns: pointer to string giving login name if OK, NULL on error

  若檢查無誤後
我們就要來檢查這位使用者的信箱容量大小

  這裡我另外寫了一個函式來完成此功能

 long get_file_size(const char *filename)  //filename即為"/var/mail/帳號”
{
    struct stat buf;     // stat這個struct之前作業有用過
                              // 裡面主要是放有各種檔案資訊如大小、名稱等

   
    if(stat(filename,&buf)==-1) {     //取得filename(信箱位置)所標示的檔案資訊
         printf("stat error\n");
         return -1;
    }
    return (long)buf.st_size;      //傳回檔案大小
}

 假如發現信箱大小有改變時 我們就要送一個signal給child

 這裡是用kill函式來實現

 #include <signal.h>

 int
kill(pid_t pid, int signo);   //pid為我們要傳signal的對象(此處為child)
                                          //signo為signal的種類


Both return: 0 if OK, 1 on error

最後在判斷一個全域變數flag來判斷log檔是否可寫入

YES的話就寫入時間 NO就繼續等待

3. (child部分) 印出收信訊息

當chlid接受到由parent的signal後

先把之前的變數flag改為NO

那怎麼接收signal呢

比如說我們在child要接收SIGUSR1這個signal

我們就在上面fork提到的child部份中使用一個函式如下:

#include <signal.h>

void (*
signal(int signo, void (*func)(int)))(int);

Returns: previous disposition of signal if OK, SIG_ERR on error

/*singo為要接收的訊號(如在child就是SIGUSR1)

(*func)(int)) 是一個function pointer的用法

 簡單說就是當這個process(child)接受到singo(SIGUSR1)時
 
 就要去執行那個function pointer所指的function裡的動作

這個function一般通稱為signal handler*/

之後便印出收信訊息

因為要避免讓有其他新的signal進來把log檔寫亂

在sleep的過程中 我們必須先把其他不相干的signal先block起來

最後寫完後 一樣用kill送signal給parent

parent也用上述一樣的發是去接收SIGUSR2

4. SIGUSR1 , SIGUSR2

在*nix系統中有很多預設好的signal

SIGINT 表示發出interrupt的訊號等

這些預設好的signal都是無法更改的

但其中的SIGUSR1 SIGUSR2是可以讓使用者自行去定義它的動作

也就是這個作業一些像寫log檔 sleep等動作

 

arrow
arrow
    全站熱搜

    molimomo 發表在 痞客邦 留言(0) 人氣()