UNIX_C 高级编程<五>

来源:互联网 发布:上海淘宝代理运营 编辑:程序博客网 时间:2024/05/01 23:46

进程间的通信技术

     进程间的通信就是指不同进程之间的信息交互/交换;

    

         a.文件

        b.信号

        c.管道

        d.共享内存

        e.消息队列

        f.信号量集

        g.网络

        

     其中,d、e、f 三种通信方式统称为XSI IPC通信方式

 

 

使用管道实现进程间的通信

 

     管道本质上就是文件,只是该文件比较特殊,叫做管道文件

 

     管道主要分为两大类:

         有名管道:主要用于任意两个进程间的通信

         无名管道:主要用于父子间进程的通信

 

     有名管道实现进程间的通信

 

         mkfifo a.pipe —— 终端下创建管道文件

         echo hello > a.pipe —— 表示写入字符串hello到文件a.pipe,写入阻塞

 

         另起一终端,使用命令cat a.pipe读取管道文件内容,此时可以看到hello,写入端阻塞解除

 

     #include <sys/types.h>

    #include <sys/stat.h>

 

    int mkfifo(const char *pathname, mode_t mode);

 

         参 1:路径名

         参 2:权限

         功能:创建有名管道文件

 

     使用无名管道实现进程间通信

 

     #include <unistd.h>

     int pipe(int pipefd[2]);

 

         功能:主要用于创建单向的数据通道实现进程间的通信,通过参数返回两个文件描述符,

其中pipefd[0]关联管道的读端,pipefd[1]关联管道的写端

        

         注意:管道的特殊性就是在于仅仅作为进程间通信的媒介,而本身并不会存储任何的数据;

               管道是一中半双工的通信方式,也就是古老的通信方式

 

使用共享内存实现进程间的通信

 

     共享内存本质上就是由系统内核维护的一块内存区域,该内存区域可以被映射到两个不同进程的虚拟地址空间中,这两个进程通过都写该内存空间从而实现通信;

             

     共享内存是最快的通信方式;

 

     通信模型

    

     1、获取key值,使用ftok函数

     2、创建/获取共享内存,使用shmget函数

     3、挂接共享内存,使用shmat函数

     4、访问共享内存

     5、脱节共享内存,使用shmdt函数

     6、如果不再使用, 则删除共享内存, shmctl函数

 

    #include <sys/types.h>

    #include <sys/ipc.h>

    #include <sys/shm.h>

 

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

 

         参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件

         参 2:项目的编号,必须是非零低八位二进制位

         返回:成功返回生成key值,失败 —— -1

         功能:主要用于根据参数指定的路径和项目编号来生成key值

         注意:使用相同的路径名和相同的项目编号,生成的key值也相同

 

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

 

         参 1:key值,ftok函数的返回值

         参 2:共享内存的大小

                   0 - 获取已经存在的共享内存

         参 3:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )

 

                   IPE_CREAT —— 不存在则创建,存在则打开

                   IPE_EXCL  —— 与IPC_CREAT搭配使用,存在则创建失败

                       0     ——   获取已经存在的共享内存

         返回:成功返回共享内存ID,失败返回-1

         功能:主要用于创建/获取共享内存

        

     void * pv = shmat(int shmid, const void *shmaddr, int shmflg);

        

         参 1:共享内存的ID, shmget函数的返回值

         参 2:共享内存的地址,用系统选择地址给NULL

         参 3:共享内存的操作标志,默认给0

         返回:成功返回挂接地址,失败返回if( (void *)-1 == pv )

         功能:主要用于将参数shmid指向的共享内存段挂接到当前正在调用进程的地址空间中

 

     int shmdt(const void *shmaddr);

         功能:主要用于脱接参数指定的共享内存从当前的地址空间中,参数一般传shmat函数返回值

 

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

         参 1:共享内存的ID, shmaget函数的返回值

         参 2:具体的操作命令 IPC_RMID

         参 3:结构体指针类型 NULL

         功能:主要用于对指针的共享内存执行指定的操作;

 

         常用的基本命令

             

              ipcs  -m               表示察看系统中已经存在的共享内存

              ipcrm -m  内存id号    共享内存的ID表示删除指定的共享内存


 1 //使用共享内存使用进程间的通信 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/shm.h> 8 #include <signal.h> 9 10 //定义全局变了定义共享内存的ID11 int shmid;12 13 void fa(int singnum)14 {15     printf("正在删除共享内存,请稍后..\n");16 17     sleep(3);18 19     //调用shmctl函数实现删除操作20 21     int res = shmctl(shmid, IPC_RMID, NULL);22     if(-1 == res )23     {24         perror("shmctl");25         exit(-1);26     }27 28     printf("删除共享内存成功\n");29 30     exit(0); //终止进程31 32 }33 34 int main(void)35 {36 37     //1.获取key值,使用ftok函数38 39     key_t key = ftok(".", 100);40     if (-1 == key )41     {42         perror("key error");43         exit(-1);44     }45 46     printf("key = %#x\n", key);47 48     //2.创建共享内存,使用shmget函数49 50     shmid = shmget(key, sizeof(int), IPC_CREAT | IPC_EXCL | 0664);51     if( -1 == shmid )52     {53         perror("SHMGET ERROR");54         exit(-1);55     }56 57     printf("shmid = %d\n", shmid);58 59     //3.挂接共享内存,使用shmat函数60      void* pv = shmat(shmid, NULL, 0);61     if( (void *) -1== pv)62     {63         perror("shmat");64         exit(-1);65     }66 67     printf("挂接共享内存成功\n");68     //4.访问共享内存,存放数据10069 70     *(int *)pv = 100;71 72     //5脱接共享内存,使用shmdt函数73 74     int res = shmdt( pv );75     if(-1 == res )76     {77         perror("shmdt");78         exit(-1);79     }80 81     printf("脱接共享内存成功\n");82     //6.如果不再使用,删除共享内存,shmctl83 84     printf("删除共享内存按,ctrl+c...\n");85 86     if( SIG_ERR == signal(SIGINT, fa) )87     {88         perror("signal");89         exit(-1);90     }91 92     while(1);93 94     return 0;95 }

 

