第二十九篇,signal()函数的具体用法,以及对信号集的操作,sigempty(),sigfillset(),sigaddset()等函数的用法,Linux的IPC对象,进程间消息队列通信的详细说明

一、研究signal函数的第二个参数。
现实例子:
假设将来收到"请我吃饭"这个信号,我就说好,一起去绿地。  --> 默认动作
假设将来收到"请我吃饭"这个信号,我就打印helloworld。   --> 自定义动作
假设将来收到"请我吃饭"这个信号,我理都不理你。         --> 信号忽略

1、自定义动作。  (收到信号之后,想做什么事情,由你自己决定)

   the  address  of  a programmer-defined function (a "signal handler").
     //填一个信号处理函数    --> 那么将来收到信号,就会自动执行该函数。

void func(int sig)  //信号处理函数必须长这个样子
{
    printf("helloworld");
}

signal("请我吃饭",func);

2、信号忽略。   (收到信号之后,没有任何反应)
   SIG_IGN

signal("请我吃饭",SIG_IGN);  //将来收到这个信号,不会有任何的动作。

3、默认动作。  (收到信号之后,就会做相应的动作)
   SIG_DFL

signal("请我吃饭",SIG_DFL);   -> 将来收到这个信号,就会执行这个信号的默认动作

----------------------------------------------------

void func(int sig)
{
    printf("helloworld\n");
}

int main(int argc,char *argv[])
{
    //signal(SIGINT,func); //自定义动作
    //signal(SIGINT,SIG_IGN); //信号忽略
    signal(SIGINT,SIG_DFL); //默认动作
    pause();
    
    return 0;
}
----------------------------------------------------


   例题: 验证以下结论:
    The signals SIGKILL and SIGSTOP cannot be caught or ignored.
    //SIGKILL和SIGSTOP这两个信号不能够被捕捉或者忽略


#include
#include
#include

void func(int sig)
{
    printf("helloworld!\n");
}

int main(int argc,char *argv[])
{
    //1. 尝试捕捉SIGKILL
    //signal(SIGKILL,func);
    
    //2. 尝试忽略SIGKILL
    signal(SIGKILL,SIG_IGN);
    pause();
    
    return 0;
}


二、信号集。
1、什么是信号集?
信号集是一个集合来的,而每一个成员都是一个信号来的。

2、为什么要学习信号集?
通过将信号加入到信号集中,再设置一个阻塞状态给信号集,那么这个信号集中所有的信号都会有阻塞状态。

3、信号阻塞与信号忽略有什么区别?
信号响应:收到信号之后,会响应信号的动作。
信号忽略:收到信号之后,直接丢弃这个信号。
信号阻塞:进程在阻塞某一个信号的前提下,收到了这个信号,不会马上响应,而是要等到解除阻塞才可以响应。
         (这个信号再没有被响应的时候,不会丢弃,而是放在一个挂起队列)

三、信号集处理函数?
1、定义信号集。
信号集其实就是一个变量,数据类型是:sigset_t
定义一个信号集: sigset_t set;

2、清空信号集。   --> 将所有的信号都剔除到集合之外。   --> sigemptyset()   --> man 3 sigemptyset

   #include

   int sigemptyset(sigset_t *set);

参数:
    set: 信号集的地址

返回值:
    成功:0
    失败:-1

3、快速将linux下所有的信号都加入到信号集中。  --> sigfillset()  -> man 3 sigfillset

    #include

   int sigfillset(sigset_t *set);

参数:
    set: 信号集的地址

返回值:
    成功:0
    失败:-1

4、将某一个信号加入到信号集中。  --> sigaddset()   --> man 3 sigaddset
 
    #include  

   int sigaddset(sigset_t *set, int signum);

参数:
    set: 信号集的地址
    signum:需要添加到信号集的信号值

返回值:
    成功:0
    失败:-1

5、将某一个信号删除掉。   -->  sigdelset()   --> man 3 sigdelset

     #include  

   int sigdelset(sigset_t *set, int signum);

参数:
    set:信号集的地址
    signum:需要删除的信号

返回值:
    成功:0
    失败:-1


6、测试某一个信号在不在集合中。  --> sigismember()  --> man 3 sigismember

    #include

   int sigismember(const sigset_t *set, int signum);

参数:
    set: 信号集的地址
    signum: 需要判断的信号

