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!\n"); 
      }
    }

    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\n");
       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\n", 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\n", data.my_msg_type);
       printf("rcv:%s\n", 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\n");
      printf("productor number is %d\n",semctl(semid,0,GETVAL));
     printf("space number is %d\n",semctl(semid,1,GETVAL)); 
     op_p(1);//写一个,那么仓库容量减1 (如果容量不足将阻塞等待)
     printf("生产了一件商品\n");
     op_v(0);//产品数目增加1  (即通知消费者可以消耗商品)
     printf("before\n");
      printf("productor number is %d\n",semctl(semid,0,GETVAL));
     printf("space number is %d\n",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\n");
      printf("productor number is %d\n",semctl(semid,0,GETVAL));
      printf("space number is %d\n",semctl(semid,1,GETVAL)); 
     //编号 0 是商品数目 1 是库存
     op_p(0);//消费一个,那么商品数目减一(如果商品数不足将阻塞等待)
     printf("消费了一件商品\n");
     op_v(1);//库存容量增加1  
     printf("after \n"); 
     printf("productor number is %d\n",semctl(semid,0,GETVAL));
     printf("space number is %d\n",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\n",(smap+i)->name );
       printf("%d\n",(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;
}

猜你喜欢

转载自www.cnblogs.com/yuluoluo/p/9084533.html