使用消息队列实现进程间的通信( 重点 )

 

     首先将传递的数据打包成消息,其中一个进程负责将消息发送到消息队列中,另外一个进程负责接受消息队列中的消息,从而实现通信

 

     通信模型

        

     1、获取key值,使用ftok函数

     2、创建/获取消息队列,使用msgget函数

     3、发送/接收消息,使用msgsnd/msgrcv函数

     4、如果不再使用,则删除消息队列,使用msgctl函数

 

     #include <sys/types.h>

     #include <sys/ipc.h>

     #include <sys/msg.h>

 

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

 

         参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件

         参 2:项目的编号,必须是非零低八位二进制位

         返回:成功返回生成key值,失败 —— -1

         功能:主要用于根据参数指定的路径和项目编号来生成key值

         注意:使用相同的路径名和相同的项目编号,生成的key值也相同

 

     int msqid = msgget(key_t key, int msgflg);

        

         参 1:key值,ftok的函数返回值

         参 2:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )

 

                   IPE_CREAT —— 不存在则创建,存在则打开

                   IPE_EXCL  —— 与IPC_CREAT搭配使用,存在则创建失败

                       0     ——   获取已经存在的消息队列

 

         返回:成功返回消息队列的ID,失败返回-1

         功能:主要用于创建/获取消息队列,从而获得ID

 

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

 

         参 1:消息队列的ID,msgget函数的返回值

         参 2:消息的首地址

        

              struct msgbuf {

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

               char mtext[1];    /* 消息的数据内容 可以是其它类型 */

           };

             

         参 3:用于指定消息内存的大小也就是(char mtext[1])的大小,不包括long mtype

         参 4:发送消息的标志   默认0 —— 阻塞状态

         功能:主要用于指定消息队列中发送指定消息

 

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

 

         参 1:消息队列的ID,msgget函数的返回值

         参 2:消息的首地址

        

              struct msgbuf {

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

               char mtext[1];    /* 消息的数据内容 可以是其它类型 */

           };

         参 3:用于指定消息内存的大小也就是(char mtext[1])的大小,不包括long mtype

         参 4:消息的类型

               0 —— 表示读取消息队列中第一个消息

              >0 —— 表示读取消息队列中第一个类型为msgtyp的消息

              <0 —— 表示读取消息队列中第一个类型<=msgtypede的绝对值的消息,其中最小的类型优先读取

         参 5:发送消息的标志   默认0 —— 阻塞状态

         返回:成功返回实际接收数据的大小,失败返回-1

         功能:主要用于指定消息队列中接收指定消息

 

              2    1    3    1    2    3    2

         -------------------------------

     <--    a b    c    d    e    f    g    <----

         -------------------------------

    

         当读取的消息类型是0时: a  b    c    e    f    g

         当读取的消息类型是2时:    a    c    g

         当读取的消息类型是-2时:b   d    a    c    g

   

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

 

         参 1:消息队列的ID,msgget函数返回值

         参 2:具体的操作命令IPC_RMID 删除消息队列,此时第三个参数给NULL

         参 3:结构体指针

         功能:主要用于操作指定消息队列中的消息

        

     常用的基本命令

    

         ipcs -q                     察看当前系统已经存在的消息队列

         ipcsrm -q 消息队列的ID 表示删除指定的消息队列