返回值:
    在集合中:1
    不在集合中:0
    出错:-1


  练习1: 创建一个信号集,先将所有信号都加入到信号集中,判断一下SIGINT在不在集合中,然后清空这个信号集,判断一下SIGINT在不在集合中
      再单独把SIGINT加入到信号集中,判断一下SIGINT在不在集合中,把SIGINT这个信号从信号集中删除,判断一下SIGINT在不在集合中。


#include
#include

int main(int argc,char *argv[])
{
    //1. 定义信号集
    sigset_t set;
    
    //2. 将所有的信号都添加到信号集中
    sigfillset(&set);
    
    //3. 判断一下在不在集合中
    int ret;
    ret = sigismember(&set,SIGINT);
    if(ret == 1)
        printf("is member!\n");  //√
    else
        printf("is not member\n");
    
    //4. 清空信号集
    sigemptyset(&set);
    
    //5. 判断一下在不在集合中
    ret = sigismember(&set,SIGINT);
    if(ret == 1)
        printf("is member!\n");  
    else
        printf("is not member\n");//√
    
    //6. 单独将SIGINT信号添加到信号集中
    sigaddset(&set,SIGINT);
    
    //7. 判断一下在不在集合中
    ret = sigismember(&set,SIGINT);//√
    if(ret == 1)
        printf("is member!\n");  
    else
        printf("is not member\n");
    
    //8. 将SIGINT删除
    sigdelset(&set,SIGINT);
    
    //9. 判断一下在不在集合中
    ret = sigismember(&set,SIGINT);
    if(ret == 1)
        printf("is member!\n");  
    else
        printf("is not member\n");//√
    
    return 0;
}

四、如何设置信号集为阻塞状态/解除阻塞状态?   --> sigprocmask()   -> man 2 sigprocmask
功能:  examine and change blocked signals
    //设置阻塞状态

使用格式:
    #include

       int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数:
    how:
        SIG_BLOCK    --> 想设置信号集set为阻塞状态
        SIG_UNBLOCK  --> 解除信号集set的阻塞状态
    set: 信号集的地址
    oldset:保留之前状态的指针,如果不关心,则设置为NULL。

返回值:
    成功:0
    失败:-1
    

例如:
   sigset_t set;
   把一些信号加入set中

   sigprocmask(SIG_BLOCK,&set,NULL);    --> 设置set信号集为阻塞状态
                    --> 这个信号集中的信号都是阻塞状态
                    --> 以后这个进程收到信号集中的信号,不会马上响应,而是把信号放在挂起队列上。

   .....
   .....  -> 在这段时间内,信号集都是阻塞。


   sigprocmask(SIG_UNBLOCK,&set,NULL);  --> 解除阻塞
                    --> 马上去响应挂起队列上的信号


   练习2:先让进程产生一个子进程。
      父进程将SIGUSR1添加到信号集中,判断信号集在不在集合中,然后给信号集设置阻塞状态,让该状态持续10s(那么在10s,都是阻塞的)
          10s,解除阻塞,看看会不会响应该信号。

      子进程在5s时,发送信号给父进程。

      子进程信号发送出去之后,马上响应    -> 信号响应
                10s时才响应  -> 信号阻塞   √
                永远都不响应 -> 信号忽略

  
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

void func(int sig)
{
    printf("helloworld!\n");
}

int main(int argc,char *argv[])
{
    //1. 先产生一个子进程
    pid_t x;
    x = fork();
    if(x > 0)
    {
        //2. 父进程先捕捉SIGUSR1
        signal(SIGUSR1,func);
        
        //3. 定义一个信号集,并将SIGUSR1添加到信号集中
        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set,SIGUSR1);
        
        int ret;
        ret = sigismember(&set,SIGUSR1);
        if(ret == 1)
            printf("SIGUSR1 is member!\n");
        else 
            printf("SIGUSR is not member!\n");
        
        //4. 设置阻塞状态
        sigprocmask(SIG_BLOCK,&set,NULL);
        
        //5. 持续10s
        int i;
        for(i=10;i>0;i--)
        {
            sleep(1);
            printf("%d\n",i);
        }
        
        //6. 解除阻塞
        sigprocmask(SIG_UNBLOCK,&set,NULL);
        
        //7. 主动回收子进程的资源
        wait(NULL);
        
        //8. 父进程
        exit(0);
    }
    
    if(x == 0)
    {
        sleep(5);
        kill(getppid(),SIGUSR1);
        printf("I send SIGUSR1 to parent!\n");
        exit(0);
    }
    
    return 0;
}

