Linux下signal通信研究(操作系统期中论文之自选题目研究)

 本学期操作系统课期中考核需要写两篇小论文,前篇已经贴过了。今晚无聊,贴出此文,聊以慰籍空虚的心。要下载的朋友,请点击这里

要求如下:

下面是我的论文,由于格式原因,叙述部分直接上图了:

三、初遇signal

在实验三《Linux进程间通信》---“消息机制的示例程序”中有如下源码:

[cpp]  view plain copy
  1. #include <sys/types.h>  
  2. …  
  3. …  
  4. …  
  5. void sigend(int);  
  6. …  
  7. …  
  8. …  
  9. int msgid;  
  10.   
  11. int main(void)  
  12. {  
  13.     struct mymsg msgbuf;  
  14.   
  15.     if((msgid=msgget(MY_KEY, IPC_CREAT|IPC_EXCL|0666)) < 0 )  
  16.     {           /* message queue exists, act as client */  
  17.          …  
  18.          …  
  19.          …  
  20.     }  
  21.     else        /* acts as server */  
  22.     {  
  23.         signal(SIGINT, sigend);  
  24.         signal(SIGTERM, sigend);  
  25.         …  
  26.         …  
  27.         …  
  28.         }  
  29.     }  
  30. }  
  31.   
  32. void sigend(int sig)  
  33. {  
  34.     msgctl(msgid, IPC_RMID, 0);  
  35.     exit(0);  
  36. }  

四、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源代码如下:

[cpp]  view plain copy
  1. /* Driver.c: Act as driver 
  2.    *author : houjialin 
  3.    *To compile: gcc Driver.c –o driver 
  4.    */  
  5. #include<unistd.h>  
  6. #include<signal.h>  
  7. #include<stdlib.h>  
  8. #include<error.h>  
  9. #include<stdio.h>  
  10. #include <sys/ipc.h>  
  11. #include <sys/shm.h>  
  12. #define SHM_Key 3003 //共享存储区Key  
  13. int shmid;//共享存储区ID  
  14. int *shmptr;  
  15. int Sellerpid;  
  16. void CreatSHM()//创建共享存储区,将自己的进程ID放入其中  
  17. {  
  18.     if((shmid=shmget(SHM_Key, sizeof(int), IPC_CREAT|0666)) < 0)  
  19.         printf("shmget error"),exit(1);  
  20.     if((shmptr=(int *)shmat(shmid, 0, 0)) == (int *)-1)  
  21.         printf("shmat error"),exit(1);  
  22.     *shmptr=getpid();   
  23. }  
  24. void driversigusr1(int signo)  
  25. {  
  26.     printf("let's go\n");  
  27. }  
  28. void driversigusr2(int signo)  
  29. {  
  30.     printf("stop the bus\n");  
  31. }  
  32. void driversigtstp(int signo)  
  33. {  
  34.     kill(Sellerpid,SIGUSR1);  
  35. }  
  36.   
  37. int main()  /*act   as  Driver*/  
  38. {  
  39.     CreatSHM();  
  40.     while(*shmptr==getpid());//等待Seller将自己pid放入SHM  
  41.     Sellerpid=*shmptr;  
  42.     signal(SIGUSR1,driversigusr1);//Driver对信号SIGUSR1响应函数driversigusr1  
  43.     signal(SIGUSR2,driversigusr2);//Driver对信号SIGUSR2响应函数driversigusr2  
  44.     signal(SIGTSTP,driversigtstp);//Driver对信号SIGSTSP(Ctrl + z)响应函数driversigtstp  
  45.     while(1);  
  46. }  

Seller.c源代码如下:

