【Linux】Linux进程通信——管道通信、信号通信、IPC通信

进程基础知识

 首先关于进程需要知道的是:

  1. 进程间的通信(信息的交互)必须经过内核
  2. 进程通信是基于文件IO的思想,就像是驱动开发的时候对文件的操作一样,有打开、读写、关闭一类的操作(只不过个别函数不一样)
    写操作只能是char型的
  3. 我们平时写代码时的main(),就是创建了一个主进程,在这个主进程中用fork()函数可以创建这个主进程的子进程,即这两个进程为父子进程
  4. Linux中查看进程
    ps -axj	//-a 选项表示显示所有用户的进程,而不仅仅是当前用户的进程。
       		//-x 选项表示显示没有终端控制的进程。这包括后台进程和守护进程。
       		//-j 选项用于以作业格式(包括pid等信息)显示进程信息。
    
  5. Linux中进程状态:
    • S+为睡眠,T+为暂停,Z+为僵死进程
    • 回收子进程用wait,暂停进程用raise,睡眠用sleep
  6. 父子进程关系
    • fork()函数创建子进程,返回值类型为pid_t p;p<0创建失败;p>0说明当前运行为父进程;p=0为子进程

      #include<stdio.h>
      #include<sys/types.h>
      #include<unistd.h>
      int main(){
          pid_t p1;
          p1=fork();
          if(p1<0)
              printf("p1 fork error!\r\n");
          else
          {
              sleep(3);
              printf("%d\r\n",p1);
          }
      
          return 0;
      }
      	
      
    • 实现父进程创建一个子进程1,子进程1再创建子进程2;且父进程打印b,子进程1打印c,子进程2打印a

      #include<stdio.h>
      #include<sys/types.h>
      #include<unistd.h>
      int main(){
          pid_t p1,p2;
          if(p1=fork())  //只有父进程到这里的时候,才会执行(子进程返回值为0)
              putchar('b');
          else        //子进程1执行
          {
              if(p2=fork())   //创建子进程2
                  putchar('c');
              else    //子进程2执行
              {
                  putchar('a'); 
              }
          }
          return 0;
      }
      
      
    • 实现父进程先运行,运行完后子进程再运行

      #include<stdio.h>
      #include<sys/types.h>
      #include<stdlib.h>
      #include <unistd.h>
      int main(){
          pid_t p1,p2;
          int process_inter=0;
          p1=fork();
          if(p1==0)   //子进程
          {
              while (process_inter==0); //为0等待
              for(int i=0;i<5;i++)
              {
                  printf("this is child process i=%d\n",i);
                  usleep(100);
              }
          }
          if(p1>0)          //父进程
          {
              for(int i=0;i<5;i++)
              {
                  printf("this is parent process i=%d\n",i);
                  usleep(100);
              }
              process_inter=1;
          }
          return 0;
      }
      
      
      

进程通信

  我看到关于进程通信分类,其实按照不同的划分方式有很多种不一样的划分,但是这个不用纠结太深,具体的通信方法就那么几种。这里我主要分为管道通信、信号通信、IPC通信
  所有不同的通信方式,其实都是一样的,都是在内核中有一个对象,两个进程的通信必须通过这个对象,只不过这个对象有时是管道(管道通信),有时是信号(信号通信)或者内存(共享内存通信)等,不同对象的操作函数不一样罢了

管道通信

 就是在内核中创建了一个对象,只不过这个对象是管道,进程a要给b发送数据,必须要先a——管道——b