五、linux IPC对象。
1、什么是IPC对象?
在linux下,IPC对象指的是消息队列、共享内存、信号量。如果用户需要使用IPC对象来实现进程之间的通信,首先必须为IPC对象申请对应的资源。
例如:如果我们想使用消息队列来通信,那么就必须为消息队列申请key值与ID号。

2、查看系统中所有的IPC对象?
1)查看所有的IPC对象: ipcs -a
gec@ubuntu:/mnt/hgfs/GZ2180/01-课程记录/10-系统编程/03-系统编程-01.14/01-代码$ ipcs -a

------ Message Queues --------  //消息队列
key        msqid      owner      perms      used-bytes   messages  
key值      消息队列ID号  

------ Shared Memory Segments --------  //共享内存
key        shmid      owner      perms      bytes      nattch     status      
key值      共享内存ID号

0x00000000 425984     gec        600        524288     2          dest         
0x00000000 524289     gec        600        16777216   2                       
0x00000000 983042     gec        600        524288     2          dest         
0x00000000 786435     gec        600        524288     2          dest         
0x00000000 884740     gec        600        524288     2          dest         
0x00000000 1212421    gec        600        524288     2          dest         
0x00000000 2424838    gec        600        524288     2          dest         
0x00000000 1245191    gec        600        524288     2          dest         
0x00000000 1277960    gec        600        67108864   2          dest         
0x00000000 1474569    gec        600        524288     2          dest         
0x00000000 1572874    gec        600        524288     2          dest         
0x00000000 1605643    gec        600        4194304    2          dest         
0x00000000 1703948    gec        600        524288     2          dest         
0x00000000 2228237    gec        600        2097152    2          dest         
0x00000000 1835022    gec        600        524288     2          dest         

------ Semaphore Arrays -------- //信号量
key        semid      owner      perms      nsems
key值      信号量ID    
 
key值: 类似于每一个人的身份证号码
ID:类似于使用身份证刷出来的排队号码

2)删除IPC对象
想删除消息队列: ipcrm -Q 消息队列的key值   /  ipcrm -q 消息队列的ID号
想删除共享内存: ipcrm -M 共享内存的key值   /  ipcrm -m 共享内存的ID号
想删除信号量:   ipcrm -S 信号量的key值     /  ipcrm -s 信号量的ID号

3、既然说使用IPC对象之前,要申请key值,那么这个key值怎么来的?   --> ftok()   --> man 3 ftok
功能: ftok - convert a pathname and a project identifier to a System V IPC key
        //提供一个合法的路径和一个数字,就可以得到一个key值

使用格式:
    #include
        #include

       key_t ftok(const char *pathname, int proj_id);

参数:
    pathname:一个合法的路径   "."
    proj_id:非0整数           10

返回值:
    成功:key值
    失败:-1

   The resulting value is the same for all pathnames that name the same file, when the same value of proj_id is used.  
  //当文件路径与proj_id一样的时候,两个函数的返回值肯定一样。

   The value returned should be different when the (simultaneously existing) files or the project IDs differ.
  //只要文件路径/proj_id有一个不一样,那么返回值就会不一样。


  例题: 验证key值。


#include
#include
#include

int main(int argc,char *argv[])
{
    key_t key1 = ftok(".",10);
    printf("key1 = %d\n",key1);
    
    key_t key2 = ftok(".",20);
    printf("key2 = %d\n",key2);
    
    key_t key3 = ftok("..",10);
    printf("key3 = %d\n",key3);
    
    key_t key4 = ftok("..",20);
    printf("key4 = %d\n",key4);
    
    key_t key5 = ftok(".",10);
    printf("key5 = %d\n",key5);
    
    return 0;
}

运行结果:
gec@ubuntu:/mnt/hgfs/GZ2180/01-课程记录/10-系统编程/03-系统编程-01.14/01-代码$ ./key
key1 = 170787629
key2 = 338559789
key3 = 170787600
key4 = 338559760
key5 = 170787629

4、尝试创建一条消息队列。   -->  msgget()   --> man 2 msgget
功能: msgget - get a System V message queue identifier
        //获取一个消息队列的ID号
        
使用格式:
    #include
        #include
        #include

       int msgget(key_t key, int msgflg);

参数:
    key:  key值
    msgflg:  IPC_CREAT|0666   --> 不存在则创建,并且消息队列是可读可写
                  --> 存在就不会创建

