zoukankan      html  css  js  c++  java
  • 2019年8月12日星期一(系统编程)

    2019812日星期一

    . linux信号集概念

    1. 什么是信号集?

    信号集是一个集合,而每一个成员都是一个信号来的,通过把信号加入到信号集中,再设置阻塞状态给信号集,那么整个信号集中的所有信号都会变成阻塞的状态。

    2. 信号阻塞与信号忽略有区别?

    忽略:收到信号之后,会直接丢弃。

    阻塞:在阻塞的状态下收到信号,不会马上响应,而是等待解除阻塞状态之后,才会响应。

    . 信号集处理函数?

    0. 信号集怎么定义?

       sigset_t是信号集的数据类型,直接定义一个变量即可,例子: sigset_t set;

    1. 清空信号集   ->  sigemptyset()  -> man 3 sigemptyset

        #include <signal.h>

       int sigemptyset(sigset_t *set);

        set:需要清空的信号集的地址

       返回值:

           成功:0

           失败:-1

    2.将所有linux信号加入到信号集中  -> sigfillset()  -> man 3 sigfillset

       #include <signal.h>

      int sigfillset(sigset_t *set);

       set:需要填充满信号的信号集的地址

       返回值:

           成功:0

           失败:-1

    3. 添加一个信号到信号集中  -> sigaddset()  -> man 3 sigaddset

        #include <signal.h>

      int sigaddset(sigset_t *set, int signum);

       set: 需要添加信号的信号集的地址

       signum: 需要添加的信号值

      返回值:

           成功:0

           失败:-1

    4. 从信号集中删除一个信号  -> sigdelset()  -> man 3 sigdelset

        #include <signal.h>

      int sigdelset(sigset_t *set, int signum);

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

       signum: 需要删除的信号值

      返回值:

           成功:0

           失败:-1

    5. 测试一个信号是不是在信号集中。

       #include <signal.h>

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

       set:需要判断的信号集的地址。

       signum:需要判断的信号值。

      返回值:

           signum在set中,那么返回1

           signum不在set中,那么返回0

           出错: -1

    6. 设置信号集的阻塞状态?  ->  sigprocmask()  -> man 3 sigprocmask

         #include <signal.h>

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

        how:

           SIG_BLOCK  -> 将信号集设置为阻塞状态

           SIG_UNBLOCK  -> 解除信号集的阻塞状态

        set: 需要设置属性的信号集的地址

       oset: 保留之前状态指针,如果不关心,则填NULL。

       返回值:

           成功:0

           失败:-1

      练习1:进程产生一个子进程。

              父进程把SIGUSR1加入到信号集中,判断该信号是否在集合中,设置阻塞10秒,10秒之后解除阻塞。

             子进程父进程设置阻塞属性之后,发送信号给父进程,看看会不会马上响应?10秒后呢?

    不会马上响应,而是要等到10秒之后才会响应。

     

    #include <sys/types.h>

    #include <signal.h>

    #include <unistd.h>

    #include <sys/wait.h>

    #include <stdlib.h>

    #include <stdio.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <string.h>

     

    void fun(int sig)

    {

           printf("catch sig = %d ",sig);

    }

     

    int main(int argc,char *argv[])

    {

           pid_t x;

           x = fork();

          

           if(x > 0)

           {

                  signal(SIGUSR1,fun);

                 

                  int ret,i;

                  sigset_t set; //定义一个信号集

                  sigemptyset(&set); //清空信号

                  sigaddset(&set,SIGUSR1);

                  ret = sigismember(&set,SIGUSR1);

                  if(ret <= 0)

                  {

                         printf("not member! ");

                         exit(-1);

                  }

                 

                  sigprocmask(SIG_BLOCK,&set,NULL);

                 

                  for(i=10;i>0;i--)

                  {

                         sleep(1);

                         printf("%d ",i);

                  }

                 

                  sigprocmask(SIG_UNBLOCK,&set,NULL);

                  wait(NULL);

                  exit(0);

           }

          

           if(x == 0)

           {

                  sleep(3);

                  kill(getppid(),SIGUSR1);

                  printf("I send SIGUSR1 to parent! ");

                  exit(0);

           }

          

           return 0;

    }

       练习2:验证阻塞掩码会被子进程继承。

     

    #include <sys/types.h>

    #include <signal.h>

    #include <unistd.h>

    #include <sys/wait.h>

    #include <stdlib.h>

    #include <stdio.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <string.h>

     

     

    void fun(int sig)

    {

           printf("catch sig = %d ",sig);

    }

     

    int main(int argc,char *argv[])

    {

           signal(SIGUSR1,fun);

          

           //1. 让进程设置阻塞信号

           sigset_t set;

           sigemptyset(&set);

           sigaddset(&set,SIGUSR1);

           sigprocmask(SIG_BLOCK,&set,NULL);

          

           //2. 带着这个阻塞状态去产生一个子进程

           pid_t x;

           int i;

           x = fork();

           if(x > 0)

           {

                  printf("parent pid = %d ",getpid());

                  for(i=20;i>0;i--) //在20S内,发送SIGUSR1给父进程。

                  {

                         sleep(1);

                         printf("parent i = %d ",i);   //挂起队列上:SIGUSR1

                  }

                 

                  //给父进程解除阻塞

                  sigprocmask(SIG_UNBLOCK,&set,NULL); //会响应

                  printf("parent unblock! ");

                 

                  wait(NULL);

                  exit(0);

           }

          

           if(x == 0)

           {

                  printf("child pid = %d ",getpid());

                  //究竟有没有继承过来?

                  for(i=35;i>0;i--) //在35S内,发送SIGUSR1给子进程。

                  {

                         sleep(1);

                         printf("child i = %d ",i);   //挂起队列上:SIGUSR1

                  }

                 

                  //给子进程解除阻塞

                  sigprocmask(SIG_UNBLOCK,&set,NULL); //会响应

                  printf("child unblock! ");

                 

                  exit(0);

           }

          

           return 0;

    }

       练习3:在进程的挂起队列中,没有相同的信号(即相同的信号会被丢弃)

     

    父进程:

    sigprocmask(SIG_BLOCK,&set,NULL);

                 

    for(i=10;i>0;i--)

    {

           sleep(1);

           printf("%d ",i);

    }

                 

    sigprocmask(SIG_UNBLOCK,&set,NULL);

    wait(NULL);

    exit(0);

     

    子进程:

    sleep(3);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    kill(getppid(),SIGUSR1);

    printf("I send SIGUSR1 to parent! ");   -> 结果: 只会响应一次SIGUSR1。

    . linux IPC对象

    1. 什么是IPC对象?

    linux下,IPC对象指的是消息队列,共享内存,信号量。如果用户想在程序中使用IPC对象进行进程之间通信,首先必须申请IPC对象对应的资源。例子: 想使用消息队列,必须先申请消息队列对应的ID号(key值)。

    2. 查看系统所有的IPC对象

    1)查看IPC对象: ipcs -a

    ------ Shared Memory Segments -------- //共享内存

    key        shmid      owner      perms      bytes      nattch     status   

     

    ------ Semaphore Arrays -------- //信号量

    key        semid      owner      perms      nsems    

     

    ------ Message Queues -------- //消息队列

    key        msqid      owner      perms      used-bytes   messages   

     

    key: key值,每一个IPC对象都有一个唯一的key值。

    id:  ID号,是根据唯一的key值申请而来的。

    2)删除IPC对象

    消息队列:  ipcrm -q key值 / ipcrm -q 消息队列ID

    共享内存:  ipcrm -m key值 / ipcrm -m 共享内存ID

    信号量:    ipcrm -s key值 / ipcrm -s 信号量ID

    3. linux下,如果需要使用任何一个IPC对象,都必须先申请对应的key值。  -> ftok()  -> man 3 ftok

    功能: convert a pathname and a project identifier to a System V IPC key

           //给定一个合法的路径以及一个整数,就可以得到一个key值。

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

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

           pathname:一个合法路径。 常用: "."

           proj_id: 非0整数  128   常用:"10"

           返回值:

                  成功: key值

                  失败: -1

    注意: 当参数全部一致时,得到的key值是一样。

           100 = ftok(".",10);

           ftok(".",10);  -> 这个函数的返回值必须是100。

    举例子。

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <stdio.h>

     

    int main(int argc,char *argv[])

    {

           key_t key;

          

           key = ftok(".",10);

           printf("key = %d ",key);

          

           key =ftok("..",10);

           printf("key = %d ",key);

          

           key =ftok(".",20);

           printf("key = %d ",key);

          

           key =ftok(".",10);

           printf("key = %d ",key);  -> 与第一个一样!

          

           return 0;

    }

    . 进程之间通信方式 - 消息队列

    1. 管道通信与消息队列通信非常相似,有什么差异?

    管道通信:不能读取特定的数据,只要管道中有数据,就一定要读取出来,操作数据时使用文件IO函数。

    消息队列通信: 可以读取特定类型的数据,有数据但是数据类型不符合,那么就可以不读取,操作数据时使用消息队列特定的函数。

    2. 消息队列的机制?

    每一个在消息队列中数据都包含"类型+正文",读取方只需要提供类型,就可以读取到消息队列中相对应的数据了。

    3. 消息队列对应的函数接口?

    0)由于是使用IPC对象,所以必须要申请key值。

       key = ftok(".",10);

    1)根据key值申请消息队列的ID号。  -> msgget()  -> man 2 msgget

    功能:get a message queue identifier  -> 获取消息队列的ID号

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/msg.h>

           int msgget(key_t key, int msgflg);

           key: 消息队列对应的key值。

           msgflg: IPC_CREAT|0666  -> 不存在则创建

                   IPC_EXCL        -> 存在则报错

           The execute permissions are not used.  -> 消息队列中执行权限是无效的,不被使用,0777与0666一致的!

           返回值:

                  成功:消息队列的ID号

                  失败:-1

    2)往消息队里中发送数据。  -> msgsnd()  -> man 2 msgsnd

    使用格式:

           #include <sys/types.h>

           #include <sys/ipc.h>

           #include <sys/msg.h>

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

           msqid:消息队列的ID

           msgp:写入到消息队列的结构体缓冲区的地址

    struct msgbuf {

           long mtype;       //消息的类型,必须>0

           char mtext[x];    //消息的正文,自定义的数组/结构体  x由你来决定

    };

           msgsz:消息正文的大小

           msgflg:普通属性  -> 0

           返回值:

                  成功:0

                  失败:-1

    3)从消息队列中读取数据。  -> msgrcv()  -> man 3 msgrcv

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/msg.h>

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

           msqid:消息队列的ID

           msgp:读取消息队列中数据的结构体缓冲区

    struct msgbuf {

           long mtype;       //消息的类型,必须>0

           char mtext[x];    //消息的正文,自定义的数组/结构体  x由你来决定

    };

           msgsz:消息队列正文大小

           msgtyp:读取的类型

           msgflg:普通属性  -> 0

           返回值:

                  成功:正文的字节数

                  失败:-1

    4)删除消息队列的ID  -> msgctl()  -> man 2 msgctl    -> 一般是读端来完成。

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/msg.h>

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

           msqid:消息队列的ID

           cmd:  IPC_RMID  -> 删除消息队列命令

           buf:  如果是删除,则填NULL。

           返回值:

                  成功:0

                  失败:-1

       练习4:使用消息队列,完成两个进程之间的通信。

    /* 读端 */

    #include "head.h"

     

    struct msgbuf{

           long mtype;

           char mtext[50];

    };

     

    int main(int argc,char *argv[])

    {

           //1. 申请key值

           key_t key = ftok(".",10);

          

           //2. 根据key值申请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),10,0);

                  if(ret == -1)

                  {

                         printf("msgrcv error! ");

                         exit(0);

                  }

                 

                  printf("from peer:%s",gec.mtext);

                  if(strncmp(gec.mtext,"quit",4) == 0)

                  {

                         break;

                  }

           }

          

           //4. 删除消息队列

           msgctl(msgid,IPC_RMID,NULL);

          

           return 0;

    }

     

    /* 写端 */

    #include "head.h"

     

    struct msgbuf{

           long mtype;

           char mtext[50];

    };

     

    int main(int argc,char *argv[])

    {

           //1. 申请key值

           key_t key = ftok(".",10);

          

           //2. 根据key值申请ID号

           int msgid = msgget(key,IPC_CREAT|0666);

          

           //3. 定义数据缓冲区

           struct msgbuf gec;

           int ret;

          

           while(1)

           {

                  bzero(&gec,sizeof(gec));

                  gec.mtype = 10; //类型

                  fgets(gec.mtext,50,stdin); //正文

                 

                  ret = msgsnd(msgid,&gec,strlen(gec.mtext),0);

                  if(ret == -1)

                  {

                         printf("msgsnd error! ");

                         exit(-1);

                  }

                 

                  if(strncmp(gec.mtext,"quit",4) == 0)

                  {

                         break;

                  }     

           }

          

           return 0;

    }

  • 相关阅读:
    Prism 源码解读5-数据绑定和命令
    Prism 源码解读4-ViewModel注入
    Prism 源码解读3-Modules加载
    Prism 源码解读2-View的加载和控制
    java中int 类型的移位运算与位运算
    二进制、十六进制理解及int类型二进制存储方式
    git bash返回上一级目录
    关于我
    【设计模式】设计原则--面向接口编程你理解的对吗?
    回到未来:Smalltalk 编程系统
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11341657.html
Copyright © 2011-2022 走看看