您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

进程间的五种通信方式是什么

2024/6/7 22:11:00发布43次查看
进程间的五种通信方式:1、管道,速度慢,容量有限,只有父子进程能通讯;2、fifo,任何进程间都能通讯,但速度慢;3、消息队列,可以实现消息的随机查询,容量受到系统限制;4、信号量,不能传递复杂消息,只能用来同步;5、共享内存区,指两个或多个进程共享一个给定的存储区。
本教程操作环境:windows7系统、dell g3电脑。
进程间的五种通信方式:
一、管道管道,通常指无名管道,是 unix 系统ipc最古老的形式。
1、特点:它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
2、原型:1 #include <unistd.h>2 int pipe(int fd[2]);    // 返回值:若成功返回0,失败返回-1
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。如下图:
要关闭管道只需将这两个文件描述符关闭即可。
3、例子单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 ipc 通道。如下图所示:
若要数据流从父进程流向子进程,则关闭父进程的读端(fd[0])与子进程的写端(fd[1]);反之,则可以使数据流从子进程流向父进程。
 1 #include<stdio.h> 2 #include<unistd.h> 3  4 int main() 5 { 6     int fd[2];  // 两个文件描述符 7     pid_t pid; 8     char buff[20]; 9 10     if(pipe(fd) < 0) // 创建管道11 printf("create pipe error!\n");12 13 if((pid = fork()) < 0) // 创建子进程14 printf("fork error!\n");15 else if(pid > 0)  // 父进程16     {17         close(fd[0]); // 关闭读端18         write(fd[1], hello world\n, 12);19     }20     else21     {22         close(fd[1]); // 关闭写端23         read(fd[0], buff, 20);24         printf(%s, buff);25     }26 27     return 0;28 }
二、fifofifo,也称为命名管道,它是一种文件类型。
1、特点fifo可以在无关的进程之间交换数据,与无名管道不同。
fifo有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。
2、原型1 #include <sys/stat.h>2 // 返回值:成功返回0,出错返回-13 int mkfifo(const char *pathname, mode_t mode);
其中的 mode 参数与open函数中的 mode 相同。一旦创建了一个 fifo,就可以用一般的文件i/o函数操作它。
当 open 一个fifo时,是否设置非阻塞标志(o_nonblock)的区别:
若没有指定o_nonblock(默认),只读 open 要阻塞到某个其他进程为写而打开此 fifo。类似的,只写 open 要阻塞到某个其他进程为读而打开它。
若指定了o_nonblock,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 fifo,其errno置enxio。
3、例子fifo的通信方式类似于在进程中使用文件来传输数据,只不过fifo类型文件同时具有管道的特性。在数据读出时,fifo管道中同时清除数据,并且“先进先出”。下面的例子演示了使用 fifo 进行 ipc 的过程:
write_fifo.c
 1 #include<stdio.h> 2 #include<stdlib.h>   // exit 3 #include<fcntl.h>    // o_wronly 4 #include<sys/stat.h> 5 #include<time.h>     // time 6  7 int main() 8 { 9     int fd;10     int n, i;11     char buf[1024];12     time_t tp;13 14     printf(i am %d process.\n, getpid()); // 说明进程id15     16     if((fd = open(fifo1, o_wronly)) < 0) // 以写打开一个fifo 17     {18         perror(open fifo failed);19         exit(1);20     }21 22     for(i=0; i<10; ++i)23     {24         time(&tp);  // 取系统当前时间25         n=sprintf(buf,process %d's time is %s,getpid(),ctime(&tp));26         printf(send message: %s, buf); // 打印27         if(write(fd, buf, n+1)  0,返回队列中消息类型为 type 的第一个消息;type < 0,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。(其他的参数解释,请自行google之)
3、例子下面写了一个简单的使用消息队列进行ipc的例子,服务端程序一直在等待特定类型的消息,当收到该类型的消息以后,发送另一种特定类型的消息作为反馈,客户端读取该反馈并打印出来。
msg_server.c
 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <sys/msg.h> 4  5 // 用于创建一个唯一的key 6 #define msg_file /etc/passwd 7  8 // 消息结构 9 struct msg_form {10     long mtype;11     char mtext[256];12 };13 14 int main()15 {16     int msqid;17     key_t key;18     struct msg_form msg;19     20     // 获取key值21     if((key = ftok(msg_file,'z'))  0,表示进程释放相应的资源数,将 sem_op 的值加到信号量的值上。如果有进程正在休眠等待此信号量,则换行它们。