[cpp]  view plain copy
  1. /* Seller.c: Act as Seller 
  2.    *author : houjialin 
  3.    *To compile: gcc Seller.c –o seller 
  4.    */  
  5. #include<unistd.h>  
  6. #include<signal.h>  
  7. #include<stdlib.h>  
  8. #include<error.h>  
  9. #include<stdio.h>  
  10. #include <sys/ipc.h>  
  11. #include <sys/shm.h>  
  12. #define SHM_Key 3003 //共享存储区Key  
  13. int shmid;  
  14. int *shmptr;  
  15. int Driverpid;//司机进程ID  
  16. void  getID()//通过共享存储区获取Driverpid,并将自己的放入其中  
  17. {  
  18.    shmid=shmget(SHM_Key, sizeof(int), 0666);  
  19.    shmptr=(int *)shmat(shmid, 0, 0);  
  20.    Driverpid=*shmptr;  
  21.    *shmptr=getpid();  
  22. }  
  23. void sellersigint(int signo)  
  24. {  
  25.     kill(Driverpid,SIGUSR1);  
  26. }  
  27.   
  28. void sellersigquit(int signo)  
  29. {  
  30.     kill(Driverpid,SIGUSR2);  
  31. }  
  32. void sellersigusr1(int signo)  
  33. {  
  34.     printf("\nplease get off the bus\n");  
  35. }  
  36.   
  37. int main()//act as Seller  
  38. {   
  39.     getID();  
  40.     signal(SIGINT, sellersigint);//Seller对信号SIGINT(Ctrl + c)响应函数sellersigint  
  41.     signal(SIGQUIT,sellersigquit);//Seller对信号SIGQUIT(Ctrl + \)响应函数sellersigquit       
  42.     signal(SIGUSR1,sellersigusr1); //Seller对信号SIGUSR1响应函数sellersigusr1  
  43.     while(1);  
  44. }  


 

运行结果如下:

 

     
 

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源代码:

[cpp]  view plain copy
  1. /* msgServe.c: Act as Serve 
  2.    *author : houjialin 
  3.    *To compile: gcc msgServe.c –o msgServe 
  4.    */  
  5. #include<stdio.h>  
  6. #include<sys/types.h>  
  7. #include<sys/ipc.h>  
  8. #include<sys/msg.h>  
  9. #define Histype 4    
  10. #define Mytype 3  
  11. #define MY_KEY 20  
  12. struct mymessage   
  13. {  
  14.    long mtype;  
  15.    char buffer[200];  
  16. } message;  
  17. int msgid;  
  18. void server()  
  19. {  
  20.     msgid=msgget(MY_KEY, IPC_CREAT|IPC_EXCL|0666);  
  21.     if(msgid<0)   
  22.     {  
  23.      printf("消息队列已经存在!");  
  24.      exit(0);  
  25.     }  
  26.     printf("server start:\n");  
  27.   while(1)  
  28.   {  
  29.      msgrcv(msgid,&message,sizeof(struct mymessage),Histype,0);  
  30.      printf("Question is:\n%s", message.buffer);  
  31.      printf("Here is the answer :\n");  
  32.      fgets(message.buffer,sizeof(struct mymessage)-sizeof(long)-1,stdin);  
  33.      message.mtype=Mytype;  
  34.      msgsnd(msgid, &message, sizeof(struct mymessage), 0);  
  35.   }  
  36. }  
  37. int main()  
  38. {  
  39.   server();  
  40. }  

msgClient源代码:

[cpp]  view plain copy
  1. /* msgClient.c: Act as Client 
  2.    *author : houjialin 
  3.    *To compile: gcc msgClient.c –o msgClient 
  4.    */  
  5. #include<stdio.h>  
  6. #include<sys/types.h>  
  7. #include<sys/ipc.h>  
  8. #include<sys/msg.h>  
  9. #define Mytype 4  
  10. #define Histype 3  
  11. #define MY_KEY 20  
  12. struct mymessage  
  13. {  
  14.    long mtype;  
  15.    char buffer[200];  
  16. } message;  
  17. int msgid;  
  18. void client()  
  19. {  
  20.     msgid=msgget(MY_KEY, 0666);  
  21.     if(msgid>=0)  
  22.     printf("welcome to numeber %d queue!Input your questions\n",msgid);  
  23.     else  
  24.     {  
  25.       exit(0);  
  26.     }  
  27.     fgets(message.buffer, sizeof(struct mymessage)-sizeof(long)-1, stdin);  
  28.     while(1)  
  29.     {  
  30.        message.mtype=Mytype;  
  31.        msgsnd(msgid, &message, sizeof(struct mymessage), 0);  
  32.        msgrcv(msgid,&message,sizeof(struct mymessage),Histype,0);  
  33.        printf("The answer is: %sInput your question:\n",message.buffer);  
  34.        fgets(message.buffer,sizeof(struct mymessage)-sizeof(long)-1,stdin);  
  35.     }  
  36. }  
  37. int main()  
  38. {  
  39.   client();  
  40. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值