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;

}

原文地址:https://www.cnblogs.com/zjlbk/p/11341657.html