若sem_op < 0,请求 sem_op 的绝对值的资源。
如果相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回。当相应的资源数不能满足请求时,这个操作与sem_flg有关。sem_flg 指定ipc_nowait,则semop函数出错返回eagain。sem_flg 没有指定ipc_nowait,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:当相应的资源数可以满足请求,此信号量的semncnt值减1,该信号量的值减去sem_op的绝对值。成功返回;此信号量被删除,函数smeop出错返回eidrm;进程捕捉到信号,并从信号处理函数返回,此情况下将此信号量的semncnt值减1,函数semop出错返回eintr若sem_op == 0,进程阻塞直到信号量的相应值为0:
当信号量已经为0,函数立即返回。如果信号量的值不为0,则依据sem_flg决定函数动作:sem_flg指定ipc_nowait,则出错返回eagain。sem_flg没有指定ipc_nowait,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回;此信号量被删除,函数smeop出错返回eidrm;进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回eintr在semctl函数中的命令有多种,这里就说两个常用的:
setval:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。ipc_rmid:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。3、例子  1 #include<stdio.h>  2 #include<stdlib.h>  3 #include<sys/sem.h>  4   5 // 联合体,用于semctl初始化  6 union semun  7 {  8     int              val; /*for setval*/  9     struct semid_ds *buf; 10     unsigned short  *array; 11 }; 12  13 // 初始化信号量 14 int init_sem(int sem_id, int value) 15 { 16     union semun tmp; 17     tmp.val = value; 18     if(semctl(sem_id, 0, setval, tmp) == -1) 19     { 20         perror(init semaphore error); 21         return -1; 22     } 23     return 0; 24 } 25  26 // p操作: 27 //    若信号量值为1,获取资源并将信号量值-1  28 //    若信号量值为0,进程挂起等待 29 int sem_p(int sem_id) 30 { 31     struct sembuf sbuf; 32     sbuf.sem_num = 0; /*序号*/ 33     sbuf.sem_op = -1; /*p操作*/ 34     sbuf.sem_flg = sem_undo; 35  36     if(semop(sem_id, &sbuf, 1) == -1) 37     { 38         perror(p operation error); 39         return -1; 40     } 41     return 0; 42 } 43  44 // v操作: 45 //    释放资源并将信号量值+1 46 //    如果有进程正在挂起等待,则唤醒它们 47 int sem_v(int sem_id) 48 { 49     struct sembuf sbuf; 50     sbuf.sem_num = 0; /*序号*/ 51     sbuf.sem_op = 1;  /*v操作*/ 52     sbuf.sem_flg = sem_undo; 53  54     if(semop(sem_id, &sbuf, 1) == -1) 55     { 56         perror(v operation error); 57         return -1; 58     } 59     return 0; 60 } 61  62 // 删除信号量集 63 int del_sem(int sem_id) 64 { 65     union semun tmp; 66     if(semctl(sem_id, 0, ipc_rmid, tmp) == -1) 67     { 68         perror(delete semaphore error); 69         return -1; 70     } 71     return 0; 72 } 73  74  75 int main() 76 { 77     int sem_id;  // 信号量集id 78     key_t key;   79     pid_t pid; 80  81     // 获取key值 82     if((key = ftok(., 'z')) < 0) 83 { 84 perror("ftok error"); 85 exit(1); 86 } 87 88 // 创建信号量集,其中只有一个信号量 89 if((sem_id = semget(key, 1, ipc_creat|0666)) == -1) 90 { 91 perror("semget error"); 92 exit(1); 93 } 94 95 // 初始化:初值设为0资源被占用 96 init_sem(sem_id, 0); 97 98 if((pid = fork()) == -1) 99 perror("fork error");100 else if(pid == 0) /*子进程*/ 101 {102 sleep(2);103 printf("process child: pid=%d\n", getpid());104 sem_v(sem_id); /*释放资源*/105 }106 else /*父进程*/107 {108 sem_p(sem_id); /*等待资源*/109 printf("process father: pid=%d\n", getpid());110 sem_v(sem_id); /*释放资源*/111 del_sem(sem_id); /*删除信号量集*/112 }113 return 0;114 }
上面的例子如果不加信号量,则父进程会先执行完毕。这里加了信号量让父进程等待子进程执行完以后再执行。
五、共享内存共享内存(shared memory),指两个或多个进程共享一个给定的存储区。
1、特点共享内存是最快的一种 ipc,因为进程是直接对内存进行存取。
因为多个进程可以同时操作,所以需要进行同步。
信号量+共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
2、原型1 #include <sys/shm.h>2 // 创建或获取一个共享内存:成功返回共享内存id,失败返回-13 int shmget(key_t key, size_t size, int flag);4 // 连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回-15 void *shmat(int shm_id, const void *addr, int flag);6 // 断开与共享内存的连接:成功返回0,失败返回-17 int shmdt(void *addr); 8 // 控制共享内存的相关信息:成功返回0,失败返回-19 int shmctl(int shm_id, int cmd, struct shmid_ds *buf);
当用shmget函数创建一段共享内存时,必须指定其 size;而如果引用一个已存在的共享内存,则将 size 指定为0 。
当一段共享内存被创建以后,它并不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。
shmdt函数是用来断开shmat建立的连接的。注意,这并不是从系统中删除该共享内存,只是当前进程不能再访问该共享内存而已。
shmctl函数可以对共享内存执行多种操作,根据参数 cmd 执行相应的操作。常用的是ipc_rmid(从系统中删除该共享内存)。
3、例子下面这个例子,使用了【共享内存+信号量+消息队列】的组合来实现服务器进程与客户进程间的通信。
共享内存用来传递数据;信号量用来同步;消息队列用来 在客户端修改了共享内存后 通知服务器读取。server.c
  1 #include<stdio.h>  2 #include<stdlib.h>  3 #include<sys/shm.h>  // shared memory  4 #include<sys/sem.h>  // semaphore  5 #include<sys/msg.h>  // message queue  6 #include<string.h>   // memcpy  7   8 // 消息队列结构  9 struct msg_form { 10     long mtype; 11     char mtext; 12 }; 13  14 // 联合体,用于semctl初始化 15 union semun 16 { 17     int              val; /*for setval*/ 18     struct semid_ds *buf; 19     unsigned short  *array; 20 }; 21  22 // 初始化信号量 23 int init_sem(int sem_id, int value) 24 { 25     union semun tmp; 26     tmp.val = value; 27     if(semctl(sem_id, 0, setval, tmp) == -1) 28     { 29         perror(init semaphore error); 30         return -1; 31     } 32     return 0; 33 } 34  35 // p操作: 36 //  若信号量值为1,获取资源并将信号量值-1  37 //  若信号量值为0,进程挂起等待 38 int sem_p(int sem_id) 39 { 40     struct sembuf sbuf; 41     sbuf.sem_num = 0; /*序号*/ 42     sbuf.sem_op = -1; /*p操作*/ 43     sbuf.sem_flg = sem_undo; 44  45     if(semop(sem_id, &sbuf, 1) == -1) 46     { 47         perror(p operation error); 48         return -1; 49     } 50     return 0; 51 } 52  53 // v操作: 54 //  释放资源并将信号量值+1 55 //  如果有进程正在挂起等待,则唤醒它们 56 int sem_v(int sem_id) 57 { 58     struct sembuf sbuf; 59     sbuf.sem_num = 0; /*序号*/ 60     sbuf.sem_op = 1;  /*v操作*/ 61     sbuf.sem_flg = sem_undo; 62  63     if(semop(sem_id, &sbuf, 1) == -1) 64     { 65         perror(v operation error); 66         return -1; 67     } 68     return 0; 69 } 70  71 // 删除信号量集 72 int del_sem(int sem_id) 73 { 74     union semun tmp; 75     if(semctl(sem_id, 0, ipc_rmid, tmp) == -1) 76     { 77         perror(delete semaphore error); 78         return -1; 79     } 80     return 0; 81 } 82  83 // 创建一个信号量集 84 int creat_sem(key_t key) 85 { 86     int sem_id; 87     if((sem_id = semget(key, 1, ipc_creat|0666)) == -1) 88     { 89         perror(semget error); 90         exit(-1); 91     } 92     init_sem(sem_id, 1);  /*初值设为1资源未占用*/ 93     return sem_id; 94 } 95  96  97 int main() 98 { 99     key_t key;100     int shmid, semid, msqid;101     char *shm;102     char data[] = this is server;103     struct shmid_ds buf1;  /*用于删除共享内存*/104     struct msqid_ds buf2;  /*用于删除消息队列*/105     struct msg_form msg;  /*消息队列用于通知对方更新了共享内存*/106 107     // 获取key值108     if((key = ftok(., 'z')) < 0)109     {110         perror(ftok error);111         exit(1);112     }113 114     // 创建共享内存115     if((shmid = shmget(key, 1024, ipc_creat|0666)) == -1)116     {117         perror(create shared memory error);118         exit(1);119     }120 121     // 连接共享内存122     shm = (char*)shmat(shmid, 0, 0);123     if((int)shm == -1)124     {125         perror(attach shared memory error);126         exit(1);127     }128 129 130     // 创建消息队列131     if ((msqid = msgget(key, ipc_creat|0777)) == -1)132     {133         perror(msgget error);134         exit(1);135     }136 137     // 创建信号量138     semid = creat_sem(key);139     140     // 读数据141     while(1)142     {143         msgrcv(msqid, &msg, 1, 888, 0); /*读取类型为888的消息*/144         if(msg.mtext == 'q')  /*quit - 跳出循环*/ 145             break;146         if(msg.mtext == 'r')  /*read - 读共享内存*/147         {148             sem_p(semid);149             printf(%s\n,shm);150             sem_v(semid);151         }152     }153 154     // 断开连接155     shmdt(shm);156 157     /*删除共享内存、消息队列、信号量*/158     shmctl(shmid, ipc_rmid, &buf1);159     msgctl(msqid, ipc_rmid, &buf2);160     del_sem(semid);161     return 0;162 }