无名管道

  1. 无名管道就是无文件节点,我理解的就是相当于没有文件实体,文件肯定有文件名等其他文件属性,但是无名管道就没有文件名这些。所以就不能通过文件名来操作无名管道
  2. 缺点:不能实现非父子进程(亲缘关系)之间的通信
  3. 代码实现
    • pipe()函数创建无名管道,创建成功返回0,失败返回-1
      参数:就是文件描述符。fd[1]是出队的文件描述符,fd[0]是入队(队列的左右两边)

      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      //1. pipe创建管道
      int main(){
          int fd[2];
          int ret;
          ret=pipe(fd);
          if(ret<0)
          {
              printf("pipe creat failed!\n");
              return -1;
          }
          else
          {
              printf("pipe creat success!\n");
              printf("fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
          }
          
          return 0;
      }
      
      
    • 进程向无名管道写数据,再从管道中读数据
      写要写从队列的出队描述符fd[1]开始写,读要从队列的入队描述符fd[0]开始读

      #include <unistd.h>
      #include <stdio.h>
      #include <stdlib.h>
      int main(){
          int fd[2];
          int ret;
          char writebuf[]="hello linux!";
          char readbuf[50]={0};
          char readbuf2[50]={0};
          ret=pipe(fd);
          if(ret<0)
          {
              printf("pipe creat failed!\n");
              return -1;
          }
          else
          {
              printf("pipe creat success!\n");
              printf("fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
          }
          ret=write(fd[1],writebuf,sizeof(writebuf));
          if(ret<0)
          {
              printf("pipe write failed!\n");
              return -1;
          }
          ret=read(fd[0],readbuf,50);
          if(ret<0)
          {
              printf("pipe read failed!\n");
              return -1;
          }
          printf("pipe readbuf=%s\n",readbuf);
          //验证读阻塞
          // ret=read(fd[0],readbuf2,50);
          // printf("pipe readbuf2=%s\n",readbuf2);
          close(fd[0]);
          close(fd[1]);
          return 0;
      }
      
    • 利用无名管道实现子进程等待,父进程先运行后子进程运行

      #include<stdio.h>
      #include<sys/types.h>
      #include<stdlib.h>
      #include <unistd.h>
      int main(){
          pid_t p1;
          int fd[2];
          int ret;
          char  process_inter=0;      
          //必须在创建进程前创建管道,这样两个进程才继承的同一个管道。如果先创建进程后创建管道,那么父子进程会分别创建自己一个管道
          ret=pipe(fd);
          if(ret<0)
          {
              printf("pipe creat failed!\n");
              return -1;
          }
          printf("pipe creat success!\n"); 
      
          p1=fork();
          if(p1==0)   //子进程
          {
              read(fd[0],&process_inter,1); //数组名就是指向首元素地址的指针,就不用加&
              while (process_inter==0); //为0等待
              for(int i=0;i<5;i++)
              {
                  printf("this is child process i=%d\n",i);
                  usleep(100);
              }
          }
          if(p1>0)          //父进程
          {
              for(int i=0;i<5;i++)
              {
                  printf("this is parent process i=%d\n",i);
                  usleep(100);
              }
              process_inter=1;
              write(fd[1],&process_inter,1);
          }
          while (1);
          return 0;
      }
      

有名管道

  1. 有文件节点,属于Linux七种文件类型中的管道文件p,不占用磁盘空间。也就是创建的管道文件是有实体的,能在系统中找到
    在这里插入图片描述

  2. 能实现非父子进程(亲缘关系)之间的通信

  3. 有名管道文件操作是典型的文件操作,有mkfifo,open、读写、关闭

  4. 代码实现

    • mkfifo()函数创建有名管道文件,创建成功返回0,失败返回-1
      参数:filename:文件路径和文件名,mode:文件权限,跟umask有关

      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      #include <sys/stat.h>
      //创建管道文件
      int main()
      {
          int ret;
         /*int mkfifo(const char * filename,mode_ t mode);*/
          ret=mkfifo("./myfifo",0777);    //在当前目录下创建一个管道文件
          if(ret<0)
          {
              printf("mkfifo failed!\n");
              return -1;
          }
          printf("mkfifo success!\n");
          return 0;
      }
      
    • 实现两个非亲缘关系的进程之间的通信。要求进程1先运行完后,进程2运行

      • 进程1代码:

        #include "unistd.h"
        #include "stdio.h"
        #include "sys/types.h"       // 提供mode_t类型  
        #include "stdlib.h" 
        #include "fcntl.h"          // 提供open()函数             
        #include "sys/stat.h"      // 提供open()函数的符号  
        int main()
        {
            int ret,fd;
            char inter=0;
            ret=mkfifo("./myfifo",0777);
            if(ret<0)
            {
                printf("mkfifo failed!\n");
                return -1;
            }
            printf("mkfifo success!\n");
        
            fd=open("./myfifo",O_WRONLY);
            if(fd<0)
            {
                printf("open failed!\n");
                return -1;
            }
            printf("open success!\n");
        
            for(int i=0;i<5;i++)
            {
                printf("this is first process i=%d\n",i);
                usleep(200);
            }
            inter=1;
            sleep(5);
            write(fd,&inter,1);
            
            while (1);
            return 0;
        }
        
      • 进程2代码:

        #include "unistd.h"
        #include "stdio.h"
        #include "sys/types.h"      
        #include "stdlib.h" 
        #include <fcntl.h>                               
        #include <sys/stat.h>                  
        int main()
        {
            int fd;
            char inter;
            fd=open("./myfifo",O_RDONLY);
            if(fd<0)
            {
                printf("second_process open failed!\n");
                return -1;
            }
            printf("first_process open success!\n");
            
            read(fd,&inter,1);
            while(inter==0);
            for(int i=0;i<5;i++)
            {
                printf("this is second process i=%d\n",i);
                usleep(100);
            }
        
            while(1);
            return 0;
        }
        

信号通信

  1. 就是内核中用于两个进程通信的对象是信号,信号是存在于内核中的,不像管道那样需要单独去创建

    • 查看Linux中64种信号的命令:kill -l
      在这里插入图片描述
      常用的命令如:
      1. 9 SIGKILL #杀死进程
      2. 2 SIGINT #键盘发送的终止信号(用pause时)
      3. 10 SIGUSR1 #用户自定义信号 默认处理:进程终止
      4. 14 SIGALRM #定时器到时后终止进程
      5. 17 SIGCHLD # 忽略
      6. 20 SIGTSTP #使进程暂停
  2. 他的典型使用就是一些封装好的命令,如

    • 发送信号函数:kill、raise、alarm

      • kill pid 9——杀死进程号为pid的进程
      • raise(SIGTSTP);——raise只能给当前进程发送信号
      • alarm(8);——alarm只能给当前进程发送SIGALRM信号,所以只用指定秒数
    • 接收信号函数:pause、sleep;

    • 信号处理函数:signal。前面的都是内核自动处理接收到的信号,而signal函数可以自定义内核如何处理接收到的信号

      #include "stdio.h" 
      #include "sys/types.h"
      #include "signal.h"
      #include "stdio.h"
      #include "stdlib.h"
      #include <unistd.h>
      void myfunc(int signum)
      {
          for(int j=0;j<5;j++)
          {
              printf("Process processing signal,signum=%d\n",signum);
          }
          return;         //返回main函数
      }
      int main()
      {
          signal(14,myfunc);
          printf("alarm signal before\n");
          alarm(8);;
          printf("alarm signal after\n");
          for(int i=0;i<15;i++)
          {
              printf("process running,i=%d\n",i);
              sleep(1);
          }
          return 0;
      }
      /*#include <signal.h>
      void (*signal(int signum, void(*handler)(int)))(int);
      A=void(*handler)(int)):函数指针,指向了有一个int型的参数,无返回值的函数
        	void (*signal(int signum, A)(int);
        	所以,signal函数是,参数有两个,其中一个是函数指针,返回值是函数指针
       参数一:signum :处理哪个信号
       参数二:如何处理
       	SIG_IGN:忽略该信号
      	SIG_DFL:采用系统默认方式处理信号
      	自定义
        */
      
      

IPC通信

  1. 删除ipc对象
    ipcrm -m id	//删除共享内存
    ipcrm -q id	//删除消息队列
    ipcrm -s id	//删除信号灯
    
  2. 查看ipc对象
    ipcs -m id	//查看共享内存
    ipcs -q id	//查看消息队列,字节数会包括换行
    ipcs -s id	//查看信号灯
    

共享内存

  1. 共享内存(shared memory)也是在内核中存在的对象,它不会像管道一样读完就删除内容
  2. 重要函数
    • shmget()函数创建共享内存
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      int shmget(key_ t key, int size, int shmflg);
      /*key: 
      	IPC_PRIVATE:key值为0,只能实现亲缘关系进程间的通信
      	ftok函数创建key值:不为0,能实现无亲缘关系进程间的通信
        size:共享内存区大小
        shmflg:权限,也可以用8进制表示法
        成功:返回一个整型shmid,是共享内存的段标识符*/
      
      
      key_t ftok(const char *pathname, int proj_id)
      /*pathname:指定的文件,此文件必须存在且可存取
        proj_id:键值
        成功:返回key值
      */
      
    • 如果用read/write的话,每次都要进入内核,麻烦,所以系统提供了shmat函数,用于内存映射将内存中的共享内存映射到用户空间
      父子进程都要映射
      void *shmat (int shmid, const void *shmaddr, int shmflg);
      /*第一个参数: ID号;
        第二个参数:映射到的地址,NULL为系统自动完成的映射;
        第三个参数shmflg:
      	SHM_ RDONLY共享内存只读
      	默认是0,表示共享内存可读写
        返回值:成功:映射后的地址;
      		失败: NULL.
      */
      
    • 映射到用户空间的内存和内核空间的内存都要释放
      int shmdt(const void *shmaddr);
      /*shmaddr;共享内存映射后的地址
        返回值:成功: 0
      		出错: -1
      */
      int shmctl(int shmid, int cmd,struct shmid ds *buf);
      /*进入内核
        shmid: 要操作的共享内存标识符(shmget的返回值)	
        cmd: IPC_STAT (获取对象属性)——实现了命令ipcs -m
      	   IPC_SET (设置对象属性)
      	   IPC_RMID (删除对象)——实现了命令ipcrm -m id
        buf :指定IPC_STAT/IPC_SET时用以保存/设置属性;IPC_RMID时为NULL
        函数返回值:成功: 0
        	       出错: -1
      */
      
  3. 代码实现
    • shmget创建共享内存:
      • 创建key值为0,只能实现亲缘关系进程通信的共享内存
        #include <sys/ipc.h>
        #include <sys/shm.h>
        #include "stdio.h"
        //1. shmget创建ipc对象——共享内存;
        int main()
        {
            int shmid;
            shmid=shmget(IPC_PRIVATE,128,0777);    
            printf("shmid=%d\n",shmid);
            return 0;
        }
        
      • 创建key值不为0,能实现无亲缘关系进程通信的共享内存
        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/shm.h>
        #include "stdio.h"
        //2. fork创建ipc对象——共享内存;
        int main()
        {
            int shmid;
            int key;
            key=ftok("./shmget.c",'a');  //创建键值a,用于区分不同的共享内存。不同的共享内存建议创建不同的键值用以区分
            printf("key=%d\n",key);
            shmid=shmget(key,128,0777);   
            printf("shmid=%d\n",shmid);
            return 0;
        }
        
    • shmat内存映射
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      #include "stdio.h"
      int main()
      {
          char *p;    //映射后的地址
          int shmid;
          
          shmid=shmget(IPC_PRIVATE,128,0777);    
          printf("shmid=%d\n",shmid);
      
          p=(char *)shmat(shmid,NULL,0);        //是void*类型,强制转换为char*类型
          printf("p=%s\n",p);     //空的
          //fgets原型fgets (char *__restrict __s, int __n, FILE *__restrict __stream)
          fgets(p,128,stdin);     //键盘输入
           //从内存读
          printf("p=%s\n",p);
          printf("p=%s\n",p);     //读完之后再读
          return 0;
      }
      
    • shmdt、shmctl释放内存
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      #include "stdio.h"
      int main()
      {
          char *p;    //映射后的地址
          int shmid;
          
          shmid=shmget(IPC_PRIVATE,128,0777);    
          printf("shmid=%d\n",shmid);
      
          p=(char *)shmat(shmid,NULL,0);        //是void*类型,强制转换为char*类型
          printf("p=%s\n",p);     //空的
          //向内存写
          fgets(p,128,stdin);     //键盘输入
           //从内存读
          printf("p=%s\n",p);
          
          shmdt(p);
      
          shmctl(shmid,IPC_RMID,NULL);
          return 0;
      }
      
    • 利用共享内存实现父子进程通信,要求:父进程向内存写一次数据,子进程就从中读一次
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/shm.h>
      #include "stdio.h"
      #include <unistd.h>
      #include  <string.h>
      #include <signal.h>	
      void myfunc(int signum)
      {
          return;
      }
      int main()
      {
          pid_t pid;
          int shmid;
          char *p;
          shmid=shmget(IPC_PRIVATE,128,0777);
          if(shmid<0)
          {
              printf("Failed to create shared memory!\n");
              return -1;
          }
          printf("shared memory's shmid=%d!\n",shmid);
      
          pid=fork();
          if(pid<0)
          {
              printf("Failed to create child process!\n");
              return -1;
          }
      
          if(pid>0)  //父进程
          {
              signal(SIGUSR2,myfunc);
              p=(char *)shmat(shmid,NULL,0);      //映射
              while (1)
              {
                  printf("Please input :");
                  fgets(p,128,stdin);
                  kill(pid,SIGUSR1);          //给子进程发送SIGUSR1信号,开始读
                  pause(); 
              }
              
          }
          if(pid==0)  //子进程
          {
              p=(char *)shmat(shmid,NULL,0);      //子进程也要映射!!!
              signal(SIGUSR1,myfunc);
              while (1)
              {
                 pause();    //睡眠
                 printf("shared memory data is:%s",p);
                 kill(getppid(),SIGUSR2);          //给父进程发送SIGUSR2信号,读完
              }
          }
          shmdt(p);
          shmctl(shmid, IPC_RMID, NULL);
          return 0;
      }
      
    • 利用共享内存实现非父子进程通信。要求:进程2写一次,进程1就读一次
      • 进程1代码

        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/shm.h>
        #include "stdio.h"
        #include <unistd.h>
        #include  <string.h>
        #include <signal.h>	
        struct mybuf
        {
           int pid;
           char buf[124];
        };
        void myfunc(int signum)
        {
            return;
        }
        int main()
        {
            pid_t pid;
            int shmid,key;
            struct mybuf *p;
            key=ftok("./a.c",'a');
            shmid=shmget(key,128,IPC_CREAT|0777);
            if(shmid<0)
            {
                printf("Failed to create shared memory!\n");
                return -1;
            }
            printf("shared memory's shmid=%d!\n",shmid);
        
            signal(SIGUSR1,myfunc);
            p=(struct mybuf *)shmat(shmid,NULL,0);      //映射
        
            pid=p->pid;     //读服务端写入的pid
            p->pid=getpid();    //进程1的pid写到共享内存
            kill(pid,SIGUSR2);   //给进程2发送SIGUSR2信号,唤醒进程2
             //循环读取
            
            while (1)
            {
                pause();   
                printf("client receive data :%s",p->buf);
                kill(pid,SIGUSR2);          //给服务端进程发送SIGUSR2信号,读完了,你可以写了
            }
                
            
        
            shmdt(p);
            shmctl(shmid, IPC_RMID, NULL);
            return 0;
        }
        
        
      • 进程2代码

        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/shm.h>
        #include "stdio.h"
        #include <unistd.h>
        #include  <string.h>
        #include <signal.h>	
        struct mybuf
        {
           int pid;
           char buf[124];
        };
        void myfunc(int signum)
        {
            return;
        }
        int main()
        {
            pid_t pid;
            int shmid,key;
            struct mybuf *p;
            key=ftok("./a.c",'a');
            shmid=shmget(key,128,IPC_CREAT|0777);
            if(shmid<0)
            {
                printf("Failed to create shared memory!\n");
                return -1;
            }
            printf("shared memory's shmid=%d!\n",shmid);
        
            signal(SIGUSR2,myfunc);
            p=(struct mybuf *)shmat(shmid,NULL,0);      //映射
        
            p->pid=getpid();    //进程2的pid写到共享内存
            pause();        //挂起进程,等待信号唤醒
            pid=p->pid;     //读进程1写入的pid
            while (1)
            {
                printf("Please input :");
                fgets(p->buf,128,stdin);
                kill(pid,SIGUSR1);          //给进程1发送SIGUSR1信号,唤醒进程1
                pause(); 	
            }
                
            
        
            shmdt(p);
            shmctl(shmid, IPC_RMID, NULL);
            return 0;
        }
        

消息队列

  1. 管道队列是顺序队列,消息队列是链式队列

  2. 重要函数

    • msgget创建消息队列(message queue):
      #include <sys/tvpes.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      int msgget(key_t key, int flag);
      /*
      	用法同shmget,但是参数没有size,链式队列不需要设置内存大小
      */
      
    • 关闭消息队列:msgctl,用法同shmctl
    • msgsnd队列插入,相当于write
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      int msgsnd(int msgid, const void *msgp, size t size, int flag);
      /*
      msgid:消息队列的ID
      msgp:指向消息的指针。常用消息结构msgbuf如下:
          struct msgbuf
          {
              long mtype;	 //消息类型
              char mtext[N];	 //消息正文
          };
      size: 发送的消息正文的字节数
      flag:
      	非阻塞方式:IPC_ NOWAIT,消息没有发送完成函数也会立即返回。
      	阻塞方式:0:直到发送完成函数才返回
      */
      
    • msgrvc队列读取,相当于read
  3. 代码实现

    • msgget创建消息队列

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #include "stdio.h"
      #include "stdlib.h"
      int main()
      {
          int msgid;
          msgid= msgget(IPC_PRIVATE, 0777);
          if(msgid<0)
          {
              printf("msgget failed!\n");
              return -1;
          }
          printf("msgid=%d\n",msgid);
          return 0;
      }
      
      
    • msgctl释放消息队列

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #include "stdio.h"
      #include "stdlib.h"
      int main()
      {
          int msgid;
          msgid= msgget(IPC_PRIVATE, 0777);
          if(msgid<0)
          {
              printf("msgget failed!\n");
              return -1;
          }
          printf("msgid=%d\n",msgid);
          system("ipcs -q");
      
          msgctl(msgid,IPC_RMID,NULL);
          system("ipcs -q");
          return 0;
      }
      
      
    • msgsnd发送信息给消息队列

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #include "stdio.h"
      #include "stdlib.h"
      #include <string.h>
      struct msgbuf
      {
          long mtype;
          char mtext[128];
      };
      
      int main()
      {
          int msgid;
          struct msgbuf sendbuf;
          msgid= msgget(IPC_PRIVATE, 0777);   //创建队列
          if(msgid<0)
          {
              printf("msgget failed!\n");
              return -1;
          }
          printf("msgid=%d\n",msgid);
          system("ipcs -q");
          //初始化结构体
          sendbuf.mtype=100;
          printf("Please input message:\n");
          fgets(sendbuf.mtext,128,stdin);
          //发送消息
          msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.mtext),0);
          system("ipcs -q");
          msgctl(msgid,IPC_RMID,NULL);    //删除队列
          return 0;
      }
      
    • msgrvc读取消息队列中的消息

      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/msg.h>
      #include "stdio.h"
      #include "stdlib.h"
      #include <string.h>
      struct msg_sndbuf
      {
          long mtype;
          char mtext[128];
      };
      struct msg_rcvbuf
      {
          long mtype;
          char mtext[128];
      };
      
      int main()
      {
          int msgid,byte_longth;
          struct msg_sndbuf sendbuf;
          struct msg_sndbuf rcvbuf;
          msgid= msgget(IPC_PRIVATE, 0777);   //创建队列
          if(msgid<0)
          {
              printf("msgget failed!\n");
              return -1;
          }
          printf("msgid=%d\n",msgid);
          //初始化结构体
          sendbuf.mtype=111;
          strcpy(sendbuf.mtext, "linux");
          //发送消息
          msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.mtext),0);
          //接收消息
          byte_longth=msgrcv(msgid,(void *)&rcvbuf,128,111,0);
          printf("receive message is:%s\n",rcvbuf.mtext);
          printf("byte_longth is: %d\n",byte_longth);
          msgctl(msgid,IPC_RMID,NULL);    //删除队列
          return 0;
      }
      
      
    • 消息队列实现两个无亲缘关系进程间的通信:要求进程A循环从键盘读入要写到消息队列的内容,进程B读

      • 进程A代码
        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/msg.h>
        #include "stdio.h"
        #include "stdlib.h"
        #include <string.h>
        struct msg_sndbuf
        {
            long mtype;
            char mtext[128];
        };
        
        int main()
        {
            int msgid,byte_longth,key;
            struct msg_sndbuf sendbuf;
            key=ftok("./05_A_write",'a');
            msgid= msgget(key,IPC_CREAT|0777);   //创建队列
            if(msgid<0)
            {
                printf("msgget failed!\n");
                return -1;
            }
            printf("msgid=%d\n",msgid);
            //初始化结构体
            sendbuf.mtype=111;
            while (1)
            {
                
                printf("Please enter the information you want to send:");
                fgets(sendbuf.mtext,128,stdin);
                //发送消息
                msgsnd(msgid,(void*)&sendbuf,strlen(sendbuf.mtext),0);
            }
            
            return 0;
        }
        
      • 进程B代码,要注意的是,进程B也需要进行ftok和msgget操作,并且每一次读取完成后/前需要清除掉上一次读取的内容,否则会影响下一次读取。写操作完成后不用进行清除,因为消息队列读完后内容就没了
        #include <sys/types.h>
        #include <sys/ipc.h>
        #include <sys/msg.h>
        #include "stdio.h"
        #include "stdlib.h"
        #include <string.h>
        struct msg_rcvbuf
        {
            long mtype;
            char mtext[128];
        };
        
        int main()
        {
            int msgid,key;
            struct msg_rcvbuf rcvbuf ;
            key=ftok("./05_A_write",'a');
            msgid= msgget(key,IPC_CREAT|0777);   //创建队列
            if(msgid<0)
            {
                printf("msgget failed!\n");
                return -1;
            }
            while (1)
            {
                memset(rcvbuf.mtext,0,128);	//清除缓存
                //接收消息
                msgrcv(msgid,(void *)&rcvbuf,128,111,0);
                printf("receive message is:%s",rcvbuf.mtext);
            }
        
            return 0;
        }
        
    • 实现两个非亲缘关系进程同时收发的双向通信(利用父子进程分别读写实现)

