Linux下signal通信研究(操作系統期中論文之自選題目研究)

         本學期操作系統課期中考覈需要寫兩篇小論文,前篇已經貼過了。今晚無聊,貼出此文,聊以慰籍空虛的心。要下載的朋友,請點擊這裏

要求如下:

下面是我的論文,由於格式原因,敘述部分直接上圖了:

三、初遇signal

在實驗三《Linux進程間通信》---“消息機制的示例程序”中有如下源碼:

#include <sys/types.h>
…
…
…
void sigend(int);
…
…
…
int msgid;

int main(void)
{
    struct mymsg msgbuf;

    if((msgid=msgget(MY_KEY, IPC_CREAT|IPC_EXCL|0666)) < 0 )
    {			/* message queue exists, act as client */
         …
         …
         …
    }
    else		/* acts as server */
    {
        signal(SIGINT, sigend);
        signal(SIGTERM, sigend);
        …
        …
        …
        }
    }
}

void sigend(int sig)
{
    msgctl(msgid, IPC_RMID, 0);
    exit(0);
}

四、Signal通信簡介

  1、基本知識

        軟中斷信號(signal,又簡稱爲信號)用來通知進程發生了異步事件。進程之間可以互相通過系統調用kill發送軟中斷信號。內核也可以因爲內部事件而給進程發送信號,通知進程發生了某個事件。收到信號的進程對各種信號有不同的處理方法。處理方法可以分爲三類:第一種是類似中斷的處理程序,對於需要處理的信號,進程可以指定處理函數,由該函數來處理。第二種方法是忽略某個信號,對該信號不做任何處理,就象未發生過一樣。第三種方法是對該信號的處理保留系統的默認值。這種缺省操作,對大部分的信號的缺省操作是使得進程終止。進程通過系統調用signal來指定進程對某個信號的處理行爲。

表頭文件:  #include<signal.h>

功 能:     設置某一信號的對應動作

函數原型 : void (*signal(intsignum,void(* handler)(int)))(int);

         或者:typedef void(*sig_t) ( int );

sig_t signal(int signum,sig_t handler);

參數說明

  第一個參數signum指明瞭所要處理的信號類型,它可以取除了SIGKILL和SIGSTOP外的任何一種信號。

  第二個參數handler描述了與信號關聯的動作,它可以取以下三種值:

  (1)一個無返回值的函數地址

  此函數必須在signal()被調用前申明,handler中爲這個函數的名字。當接收到一個類型爲sig的信號時,就執行handler 所指定的函數。這個函數應有如下形式的定義:

  void func(int sig);

  sig是傳遞給它的唯一參數。執行了signal()調用後,進程只要接收到類型爲sig的信號,不管其正在執行程序的哪一部分,就立即執行func()函數。當func()函數執行結束後,控制權返回進程被中斷的那一點繼續執行。

  (2)SIGIGN

  這個符號表示忽略該信號,執行了相應的signal()調用後,進程會忽略類型爲sig的信號。

  (3)SIGDFL

  這個符號表示恢復系統對信號的默認處理。

  函數說明

  signal()會依參數signum指定的信號編號來設置該信號的處理函數。當指定的信號到達時就會跳轉到參數handler指定的函數執行。當一個信號的信號處理函數執行時,

  如果進程又接收到了該信號,該信號會自動被儲存而不會中斷信號處理函數的執行,直到信號處理函數執行完畢再重新調用相應的處理函數。但是如果在信號處理函數執行時進程收到了其它類型的信號,該函數的執行就會被中斷。

  返回值: 返回先前的信號處理函數指針,如果有錯誤則返回SIG_ERR(-1)。

  附加說明 :在信號發生跳轉到自定的handler處理函數執行後,系統會自動將此處理函數換回原來系統預設的處理方式,如果要改變此操作請改用sigaction()。

  下面的情況可以產生Signal:

  1. 按下CTRL+C產生SIGINT

  2. 硬件中斷,如除0,非法內存訪問(SIGSEV)等等

  3. Kill函數可以對進程發送Signal

  4. Kill命令。實際上是對Kill函數的一個包裝

2.Signals

各種信號的基本信息如下;

 

 

Signal

 
 

Description

 
 

SIGABRT

 
 

由調用abort函數產生,進程非正常退出

 
 

SIGALRM

 
 

用alarm函數設置的timer超時或setitimer函數設置的interval timer超時

 
 

SIGBUS

 
 

某種特定的硬件異常,通常由內存訪問引起

 
 

SIGCANCEL

 
 

由Solaris Thread Library內部使用,通常不會使用

 
 

SIGCHLD

 
 

進程Terminate或Stop的時候,SIGCHLD會發送給它的父進程。缺省情況下該Signal會被忽略

 
 

SIGCONT

 
 

當被stop的進程恢復運行的時候,自動發送

 
 