client.c
  1 #include<stdio.h>  2 #include<stdlib.h>  3 #include<sys/shm.h>  // shared memory  4 #include<sys/sem.h>  // semaphore  5 #include<sys/msg.h>  // message queue  6 #include<string.h>   // memcpy  7   8 // 消息队列结构  9 struct msg_form { 10     long mtype; 11     char mtext; 12 }; 13  14 // 联合体,用于semctl初始化 15 union semun 16 { 17     int              val; /*for setval*/ 18     struct semid_ds *buf; 19     unsigned short  *array; 20 }; 21  22 // p操作: 23 //  若信号量值为1,获取资源并将信号量值-1  24 //  若信号量值为0,进程挂起等待 25 int sem_p(int sem_id) 26 { 27     struct sembuf sbuf; 28     sbuf.sem_num = 0; /*序号*/ 29     sbuf.sem_op = -1; /*p操作*/ 30     sbuf.sem_flg = sem_undo; 31  32     if(semop(sem_id, &sbuf, 1) == -1) 33     { 34         perror(p operation error); 35         return -1; 36     } 37     return 0; 38 } 39  40 // v操作: 41 //  释放资源并将信号量值+1 42 //  如果有进程正在挂起等待,则唤醒它们 43 int sem_v(int sem_id) 44 { 45     struct sembuf sbuf; 46     sbuf.sem_num = 0; /*序号*/ 47     sbuf.sem_op = 1;  /*v操作*/ 48     sbuf.sem_flg = sem_undo; 49  50     if(semop(sem_id, &sbuf, 1) == -1) 51     { 52         perror(v operation error); 53         return -1; 54     } 55     return 0; 56 } 57  58  59 int main() 60 { 61     key_t key; 62     int shmid, semid, msqid; 63     char *shm; 64     struct msg_form msg; 65     int flag = 1; /*while循环条件*/ 66  67     // 获取key值 68     if((key = ftok(., 'z')) < 0) 69     { 70         perror(ftok error); 71         exit(1); 72     } 73  74     // 获取共享内存 75     if((shmid = shmget(key, 1024, 0)) == -1) 76     { 77         perror(shmget error); 78         exit(1); 79     } 80  81     // 连接共享内存 82     shm = (char*)shmat(shmid, 0, 0); 83     if((int)shm == -1) 84     { 85         perror(attach shared memory error); 86         exit(1); 87     } 88  89     // 创建消息队列 90     if ((msqid = msgget(key, 0)) == -1) 91     { 92         perror(msgget error); 93         exit(1); 94     } 95  96     // 获取信号量 97     if((semid = semget(key, 0, 0)) == -1) 98     { 99         perror(semget error);100         exit(1);101     }102     103     // 写数据104     printf(***************************************\n);105     printf(*                 ipc                 *\n);106     printf(*    input r to send data to server.  *\n);107     printf(*    input q to quit.                 *\n);108     printf(***************************************\n);109     110     while(flag)111     {112         char c;113         printf(please input command: );114         scanf(%c, &c);115         switch(c)116         {117             case 'r':118                 printf(data to send: );119                 sem_p(semid);  /*访问资源*/120                 scanf(%s, shm);121                 sem_v(semid);  /*释放资源*/122                 /*清空标准输入缓冲区*/123                 while((c=getchar())!='\n' && c!=eof);124                 msg.mtype = 888;  125                 msg.mtext = 'r';  /*发送消息通知服务器读数据*/126                 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);127                 break;128             case 'q':129                 msg.mtype = 888;130                 msg.mtext = 'q';131                 msgsnd(msqid, &msg, sizeof(msg.mtext), 0);132                 flag = 0;133                 break;134             default:135                 printf(wrong input!\n);136                 /*清空标准输入缓冲区*/137                 while((c=getchar())!='\n' && c!=eof);138         }139     }140 141     // 断开连接142     shmdt(shm);143 144     return 0;145 }
注意:当scanf()输入字符或字符串时,缓冲区中遗留下了\n,所以每次输入操作后都需要清空标准输入的缓冲区。但是由于 gcc 编译器不支持fflush(stdin)(它只是标准c的扩展),所以我们使用了替代方案:
1 while((c=getchar())!='\n' && c!=eof);
五种通讯方式总结1.管道:速度慢,容量有限,只有父子进程能通讯    
2.fifo:任何进程间都能通讯,但速度慢    
3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    
4.信号量:不能传递复杂消息,只能用来同步    
5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存
相关免费学习推荐:php编程(视频)
以上就是进程间的五种通信方式是什么的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product