• Linux编程--进程间通信


    进程间通信

    • 管道
    • 消息队列
    • 信号量
    • 共享内存

    管道

    无名管道 Pipe

    • 同主机进程间数据交互机制:
    • 无名管道(PIPE):只有创建该管道的程序,才能够找到该管道(亲缘进程通信)
    • 单向传输
    • 控制进程只有pipe的一端
    • pipe的阻塞操作
      *fd[0] 读 fd[1]写
    
    /*匿名管道*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main ()
    {
       int fd[2];
       int ret;
       pid_t pid;
    
       if ( pipe(fd) == -1)
       {
       	 perror("pipe error");
       	 return -1;
       }
       
       if ( ( pid = fork() ) == -1)
       {
       	 perror("fork error");
       	 return -1;
       }
       
       if (pid > 0) //父进程
       {
         close(fd[0]); //关闭读端
         char * str = "hello";
         write(fd[1],str,strlen(str)+1);
         close(fd[1]);
         wait(NULL);
       }
       else if (pid == 0) //子进程
       {
         close(fd[1]);//关闭写端
         char str[64] = {0};
         ret = read(fd[0],str,sizeof(str));
         write(STDOUT_FILENO,str,ret);
         close(fd[0]);
       }
       
      return 0;
    }
    

    有名管道 fifo

    • 依赖于文件系统,像普通文件一样具有磁盘路径,文件权限和其他属性,所有程序都可以通过path找到有名管道
    • fifo是一个文件,存储信息在内存,当两个进程都消失,数据消息,文件只有接口的作用
    • mkfifo
      int mkfifo(const char* pathname, mode_t mode);
    • 单项通信
    • 只能够进行少量数据传输
    • 只能一对一进行传输
    • 两个进程,中途退出了一个进程,未退出的一端如果是写操作的话,返回sigpipe信号
    • 未退出的一端如果是阻塞读操作的话,该读操作不会继续阻塞,直接返回0
    fifo_w.c
    /*命名管道*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    int main ()
    {
       int key,ret;
       int fd;
       
        //判断文件是否存在
        ret = access("fifo.tmp",F_OK);
        if (ret == -1)  //文件不存在,则创建
        {
          if (mkfifo("fifo.tmp",0664) == -1)
          {
            perror("mkfifo error");
            exit(1);
          }
          else
          {
            printf("mkfifo sucess!
    "); 
          }
        }
    
        fd = open("fifo.tmp",O_WRONLY);
        while(1){
           char *pt = "hello world !";
           write (fd , pt ,strlen(pt)+1);
           sleep(1);
        }
       
       close(fd);
      return 0;
    }
    
    
    fifo_r.c
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    
    int main(int argc, char const *argv[])
    {
    	
    	int fd ;
    	int ret ;
        
        if ( access("fifo.tmp",F_OK) == -1)
        {
           printf("no exit file
    ");
           exit(1);
        }
    
        fd = open("fifo.tmp",O_RDONLY);
        if(fd == -1)
        {
        	perror ("open error");
        	return 1;
        }
        while(1)
        {
          char buf[64] = {0};
          read(fd,buf,sizeof(buf));
          printf("%s
    ", buf);
        }
    
        close(fd);
    
    	return 0;
    }
    

    消息队列

    • 创建消息对象:
      int msgget(key_t key, int msgflg);

    • 设置对象:
      int msgctl(int msqid,int cmd,struct msqid_ds *buf);
      cmd:
      IPC_RMID 删除
      IPC_SET 设置ipc_perm参数
      IPC_STAT 获取ipc_perm参数
      IPC_INFO 获取ipc信息 和ipcs

    • 使用对象:

      1. 发送
        int msgsnd(int msqid,const void *msg_ptr,size_t sz,int msgflg);
      2. 接收
        ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
    • 消息的数据结构,数据前有个消息类型

    • struct msgbuf
      {
      long mtype; /* message type, must be > 0 /
      char mtext[1]; /
      message data */
      };

    msq_w.c
    /*消息队列*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/msg.h>
    #define MAX_TEXT 1024
     
    //消息结构体
    struct my_msg{
      long int my_msg_type;//消息类型
      char buf[MAX_TEXT];//消息数据
    };
    
    int main ()
    {
        int msqid;
        struct my_msg data;
       
        msqid = msgget((key_t)12345,0664|IPC_CREAT) ;//创建消息队列
       if (msqid == -1)
       {
         perror("msgget error");
         exit(-1);
       }
        
        int i = 0;
        for (i = 0;i < 5;++i) //发五条消息
        {
            printf("please input: ");
            fgets(data.buf,BUFSIZ,stdin);
            data.my_msg_type = i+1;
           //发送消息
           if ( msgsnd(msqid,(void *)&data,sizeof(data.buf),0) == -1)
           {
             perror("msgsnd error");
             exit(-1);
           }
        }
    
      return 0;
    }
    
    
     msq_r.c
    /*消息队列*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/msg.h>
    #define MAX_TEXT 1024
    
    struct my_msg
    {
      
      long int my_msg_type;
      char buf[MAX_TEXT];
    };
    
    int main ()
    {
       
       int msqid;
       struct my_msg data;
       
       msqid = msgget( (key_t)12345,0664|IPC_CREAT);//获取消息队列
       if (msqid == -1)
       {
         perror("msgget error");
         exit(-1);
       }
        
        int i = 0;
        for (i = 0;i < 5;++i) //收五条消息
        {
    
           //发送消息
           if ( msgrcv(msqid,(void *)&data,BUFSIZ,i+1,0) == -1)
           {
             perror("msgrcv error");
             exit(-1);
           }
           //打印
           printf("rcv:%ld
    ", data.my_msg_type);
           printf("rcv:%s
    ", data.buf);
        }
    
        //删除消息队列
        if (msgctl(msqid,IPC_RMID,NULL) == -1)
        {
             perror("msgctl error");
             exit(-1);
        }
    
      return 0;
    }
    

    信号量

    • 实现进程间同步
    • 标识系统可用资源的个数

    对象:

    struct semid_ds {
    	struct ipc_perm sem_perm;  /* Ownership and permissions */
    	time_t          sem_otime; /* Last semop time */
    	time_t          sem_ctime; /* Last change time */
    	unsigned long   sem_nsems; /* No. of semaphores in set */信号量个数
    	struct   sem *	sem_base 
    };
    
    struct sem{
    	int semval  信号量的值
    	int sempid  最近一个操作的进程号
    }
    

    对象操作

    • 创建对象:
      int semget(key_t k,int n,int semflag);//key 个数 权限

    • 设置对象:
      int semctl(int sid,int semnum,int cmd,union semun a);
      union semun {
      int val; /* Value for SETVAL */
      struct semid_ds buf; / Buffer for IPC_STAT, IPC_SET */
      unsigned short array; / Array for GETALL, SETALL */
      struct seminfo __buf; / Buffer for IPC_INFO
      (Linux-specific) */
      };

    int semop(int s,struct sembuf *sb,size _t n);//操作信号量
    
    struct sembuf
    {
     unsigned short int sem_num; //信号量的下标 
     short int sem_op;   	// 操作码 正数 表示增加信号的值
     short int sem_flg;
    };
    
    

    sem_flg 操作标识 有两个取值IPC_NOWAIT 和 SEM_UNDO

    IPC_NOWAIT 如果操作信号集合中任意一个失败,立即返回,并且不会对其他的信号量做操作,所以的信号量都不被操作

    SEM_UNDO :进程退出后,该进程对sem进行的操作将被撤销

    sem_w.c
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <errno.h>
    
    //自定义联合体
    union semu{
     int val ;
     struct semid_ds * buf;
     unsigned short * array;
     struct seminfo * _buf;
    };
    
    static int semid;
    
    void init()
    {
       union semu sem_u;
     
       key_t key;
       key = ftok(".",0);
       short arr[2];
       
       arr[0] = 0;   //生产出的产品数
       arr[1] = 100; //仓库剩余容量
       semid = semget(key,2,0664 | IPC_CREAT);
       if (semid == -1)
       {
       	 perror("semget error");
       	 exit(-1);
       }
       sem_u.array = arr;
       if (semctl(semid,0,SETALL,sem_u) == -1 )
       {
       	perror("semctl error");
       	exit(-1);
       }
    }
    
     // p 操作 -1
     int op_p(int num)
     {
       struct sembuf sem_p;
       sem_p.sem_num = num;
       sem_p.sem_op = -1;
       sem_p.sem_flg = 0;
       if (semop(semid,(struct sembuf *)&sem_p,1) == -1)
         return 0;
         
         return 1;
     }
    
     // v 操作 +1
     int op_v(int num)
     {
       struct sembuf sem_v;
       sem_v.sem_num = num;
       sem_v.sem_op = 1;
       sem_v.sem_flg = 0;
       if (semop(semid,(struct sembuf *)&sem_v,1) == -1)
       	 return 0;
    
       	return 1;
     } 
    
    int main ()
    {
    
       init();
       
      while(1)
      {
      	printf("before
    ");
      	  printf("productor number is %d
    ",semctl(semid,0,GETVAL));
    	 printf("space number is %d
    ",semctl(semid,1,GETVAL)); 
         op_p(1);//写一个,那么仓库容量减1 (如果容量不足将阻塞等待)
         printf("生产了一件商品
    ");
         op_v(0);//产品数目增加1  (即通知消费者可以消耗商品)
         printf("before
    ");
      	  printf("productor number is %d
    ",semctl(semid,0,GETVAL));
    	 printf("space number is %d
    ",semctl(semid,1,GETVAL)); 
         sleep(4);
      }
    
     //删除信号量
       semctl(semid,IPC_RMID,0);
     return 0;
    }
    
    
    sem_r.c
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <errno.h>
    
    
    static int semid;
    
    void init()
    {
       
     
       key_t key;
       key = ftok(".",0);
     
       semid = semget(key,2,0664 | IPC_CREAT);
       if (semid == -1)
       {
       	 perror("semget error");
       	 exit(-1);
       }
       
    }
    
     // p 操作 -1
     int op_p(int num)
     {
       struct sembuf sem_p;
       sem_p.sem_num = num;
       sem_p.sem_op = -1;
       sem_p.sem_flg = 0;
       if (semop(semid,(struct sembuf *)&sem_p,1) == -1)
         return 0;
         
         return 1;
     }
    
     // v 操作 +1
     int op_v(int num)
     {
       struct sembuf sem_v;
       sem_v.sem_num = num;
       sem_v.sem_op = 1;
       sem_v.sem_flg = 0;
       if (semop(semid,(struct sembuf *)&sem_v,1) == -1)
       	 return 0;
    
       	return 1;
     } 
    
    int main ()
    {
       init();
       
      while(1)
      {   printf("before
    ");
      	  printf("productor number is %d
    ",semctl(semid,0,GETVAL));
    	  printf("space number is %d
    ",semctl(semid,1,GETVAL)); 
      	 //编号 0 是商品数目 1 是库存
         op_p(0);//消费一个,那么商品数目减一(如果商品数不足将阻塞等待)
         printf("消费了一件商品
    ");
         op_v(1);//库存容量增加1  
         printf("after 
    "); 
         printf("productor number is %d
    ",semctl(semid,0,GETVAL));
    	 printf("space number is %d
    ",semctl(semid,1,GETVAL)); 
         sleep(3);
      }
       
       
     return 0;
    }
    
    

    共享内存 (内存映射的方式)

    • 数据量大
    • 传输最快
    • nmap

    共享内存对象

    struct shmid_ds

    struct shmid_ds {
    struct ipc_perm		shm_perm;	/* operation perms */
    int	  shm_segsz;	/* size of segment (bytes) */
    __kernel_time_t		shm_atime;	/* last attach time */
    __kernel_time_t		shm_dtime;	/* last detach time */
    __kernel_time_t		shm_ctime;	/* last change time */
    __kernel_ipc_pid_t	shm_cpid;	/* pid of creator */
    __kernel_ipc_pid_t	shm_lpid;	/* pid of last operator */
    unsigned short		shm_nattch;	/* no. of current attaches */
    unsigned short 		shm_unused;	/* compatibility */
    void 			*shm_unused2;	/* ditto - used by DIPC */
    void			*shm_unused3;	/* unused */
    

    };

    创建共享内存

    int shmget (key_t key, size_t size, int shmflg) ;

    设置共享内存

    int shmctl (int shmid, int cmd, struct shmid_ds *buf) ;

    特殊 cmd:
    SHM_INFO
    SHM_STAT
    SHM_LOCK
    SHM_UNLOCK

    使用对象

    void *shmat (int shmid, const void *shmaddr, int shmflg);//将进程与共享内存绑定

    int shmdt (const void * shmaddr); //解除绑定

    // shmaddr 填NULL ,由内核帮助你来分配

    • smflg:

    SHM_RDONLY //只读

    SHM_REMAP //重新映射

    SHM_EXEC //可读可写

    0 //可读可写

    注意

    1.父子进程的共享内存约定

    fork函数 子进程会继承共享内存

    exec执行一个新的程序 自动卸载

    共享内存用完了记得shmdt();

    信号量 和 共享内存的连用,信号通知,然后从共享内存中读

    shm_w.c
    
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <errno.h>
    #define SEGSIZE 680
    /*对于共享内存,获得内存地址后,更具你要传递的数据的类型
    该指针进行类型转换即可,这样很方便完成数据的传递,非常不错的IPC方式*/
    typedef struct data
    {
      char name[64];
      int age ;
    }Sdat;
    
    int main(int argc, char const *argv[])
    {
      
        key_t key;
        int shm_id;
        Sdat * smap = NULL;
        key = ftok(".",2);
       //创建共享内存
       shm_id = shmget(key,SEGSIZE,IPC_CREAT | 0664);
       if (shm_id == -1)
       {
        perror("shmget error");
        return 1;
       }
        //将进程与共享内存绑定,映射
          smap =(struct data*)shmat(shm_id,NULL,0);
         char buf[64] = "john";
         int i = 0;
         for (i = 0;i < 10 ; ++i) //往共享内存中写东西
         {
           strcpy((smap+i)->name,buf);
           (smap+i)->age = i*10;
         }
    
         if (shmdt(smap) == -1)
         {
           perror("shmdt error");
           return -1;
         }
    
      return 0;
    }
    
    shm_r.c
    
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <errno.h>
    #define SEGSIZE 680  //刚好10个结构体大小
    /*对于共享内存,获得内存地址后,更具你要传递的数据的类型
    该指针进行类型转换即可,这样很方便完成数据的传递,非常不错的IPC方式*/
    typedef struct data
    {
      char name[64];
      int age ;
    }Sdat;
    
    int main(int argc, char const *argv[])
    {
      
        key_t key;
        int shm_id;
        Sdat * smap = NULL;
        key = ftok(".",2);
       //创建共享内存
       shm_id = shmget(key,SEGSIZE,IPC_CREAT | 0664);
       if (shm_id == -1)
       {
        perror("shmget error");
        return 1;
       }
        //将进程与共享内存绑定,映射
          smap =(struct data*)shmat(shm_id,NULL,0);
          
         int i = 0;
         for (i = 0;i < 10 ; ++i) //从共享内存中读东西
         {
           printf("%s
    ",(smap+i)->name );
           printf("%d
    ",(smap+i)->age );
         }
    
         if (shmdt(smap) == -1)
         {
           perror("shmdt error");
           return -1;
         }
         
         if ( -1== shmctl(shm_id,IPC_RMID,0) )
         {
         	perror("shmctl error");
         	return -1;
         }
    
      return 0;
    }
    

    信号和共享内存的混合使用

    sem_shm_w.c
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <string.h>
    
    
    int main(int argc, char const *argv[])
    {
    	int running = 1;
        key_t key;
        int sem_id;
        int shm_id;
        void * nmap;
        struct sembuf sem_u;
        sem_u.sem_num = 0;
        sem_u.sem_flg = SEM_UNDO;
    
        key = ftok(".",3);
        
       if (sem_id = semget(key,1,IPC_CREAT | 0664) ==-1)
       {
       	perror("semget error");
       	return -1;
       }
    
       if (semctl(sem_id,0,SETVAL,0) == -1)
       {
       	 printf("sem init error");
       	 return -1;
       }
       
        shm_id = shmget(key,(size_t)4096,IPC_CREAT | 0664);
        if (shm_id == -1)
        {
        	 printf("shm init error");
       	      return -1;
        }
      
        if ( (nmap = shmat(shm_id,NULL,0) )== NULL)
        {
        	 printf("shmat error");
       	     return -1;
        }
    
       while(running)
        {
            if (semctl(sem_id,0,GETVAL)==0) //可以写数据
            {
               printf("please input something:");
               scanf("%s",(char *)nmap);
               sem_u.sem_op = 1;
               if (semop(sem_id,&sem_u,1)==-1)
               {
               	 printf("semop error");
       	          return -1;
               }
            }
            if (strcmp(nmap,"end")==0)
              running--;
        }
    
       shmdt(nmap);
    	return 0;
    }
    
    sem_shm_r.c
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/shm.h>
    #include <sys/ipc.h>
    #include <sys/sem.h>
    #include <string.h>
    
    
    int main(int argc, char const *argv[])
    {
    	int running = 1;
        key_t key;
        int sem_id;
        int shm_id;
        void * nmap;
        struct sembuf sem_u;
        sem_u.sem_num = 0;
        sem_u.sem_flg = SEM_UNDO;
    
        key = ftok(".",3);
        
       if (sem_id = semget(key,1,IPC_CREAT | 0664) ==-1)
       {
       	perror("semget error");
       	return -1;
       }
    
      
       
        shm_id = shmget(key,(size_t)4096,IPC_CREAT | 0664);
        if (shm_id == -1)
        {
        	 printf("shm init error");
       	      return -1;
        }
      
        if ( (nmap = shmat(shm_id,NULL,0) )== NULL)
        {
        	 printf("shmat error");
       	     return -1;
        }
    
       while(running)
        {
            if (semctl(sem_id,0,GETVAL)==1) //可以读数据
            {
               printf("recv:%s
    ",(char*)nmap);
               sem_u.sem_op = -1;
               if (semop(sem_id,&sem_u,1)==-1)
               {
               	 printf("semop error");
       	          return -1;
               }
            }
            if (strcmp(nmap,"end")==0)
              running--;
        }
    
       shmdt(nmap);
     
        if (shmctl(shm_id,IPC_RMID,0)==-1)
        {
            printf("shm_id error");
            return -1;
        }
    
        if (semctl(sem_id,0,IPC_RMID,0)==-1)
        {
            printf("sem_id error");
            return -1;
        }
    	 return 0;
    }
    
    
  • 相关阅读:
    PHP中字符串和正则表达式的常用函数
    PHP基础语法
    vscode新手心得
    socket数据通讯
    PHP 登录类
    PHP Session 封装类
    php 邮箱验证函数
    PHP MySql 分页实例
    Bootstrap
    projact mail
  • 原文地址:https://www.cnblogs.com/yuluoluo/p/9084533.html
Copyright © 2020-2023  润新知