返回值:
    成功:消息队列的ID号
    失败:-1


   练习3:尝试写一个程序,在程序中创建一条消息队列出来,顺便打印key值与ID号。
      程序执行后,通过ipcs -a看看,刚才创建的消息队列在不在,如果存在,则对比key值与ID号跟程序的打印结果一不一样。
      最后使用命令将该消息队列删除。


#include
#include
#include
#include

int main(int argc,char *argv[])
{
    //1. 申请key值
    key_t key = ftok(".",10);
    printf("key = %#x\n",key);
    
    //2. 申请消息队列的ID号
    int msgid = msgget(key,IPC_CREAT|0666);
    printf("msgid = %d\n",msgid);
    
    return 0;
}

六、进程之间的通信方式  ---  消息队列。
1、为消息队列申请key值。
   key_t key = ftok(".",10);

2、根据key值来申请消息队列的ID号
   int msgid = msgget(key,IPC_CREAT|0666);

3、如何写入数据到消息队列中?  -->  msgsnd()  --> man 2 msgsnd

    #include
    #include
    #include

   int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

参数:
    msqid:  消息队列的ID号
    msgp:写入的数据的结构体的地址

struct msgbuf {
       long mtype;       //消息的类型,必须是一个大于0的数字
       char mtext[100];  //消息的正文
};
    msgsz:数据结构体中正文的大小
    msgflg:0   --> 默认是阻塞属性 (有数据就读,每数据就等待数据)

返回值:
    成功:0
    失败:-1

4、如何读取消息队列中的数据?  --> msgrcv()  --> man 2 msgrcv

    #include
    #include
    #include

   ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

参数:
    msqid:  消息队列的ID号
    msgp:   接收数据的缓冲区   --> 结构体变量的地址 
    msgsz:  数据结构体正文的大小
    msgtyp: 想读取的类型
    msgflg: 0  --> 普通属性

返回值:
    成功:真正读取到正文数组的字节数
    失败:-1

5、如何删除消息队列?   -->  msgctl()   --> man 2 msgctl

   #include
   #include
   #include

  int msgctl(int msqid, int cmd, struct msqid_ds *buf);

参数:
    msqid:消息队列的ID号
    cmd: IPC_RMID   --> 想删除消息队列
    buf: 如果是删除,则设置为NULL

返回值:
    成功:0
    失败:-1


   例题2: 要求使用消息队列,实现两个进程之间的通信。

写端:
#include
#include
#include
#include
#include
#include
#include

struct msgbuf{
    long mtype; //类型
    char mtext[50]; //正文
};

int main(int argc,char *argv[])
{
    //1. 申请key值
    key_t key = ftok(".",10);
    
    //2. 申请消息队列的ID号
    int msgid = msgget(key,IPC_CREAT|0666);
    
    //3. 不断往消息队列中发送数据
    struct msgbuf gec;
    int ret;
    while(1)
    {
        bzero(&gec,sizeof(gec));
        
        gec.mtype = 30;
        fgets(gec.mtext,sizeof(gec.mtext),stdin);
        
        ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);
        if(ret == -1)
        {
            printf("msgsnd error!\n");
            exit(-1);
        }
        
        if(strncmp(gec.mtext,"quit",4) == 0)
        {
            break;
        }
    }
    
    return 0;
}

读端:
#include
#include
#include
#include
#include
#include
#include


struct msgbuf{
    long mtype; //类型
    char mtext[50]; //正文
};

int main(int argc,char *argv[])
{
    //1. 申请key值
    key_t key = ftok(".",10);
    
    //2. 申请消息队列的ID号
    int msgid = msgget(key,IPC_CREAT|0666);
    
    //3. 不断接收消息队列上的数据
    struct msgbuf gec;
    int ret;
    while(1)
    {
        bzero(&gec,sizeof(gec));
        ret = msgrcv(msgid,&gec,sizeof(gec.mtext),30,0);
        if(ret == -1)
        {
            printf("msgrcv error!\n");
            exit(-1);
        }
        
        printf("from queue:%s",gec.mtext);
        
        if(strncmp(gec.mtext,"quit",4) == 0)
        {
            break;
        }
    }
    
    //4. 删除消息队列
    msgctl(msgid,IPC_RMID,NULL);
    
    return 0;
}


 

你可能感兴趣的:(嵌入式C语言基础,linux)