SIGEMT

 
 

和實現相關的硬件異常

 
 

SIGFPE

 
 

數學相關的異常,如被0除,浮點溢出,等等

 
 

SIGFREEZE

 
 

Solaris專用,Hiberate或者Suspended時候發送

 
 

SIGHUP

 
 

發送給具有Terminal的Controlling Process,當terminal被disconnect時候發送

 
 

SIGILL

 
 

非法指令異常

 
 

SIGINFO

 
 

BSD signal。由Status Key產生,通常是CTRL+T。發送給所有Foreground Group的進程

 
 

SIGINT

 
 

由Interrupt Key產生,通常是CTRL+C或者DELETE。發送給所有ForeGround Group的進程

 
 

SIGIO

 
 

異步IO事件

 
 

SIGIOT

 
 

實現相關的硬件異常,一般對應SIGABRT

 
 

SIGKILL

 
 

無法處理和忽略。中止某個進程

 
 

SIGLWP

 
 

由Solaris Thread Libray內部使用

 
 

SIGPIPE

 
 

在reader中止之後寫Pipe的時候發送

 
 

SIGPOLL

 
 

當某個事件發送給Pollable Device的時候發送

 
 

SIGPROF

 
 

Setitimer指定的Profiling Interval Timer所產生

 
 

SIGPWR

 
 

和系統相關。和UPS相關。

 
 

SIGQUIT

 
 

輸入Quit Key的時候(CTRL+\)發送給所有Foreground Group的進程

 
 

SIGSEGV

 
 

非法內存訪問

 
 

SIGSTKFLT

 
 

Linux專用,數學協處理器的棧異常

 
 

SIGSTOP

 
 

中止進程。無法處理和忽略。

 
 

SIGSYS

 
 

非法系統調用

 
 

SIGTERM

 
 

請求中止進程,kill命令缺省發送

 
 

SIGTHAW

 
 

Solaris專用,從Suspend恢復時候發送

 
 

SIGTRAP

 
 

實現相關的硬件異常。一般是調試異常

 
 

SIGTSTP

 
 

Suspend Key,一般是Ctrl+Z。發送給所有Foreground Group的進程

 
 

SIGTTIN

 
 

當Background Group的進程嘗試讀取Terminal的時候發送

 
 

SIGTTOU

 
 

當Background Group的進程嘗試寫Terminal的時候發送

 
 

SIGURG

 
 

當out-of-band data接收的時候可能發送

 
 

SIGUSR1

 
 

用戶自定義signal 1

 
 

SIGUSR2

 
 

用戶自定義signal 2

 
 

SIGVTALRM

 
 

setitimer函數設置的Virtual Interval Timer超時的時候

 
 

SIGWAITING

 
 

Solaris  Thread Library內部實現專用

 
 

SIGWINCH

 
 

當Terminal的窗口大小改變的時候,發送給Foreground Group的所有進程

 
 

SIGXCPU

 
 

當CPU時間限制超時的時候

 
 

SIGXFSZ

 

 

 

進程超過文件大小限制

   

SIGXRES

  

Solaris專用,進程超過資源限制的時候發送

 

五、司機售票員問題

問題描述:

創建driver進程代表司機,seller進程代表售票員。

售票員捕捉SIGINT(代表開車),發SIGUSR1給司機,司機打印("let's go")。

售票員捕捉SIGQUIT(代表停車),發SIGUSR2給司機,司機打印("stop the bus")
司機捕捉SIGTSTP(代表車到總站),發SIGUSR1給售票員,售票員打印("please get off thebus")

Driver.c源代碼如下:

/* Driver.c: Act as driver
   *author : houjialin
   *To compile: gcc Driver.c –o driver
   */
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<error.h>
#include<stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_Key 3003 //共享存儲區Key
int shmid;//共享存儲區ID
int *shmptr;
int Sellerpid;
void CreatSHM()//創建共享存儲區,將自己的進程ID放入其中
{
    if((shmid=shmget(SHM_Key, sizeof(int), IPC_CREAT|0666)) < 0)
        printf("shmget error"),exit(1);
    if((shmptr=(int *)shmat(shmid, 0, 0)) == (int *)-1)
        printf("shmat error"),exit(1);
    *shmptr=getpid(); 
}
void driversigusr1(int signo)
{
    printf("let's go\n");
}
void driversigusr2(int signo)
{
    printf("stop the bus\n");
}
void driversigtstp(int signo)
{
    kill(Sellerpid,SIGUSR1);
}

int main()  /*act   as  Driver*/
{
    CreatSHM();
    while(*shmptr==getpid());//等待Seller將自己pid放入SHM
    Sellerpid=*shmptr;
    signal(SIGUSR1,driversigusr1);//Driver對信號SIGUSR1響應函數driversigusr1
    signal(SIGUSR2,driversigusr2);//Driver對信號SIGUSR2響應函數driversigusr2
    signal(SIGTSTP,driversigtstp);//Driver對信號SIGSTSP(Ctrl + z)響應函數driversigtstp
    while(1);
}