使用信号量集实现进程间的通信

 

     信号量本质就是一种计数器,用于控制同时访问同一种共享资源的进程/线程的个数

 

     信号量的工作原理

         a、初始化信号量到最大值

         b、如果有进程申请到了一个资源,则信号量减一

         c、当信号量的数值为0时,让申请资源的进程进入阻塞状态

         d、如果有进程释放了一个资源,则信号量加一

         e、只要信号量的数值>0,则阻塞的进程就可以继续抢占资源,抢占不到的进程继续阻塞

             

     什么是信号量集

 

         信号量集本质上就是信号量的集合,用于控制多种共享资源被同时访问的进程/线程个数

 

     通信模型

         a、获取key值,使用ftok函数

         b、创建/获取信号量集,使用semget函数

         c、初始化/操作信号量集,使用semctl/semop函数

         d、如果不再使用,则删除信号量集,使用semctl函数

 

     #include <sys/types.h>

   #include<sys/ipc.h>

   #include<sys/sem.h>

 

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

 

         参 1:字符串形式的路径名,必须关联一个存在的,并且可以访问的文件

         参 2:项目的编号,必须是非零低八位二进制位

         返回:成功返回生成key值,失败 —— -1

         功能:主要用于根据参数指定的路径和项目编号来生成key值

         注意:使用相同的路径名和相同的项目编号,生成的key值也相同

 

   int semid =  semget(key_t key, int nsems, int semflg);

    

         参 1:key值

         参 2:信号量集的大小/信号量的个数

                   0 —— 表示获取已经存在的信号量集

         参 3:具体的操作标志 ( IPC_CREAT | IPC_EXCL | 0664 )

                   0 —— 表示获取已经存在的信号量集

         返回:成功返回信号量集的编号,失败返回-1

         功能:主要创建/获取信号量集

        

     int semctl(int semid, int semnum, int cmd, ...);

        

         参 1:信号量集的id,semget返回值

         参 2:信号量集的下标,从零开始

         参 3:具体的操作命令

                   SETVAL  —— 使用第四个参数的数值给信号量集中下标为semnum的信号量进行(初始化)

                   IPC_RMID —— 删除信号量集,此时忽略第二个参数,不需要第四个参数 (删除)

         参 4:可变长参数,是否需要取决于cmd

 

               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

           };

 

         功能:主要用于操作指定的信号量集/信号量

 

   int semop(int semid, structsembuf *sops, unsigned nsops);

 

         参 1:信号量集的id,semget返回值

         参 2:结构体指针,可以指向结构体变量/数组地址

              struct sembuf{

                   unsigned shortsem_num;  /* 信号量集的下标 */

                   short          sem_op;   /* 信号量的操作,整数表示增加,负数表示减少,0不变 */

                   short         sem_flg;  /* 操作的标志,默认给0 */

              }

 

         参 3:结构体指针所指向的结构体数量

         功能:主要用于操作信号量集/信号量

 

         ipcs  -s —— 察看信号量集

         ipcrm -s —— 删除信号量集

         ipcs  -a —— 察看所有的IPC结构

 1 //使用信号量集实现进程间的通信 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <unistd.h> 5 #include <sys/types.h> 6 #include <sys/ipc.h> 7 #include <sys/sem.h> 8 #include <signal.h> 9 10 int semid;11 12 void fa(int signum)13 {14 15     printf("正在删除信号量集...\n");16 17     int res = semctl( semid, 0, IPC_RMID);18 19     if( -1 == res )20     {21         perror( "semctl error" );22         exit(-1);23     }24 25     sleep(3);26 27     printf("删除成功!");28 29     exit(1);30 }31 32 int main(void)33 {34 35     //1.获取key,ftok函数36 37         key_t key = ftok(".", 200);38         if(-1 == key )39         {40             perror("FTOK ERROR");41             exit(-1);42         }43 44         printf("key = %#x\n", key);45     //2.创建信号量集,使用semget函数46      semid = semget(key, 1/*信号量集的大小*/, IPC_CREAT | IPC_EXCL | 0666);47         if( -1 == semid )48         {49             perror("SEMGET ERROR");50             exit(-1);51         }52 53         printf("semid = %d \n", semid);54     //3.初始化信号量集,使用semctl函数55     //int arr[1];56     //int arr[0] = 5;57         int res = semctl(semid, 0/*信号量下标*/,SETVAL, 5/*信号量的初始值*/);58         if(-1== res)59         {60             perror("semctle");61             exit(-1);62         }63         printf("初始化信号量集成功\n");64     //4.如果不再使用,删除信号量集,semctl65 66         printf("请按ctrl+c删除信号量集\n");67         if( SIG_ERR == signal(SIGINT, fa) )68         {69             perror("SIGNAL ERROR");70             exit(-1);71         }72 73         while(1);74 75     return 0;76 }42 43             struct sembuf buf;44             buf.sem_num = 0;45             buf.sem_op = -1;46             buf.sem_flg = 0;47 48             int res = semop(semid, &buf, 1);49             if( -1 == res )50             {51                 perror("semop");52                 exit(-1);53             }54 55             printf("进程%d抢占共享资源成功\n", getpid() );56 57             sleep(20);58 59             buf.sem_op = 1;60 61             res = semop(semid, &buf, 1);62             if(-1 == res )63             {64                 perror("semop");65                 exit(-1);66             }67 68             printf("进程%d释放资源\n", getpid());





0 0
原创粉丝点击