信号灯

  1. 信号量的集合
  2. 重要函数
    • semget创建信号灯
      #include <sys/types.h>+
      #include <sys/ipc.h>s
      #include <sys/sem.h>p
      int semget(key t key, int nsems, int semflg);
      用法同shmget
      /*key:和信号灯集关联的key值
      nsems:信号灯集中包含的信号灯数目
      semflg:信号灯集的访问权限
      返回值
      	成功:信号灯集ID
      	出错: -1
      */
      
    • semctl删除信号灯
      #include <sys/types.h>v
      #include <sys/ipc.h>s
      #include <sys/sem.h>.
      int semctl ( int semid, int semnum, int cmd, ..union semun arg);
      /*
      semid: 信号灯集ID.
      semnum: 要修改的信号灯编号(此参数在做删除用时随便写)
      emd:
      	GETVAL:获取信号灯的值。
      	SETVAL:设置信号灯的值。
      	IPC_RMID:从系统中删除信号灯集合
      semun arg:不是地址
      返回值:
      	成功: 0
      	出错: -1
      */
      
  3. 代码实现
    • semget创建信号灯、semctl删除信号灯
      #include <sys/types.h>
      #include <sys/ipc.h>
      #include <sys/sem.h>
      #include "stdio.h"
      #include "stdlib.h"
      int main()
      {
          int semid;
          semid=semget(IPC_PRIVATE,3,0777);    
          if(semid<0)
          {
              printf("semget failed!\n");
              return -1;
          }
          printf("semid=%d\n",semid);
          system("ipcs -s");
          semctl(semid,0,IPC_RMID);
          system("ipcs -s");
          return 0;
      }
      
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值