Seller.c源代碼如下:

/* Seller.c: Act as Seller
   *author : houjialin
   *To compile: gcc Seller.c –o seller
   */
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<error.h>
#include<stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_Key 3003 //共享存儲區Key
int shmid;
int *shmptr;
int Driverpid;//司機進程ID
void  getID()//通過共享存儲區獲取Driverpid,並將自己的放入其中
{
   shmid=shmget(SHM_Key, sizeof(int), 0666);
   shmptr=(int *)shmat(shmid, 0, 0);
   Driverpid=*shmptr;
   *shmptr=getpid();
}
void sellersigint(int signo)
{
    kill(Driverpid,SIGUSR1);
}

void sellersigquit(int signo)
{
    kill(Driverpid,SIGUSR2);
}
void sellersigusr1(int signo)
{
    printf("\nplease get off the bus\n");
}

int main()//act as Seller
{ 
    getID();
    signal(SIGINT, sellersigint);//Seller對信號SIGINT(Ctrl + c)響應函數sellersigint
    signal(SIGQUIT,sellersigquit);//Seller對信號SIGQUIT(Ctrl + \)響應函數sellersigquit     
    signal(SIGUSR1,sellersigusr1); //Seller對信號SIGUSR1響應函數sellersigusr1
    while(1);
}


 

運行結果如下:

 

 

Driver.c

 
 

Selller.c

 
 

houjialin@houjialin-ThinkPad-Edge:~/Documents$ ./driver

 

let's go

 

stop the bus

 

^Z

 
 

houjialin@houjialin-ThinkPad-Edge:~/Documents/期中$ ./seller

 

^C^\

 

please get off the bus

 

 

 

結果分析:

         當在seller進程中按下Ctrl+c(Ctrl+\,Ctrl+z同理)時,seller通過Signal捕獲該信號,然後調用sellersigint函數處理該信號。Sellersigint函數通過系統調用kill發送SIGUSR1給driver進程(通過進程ID標識)。在driver進程中,當捕獲到SIGUSR1後,掉用函數driversigusr1打印let’s go.

六、總結

     到此,通過Signal刪除消息隊列的原理已經基本明白了。當按下Ctrl+C結束該進程時(Kill同理),當前進程通過Signal捕獲該信號然後調用sigend(int) 處理。在sigend(int) 中,通過msgctl(msgid, IPC_RMID, 0)刪除相應的消息隊列。

     以上只是對Signal通信的一些簡單的介紹。在Linux操作系統中,許多系統進程通過Signal進行通信此處尚未涉及,有待今後研究。

附錄一:

msgServe.c源代碼:

/* msgServe.c: Act as Serve
   *author : houjialin
   *To compile: gcc msgServe.c –o msgServe
   */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define Histype 4  
#define Mytype 3
#define MY_KEY 20
struct mymessage 
{
   long mtype;
   char buffer[200];
} message;
int msgid;
void server()
{
    msgid=msgget(MY_KEY, IPC_CREAT|IPC_EXCL|0666);
    if(msgid<0) 
    {
     printf("消息隊列已經存在!");
     exit(0);
    }
    printf("server start:\n");
  while(1)
  {
     msgrcv(msgid,&message,sizeof(struct mymessage),Histype,0);
     printf("Question is:\n%s", message.buffer);
     printf("Here is the answer :\n");
     fgets(message.buffer,sizeof(struct mymessage)-sizeof(long)-1,stdin);
     message.mtype=Mytype;
     msgsnd(msgid, &message, sizeof(struct mymessage), 0);
  }
}
int main()
{
  server();
}

msgClient源代碼:

/* msgClient.c: Act as Client
   *author : houjialin
   *To compile: gcc msgClient.c –o msgClient
   */
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>
#define Mytype 4
#define Histype 3
#define MY_KEY 20
struct mymessage
{
   long mtype;
   char buffer[200];
} message;
int msgid;
void client()
{
    msgid=msgget(MY_KEY, 0666);
    if(msgid>=0)
    printf("welcome to numeber %d queue!Input your questions\n",msgid);
    else
    {
      exit(0);
    }
    fgets(message.buffer, sizeof(struct mymessage)-sizeof(long)-1, stdin);
    while(1)
    {
       message.mtype=Mytype;
       msgsnd(msgid, &message, sizeof(struct mymessage), 0);
       msgrcv(msgid,&message,sizeof(struct mymessage),Histype,0);
       printf("The answer is: %sInput your question:\n",message.buffer);
       fgets(message.buffer,sizeof(struct mymessage)-sizeof(long)-1,stdin);
    }
}
int main()
{
  client();
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章