linux多进程通信专题01
一、进程间通信概述:
1、什么是进程间通信:由于进程之间的用户空间都是独立的;所以在用户空间进行通信是不可能的;因此借助于Linux内核实现进程之间通信。
进程是CPU分配内存资源的最小单位;类似于小房子一样,线程类似于小房子中的具体物件,执行任务;
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> /* 使用用户空间的变量实现父子进程之间通信 结论:由于用于空间的变量都是独立的,子进程中的变量,继承父进程的变量,所以无法实现进程之间通信 */ int main() { int val = 0; pid_t pid = 0; pid = fork(); if(pid< 0){ printf("func fork failed\n"); return -1; } if(pid == 0) // child process { printf("child process runing\n"); while(val != 1) { printf("waiting parent process run...\n"); sleep(1); } } if(pid > 0) { printf("parent process run....\n"); val = 1; printf("set val = 1 over\n"); } wait(NULL); return 0; }
什么是线程间通信:线程是CPU执行任务的最小单位;执行具体的任务,可以在用户空间完成;在同一个进程中的多个线程共享进程的内存
资源;类似于小房子中的具体物件;
#include <stdio.h> #include <pthread.h> #include <unistd.h> int val = 0; void* pFunc(void* arg) { printf("new thread run...\n"); while(val != 2) { sleep(1); printf("wait main thread set val = 2\n"); } printf("main thread set val = 2 over....\n"); return (void*)0; } int main() { pthread_t tid; tid = pthread_create(&tid,NULL,pFunc,NULL); if(tid < 0) { printf("create new pthreaf d failed\n"); return -1; } sleep(5); // child enough time to run val = 2; printf("main thread set val =2 over\n"); pthread_join(tid,NULL); // wait relase child thread return 0; } /* topeet@ubuntu:~/mycode/multprocess$ ./a.out new thread run... wait main thread set val = 2 wait main thread set val = 2 wait main thread set val = 2 wait main thread set val = 2 main thread set val =2 over */
2、进程间通信有那种方式:在Linux 内核中创建对象,用于进程间通信;不同的对象存在不同的通信方式;不同的应用场景,不同的实现机制;
管道通信:无名管道、有名管道(文件系统中有名,有文件节点)
信号通信:信号发送、信号接收、信号处理
IPC通信:共享内存、消息队列、信号灯
socket通信:多用于两台主机上的进程之间通信
3、进程间通信学习思路:每一种通信方式都是基于文件IO思想
open:创建进程通信的不同对象,函数形式不一样;
write:向进程之间通信的对象中写操作;
read:从进程对象中读取数据;
close:进程间通信对象释放内存、资源;
二、无名管道:
如果Linux内核中为进程之间通信的对象是管道,那么进程之间通信就是管道通信;如果Linux内核在文件系统中并且创建有文件节点,那么属于有名管道;相反如果没有创建文件节点,那么就是无名管道通信方式;
很显然,如果在文件系统中没有创建文件节点,就是无名管道;我们先回顾下如果使用open函数创建一个文件都需要那些参数,open函数原型如下:
int open(const char *pathname, int flags, mode_t mode);
1、const char* pathname:这个是包含文件路径的文件名称;
2、int flags:操作文件的权限;
3、操作文件的模式
4、创建成功,返回文件描述符;
接下来可以思考下,创建无名管道需要的参数:1、无名管道并且在文件系统中没有文件节点,因此不需要文件名;在操作管道时候,类似操作队列,只需要关注读写,因此不需要操作权限;创建无名管道函数原型如下:应用程序调用此程序后,内核将在内核空间创建一个管道队列,并将文件描述符返回:
int pipe(int pipefd[2]);
1、返回值:成功返回0,失败返回-1;
2、参数列表中的参数:返回两个文件描述符,pipef[0]用于读操作;pipefd[1]用于写操作;
那么看第一个列子,在一个进程中创建一个管道,并读写操作:
#include <stdio.h> #include <unistd.h> #include <string.h> int main() { char buf[] = "hello pipe"; char val[128]={0}; int fd[2]; int ret; ret = pipe(fd); if(ret != 0){ printf("func create pipe failed\n"); return -1; } printf("create new filw describe fd[0] = %d,fd[1] = %d\n",fd[0],fd[1]); printf("create pipe success...\n"); write(fd[1],buf,sizeof(buf)); sleep(1); printf("write string over and read pipe\n"); read(fd[0],val,16); printf("read data from pipe...\n"); printf("data from buf is %s\n",val); return 0; }
在学习两个进程之间使用管道通信通信之前,我们先了解下管道的几个特性:
1、管道如果为空,读管道操作将阻塞;
2、读操作一次管道,管道数据将被清空;而读操作普通文件,读操作并不会影响数据;
3、写入管道的字符大小有限制,65535
4、管道通信适用于父子进程或者具有血缘关系的进程;
5、创建的管道文件不会占用用户空间内存资源;
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/wait.h> 5 #include <string.h> 6 7 int main() 8 { 9 int fd[2]; 10 pid_t pid; 11 int ret; 12 char readBuf[64]; 13 char wBuf[] = "hello linux"; 14 15 ret = pipe(fd); 16 if(ret !=0) 17 { 18 printf("func create pipe failed\n"); 19 return -2; 20 } 21 pid = fork(); 22 if(pid <0) 23 { 24 printf("func fork failed\n"); 25 return -1; 9,11-14 Top 19 return -2; 20 } 21 pid = fork(); 22 if(pid <0) 23 { 24 printf("func fork failed\n"); 25 return -1; 26 } 27 if(pid == 0) // child process 28 { 29 printf("child process runing...\n"); 30 memset(readBuf,0,64); 31 read(fd[0],readBuf,64); 32 printf("read data from parent %s\n",readBuf); 33 34 } 35 if(pid > 0){ 36 sleep(5); 37 write(fd[1],wBuf,sizeof(wBuf)); 38 printf("Parent write data over\n"); 39 } 40 wait(NULL); 41 42 return 0; 43 } 42,10-13 Bot
测试管道的最大缓冲字节:65535
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/wait.h> 5 #include <string.h> 6 7 int main() 8 { 9 int fd[2]; 10 pid_t pid; 11 int ret; 12 int cnt = 0; 13 char readBuf[64]; 14 char wBuf[] = "hello linux"; 15 char c = 'a'; 16 ret = pipe(fd); 17 if(ret !=0) 18 { 19 printf("func create pipe failed\n"); 20 return -2; 21 } 22 // write data to pipe 23 for(cnt =0;cnt<9000000;cnt++) 24 { 25 write(fd[1],&c,1); 7 int main() 8 { 9 int fd[2]; 10 pid_t pid; 11 int ret; 12 int cnt = 0; 13 char readBuf[64]; 14 char wBuf[] = "hello linux"; 15 char c = 'a'; 16 ret = pipe(fd); 17 if(ret !=0) 18 { 19 printf("func create pipe failed\n"); 20 return -2; 21 } 22 // write data to pipe 23 for(cnt =0;cnt<9000000;cnt++) 24 { 25 write(fd[1],&c,1); 26 if(cnt > 65500) 27 printf("cnt char is :%d\n",cnt); 28 } 29 30 return 0; 31 } /* cnt在65535处阻塞了,并且该进程处于睡眠态 */
读操作将清空管道中的数据:程序将阻塞在第二次读操作处
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/wait.h> 5 #include <string.h> 6 7 int main() 8 { 9 int fd[2]; 10 pid_t pid; 11 int ret; 12 int cnt = 0; 13 char readBuf[64]; 14 char wBuf[] = "hello linux"; 15 char c = 'a'; 16 ret = pipe(fd); 17 if(ret !=0) 18 { 19 printf("func create pipe failed\n"); 20 return -2; 21 } 22 // write only once 23 write(fd[1],wBuf,sizeof(wBuf)); 24 sleep(1); 25 // read data first 14 char wBuf[] = "hello linux"; 15 char c = 'a'; 16 ret = pipe(fd); 17 if(ret !=0) 18 { 19 printf("func create pipe failed\n"); 20 return -2; 21 } 22 // write only once 23 write(fd[1],wBuf,sizeof(wBuf)); 24 sleep(1); 25 // read data first 26 memset(readBuf,0,64); 27 read(fd[0],readBuf,64); 28 printf("first read data readBuf is %s\n",readBuf); 29 memset(readBuf,0,64); 30 // read data second //process will wait this and go to sleep status 31 read(fd[0],readBuf,64); 32 printf("second read data readBuf : %s\n",readBuf); 33 34 close(fd[0]); 35 close(fd[1]); 36 37 return 0; 38 }
三、有名管道:
在有名管道中我们有提到,所谓有名管道是在文件系统中有创建文件节点;或者说有名管道是借助一个已经存再的文件进行多个线程之间通信;
在使用有名管道之前,我们需要创建一个文件,函数原型如下:
int mkfifo(const char *pathname, mode_t mode);
const char* pathname: 包含有文件路径的文件名称;
mode_t mode:创建文件的权限;
创建的该特殊文件,当使用open函数打开该文件时,内核会创建一个管道,通过文件节点,返回管道的文件描述符;
如果一个程序在读操作该文件时,读操作会阻塞直到写操作开始,读操作才会返回;同样的当写该文件时,写操作也会阻塞,直到读操作开始,写操作才开始;
prw-rw-r-- 1 liu liu 0 3月 15 14:23 mkfifo // 管道文件前面是“p” -rwxrwxr-x 1 liu liu 8400 3月 15 14:23 a.out // 普通文件前面是“-”
创建一个特殊的文件用于管道通信:
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/wait.h> 5 #include <string.h> 6 #include <sys/stat.h> 7 8 int main() 9 { 10 int fd[2]; 11 pid_t pid; 12 int ret; 13 int cnt = 0; 14 char readBuf[64]; 15 char wBuf[] = "hello linux"; 16 char c = 'a'; 17 // create special fifo file 18 ret = mkfifo("./mkfifo",0666); 19 if(ret != 0) 20 printf("mkfifo failed\n"); 21 return -1; 22 //write data to pipe 23 24 return 0; 25 } -- INSERT -- 17,29-32 All
向管道写数据:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd; int ret; int cnt = 0; char wBuf[] = "hello linux"; //write data to pipe fd = open("./mkfifo",O_WRONLY); if(fd < 0) { printf("open file mkfifo failed\n"); return -1; } printf("open mkfifo success\n"); write(fd,wBuf,sizeof(wBuf)); printf("write data over\n"); return 0; }
从管道读数据:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <string.h> #include <sys/stat.h> #include <fcntl.h> int main() { int fd; int ret; int cnt = 0; char readBuf[64] ={0}; char wBuf[] = "hello linux"; //read data from file mkfifo fd = open("./mkfifo",O_RDONLY); if(fd < 0) { printf("open file mkfifo failed\n"); return -1; } printf("open mkfifo success\n"); read(fd,readBuf,64); printf("read data is %s from mkfifo\n",readBuf); printf("write data over\n"); return 0; }
可以在编译的时候,查看另一个程序的运行状态,一个程序打开时,将会阻塞,等待另一个进程打开管道;
从上面可以看出来,有名管道使用文件节点,在打开文件节点的时候,内核创建了管道,共进程之间通信、数据交换;因此有名管道补充了无名管道的缺点;从而有名管道可适用范围更广;不仅仅局限于父子进程之间、具有血缘关系进程;
四、信号通信
通信模型:
在前面我们有说到过,两个进程之间通信,在用户空间是不可以实现的,需要间接或者直接借用linux内核完成进程之间通信;借用linux内核的方式类型(对象)不同,通信方式,模型也是不同的;
在linux内核中有64种信号,包括可靠信号与不可靠信号、可阻塞信号与不可阻塞信号;信号在linux内核已经存在的;
进程A与进程B之间通信,进程A需要告诉linux内核哪个进程、发送什么信号;当然了,进程B需要存在,可以睡眠、可以暂停(while、sleep、pause);
进程A发送信号,停止进程B:进程Bwhile()循环运行,直到进程B接收到linux内核发送过来的终止信号;
进程A:使用kill()来给其他进程发送信号,raise()给自己发送信号,alarm() 延时发送信号;
#include <stdio.h> #include <signal.h> #include <stdlib.h> int main(int argc,char* argv[]) { int sig; int pid; if(argc < 3){ printf("not enough parameter...try again\n"); return -1; } sig = atoi(argv[1]); pid = atoi(argv[2]); printf("argv[1]:%d...argv[2]:%d\n",sig,pid); kill(pid, sig); return 0; }
发送信号:kill()、raise()、alarm()
接收信号:发送的信号,进程默认接收处理;while()、sleep()、pause()
处理信号:signal()、sigaction()
#include <stdio.h> #include <signal.h> #include <stdlib.h> int main(int argc,char* argv[]) { printf("raise before ...\n"); raise(9); printf("raise after ....\n"); return 0; }
alarm():让内核延时发送信号给当前进程;只能发送sigalrm信号终止当前进程;发送给当前进程,不能让当前进程终止;该函数非阻塞;
进程中存在的状态:R(运行状态)、T(暂停状态)、S(睡眠状态)、Z(僵死状态)
#include <stdio.h> #include <unistd.h> #include <signal.h> int main() { int i = 0; printf("alarm before...\n"); alarm(10); // after 10s and send sigalrm signal,进程结束 printf("alarm after ...\n"); while(i < 15) { printf("main process runing %d..\n",i); sleep(1); i++; } return 0; }
处理信号:
在使用signal()处理信号时,根据参数不同有三种不同的处理方式:
1、忽略发送过来的信号;
2、进程默认信号的处理方式(结束进程、暂停进程等);
3、自定义信号的响应方式;
忽略信号:
#include <stdio.h> #include <unistd.h> #include <signal.h> int main() { int i = 0; printf("alarm before...\n"); alarm(10); // after 10s and send sigalrm signal printf("alarm after ...\n"); // 忽略alarm发送过来的信号,进程正常运行 signal(SIGALRM,SIG_IGN); // ignore signal 14(SIGALRM) while(i < 15) { printf("main process runing %d..\n",i); sleep(1); i++; } return 0; }
恢复信号默认处理方式:在前面忽略后,在恢复默认处理方式:
#include <stdio.h> #include <unistd.h> #include <signal.h> int main() { int i = 0; printf("alarm before...\n"); alarm(10); // after 10s and send sigalrm signal printf("alarm after ...\n"); signal(SIGALRM,SIG_IGN); // ignore signal 14(SIGALRM) printf("recover signal deal method SIG_DFL\n"); signal(SIGALRM, SIG_DFL); // 信号默认处理方式,程序终止 while(i < 15) { printf("main process runing %d..\n",i); sleep(1); i++; } return 0; }
自定义信号的处理方式:
#include <stdio.h> #include <unistd.h> #include <signal.h> void sigFunc(int signum) { printf("signal call back function signum:%d\n",signum); return; } int main() { int i = 0; printf("alarm before...\n"); alarm(10); // after 10s and send sigalrm signal printf("alarm after ...\n"); signal(SIGALRM, sigFunc); // ignore signal 14(SIGALRM) while(i < 15) { printf("main process runing %d..\n",i); sleep(1); i++; } return 0; }
自定义信号处理方式在父子进程中简单应用:父进程处理自己的任务,子进程给父进程发送信号然后退出,父进程去处理信号;
由于wait()函数是阻塞的,所以当父进程等待子进程退出时,不能执行其他任务,因此可以处理子进程退出时发送的信号,以避免僵尸进程
#include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h> #include <sys/wait.h> void sigFunc(int signum) { printf("signal call back function signum:%d\n",signum); return; } void sigFunc1(int signum) // deal signal SIGCHLD { printf("deal with SIGCHLD...\n"); wait(NULL); } int main() { pid_t pid; pid = fork(); if(pid < 0){ if(pid < 0){ printf("create new child process failed\n"); return -1; } if(pid > 0) // parent process { int i=0; signal(SIGUSR1, sigFunc); signal(SIGCHLD, sigFunc1); while(i < 20) { printf("parent runing.. %d s\n",i); sleep(1); i++; } } if(pid == 0) // child process { sleep(2); kill(getppid(),SIGUSR1); printf("child process exit...\n"); exit(0); // send signal to parent SIGCHLD } }
五、IPC通信
在前面我们有提到过,进程间通信使用内核中不同的对象进行通信。前面有无名管道、有名管道、信号;在IPC进程间通信也是用内核中的IPC对象,IPC对象包含有共享内存、消息队列、信号灯;同样也是基于文件IO思想:
| 文件IO | IPC |
| open | Msgget、shmget、Semget |
| read、write | msgsnd、msgrecv、shmat、semop |
| close | msgctrl、shmctrl、semctrl |
共享内存:在使用shmget()创建了共享内存后,可以使用命令ipcs -m查看内核中的所有共享内存;以及使用ipcrm -m shmid删除指定的共享内存;ipcs -q查看创建的消息队列;ipcs -s查看创建的信号灯对象;
1、IPC-共享内存通信
创建共享内存,并查看创建的共享内存:
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <errno.h> int main() { int shmid; int key; key = ftok("./a.c",'e'); if(key < 0) { printf("create key falure\n"); return -1; } // shmid = shmget(IPC_PRIVATE, 128, 6060); shmid = shmget(key,128,6060); if(shmid < 0) { printf("get share memory failed\n"); return -1; } printf("get share memory sucess shmid=%d\n",shmid); system("ipcs -m"); return 0; }
创建共享内存时,使用IPC_PRIVATE宏和ftok()函数填充key值,IPC_PRIVATE创建的共享内存的key值总是0;使用ftok()函数填充key值由Linux内核分配;

对于这两种技术的出现,类似于有名管道和无名管道,IPC_PRIVATE创建的共享内存适用于父子进程之间通信,或者具有血缘关系的进程之间通信;
使用ftok()创建的key值适用于非父子进程、不具有血缘关系的进程之间通信,适用更广;
向共享内存中写入数据后,数据会一直保存在内存中,除非删除数据或者Linux内核停止重启;
读写共享内存操作:多次读操作后,数据依然在内存中
创建好的共享内存实在Linux内核空间中,如果使用write或者read操作共享内存,会多次进出内核;Linux为我们操作共享内存提供了shmat(),将Linux内核中的共享内存使用该函数映射到用户空间,因此多个进程可以在用户空间中操作共享内存;
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <errno.h> int main() { char *p; int shmid; int key; key = ftok("./a.c", 'e'); if(key < 0) { printf("create key falure\n"); return -1; } // shmid = shmget(IPC_PRIVATE, 128, 6060); shmid = shmget(key,128,606); if(shmid < 0) { printf("get share memory failed\n"); return -1; } printf("get share memory sucess shmid=%d\n",shmid); system("ipcs -m"); // shmat() p = (char*)shmat(shmid, NULL, 0); if(p == NULL) { printf("share memory function failure\n"); return -1; } // write share memory fgets(p, 128, stdin); // read share memory printf("share memory data:%s\n",p); printf("second read share memory data:%s", p); return 0; }
上面可以将共享内存映射到用户空间,可以使用shmdt()释放掉用户空间中的内存;内核中的共享内存可以使用shmctl();
shmctl():根据不同的参数选择:1、获取共享内存的属性;2、设置共享内存的属性;3、删除共享内存;
删除映射到用户空间的共享内存以及内核空间的共享内存:
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main() { char *p; int shmid; int key; key = ftok("./a.c", 'e'); if(key < 0) { printf("create key falure\n"); return -1; } // shmid = shmget(IPC_PRIVATE, 128, 6060); shmid = shmget(key,128,606); if(shmid < 0) { printf("get share memory failed\n"); return -1; } printf("get share memory sucess shmid=%d\n",shmid); system("ipcs -m"); // shmat() p = (char*)shmat(shmid, NULL, 0); if(p == NULL) { printf("share memory function failure\n"); return -1; } // write share memory fgets(p, 128, stdin); // read share memory printf("share memory data:%s\n",p); printf("second read share memory data:%s", p); // release user memory shmdt(p); //memcpy(p,"hello", 20); // 删除后再拷贝数据,出现段错误 shmctl(shmid, IPC_RMID, NULL); system("ipcs -m"); return 0; }
使用共享内存:父子进程之间通信
首先创建一个共享内存对象shmget(),将该对象内存映射到用户空间ftok(),使用fork()创建两个进程,父进程写一次数据发送一个信号SIGUSR1,并且pause()等待子进程发送来的信号SIGUSR2,父进程再写入数据;
子进程读一次共享内存后,发送一个信号SIGUSR2给父进程,然后pause()等待父进程发送信号,子进程继续运行;
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <unistd.h> #include <signal.h> void pfunc01(int num) { } void pfunc02(int num){} int main() { char *p; int shmid; int key; pid_t pid; if(key < 0) { printf("create key falure\n"); return -1; } shmid = shmget(IPC_PRIVATE, 128,IPC_CREAT | 0777); // shmid = shmget(key,128,606); if(shmid < 0) { printf("get share memory failed\n"); return -1; } printf("get share memory sucess shmid=%d\n",shmid); system("ipcs -m"); // shmat() p = (char*)shmat(shmid, NULL, 0); if(p == NULL) { printf("share memory function failure\n"); return -1; } pid = fork(); if(pid < 0){ printf("create new process failure\n"); return -1; } if(pid > 0){ // parent process printf("parent process start to write:\n"); signal(SIGUSR2, pfunc01); while(1){ printf("parent write data to share momery:\n"); fgets(p, 128, stdin); kill(pid,SIGUSR1); pause(); } } if(pid == 0){ // child process printf("child process start to read:\n"); signal(SIGUSR1, pfunc02); while(1){ printf("child start to read data from share memory:\n"); printf("child read data: %s\n",p); kill(getppid(), SIGUSR2); pause(); } } // release user memory shmdt(p); //memcpy(p,"hello", 20); // 删除后再拷贝数据,出现段错误 /// 出现段错误,是访问内存超过范围 system("ipcs -m"); return 0; }
使用共享内存:两个没有血缘关系进程之间通信
在上面的例子中,父子进程很容易获取到对象的pid号,但是没有血缘关系进程,我们需要首先对方知道对方进程的pid号码;因此需要首先发送pid号到共享内存中,对方读取后,再将自己的pid写入共享内存中,等待双方都知道后,在开始通信;
没有血缘关系的进程也可以使用共享内存通信:
server.c端,单工通信,需要先运行server端
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <signal.h> struct writeBuf{ pid_t pid; char buf[124]; }; void pfunc(int num){} int main() { int shmid; int key; pid_t pid; struct writeBuf* wBuf; key = ftok("./b.c", 'a'); if(key < 0){ printf("create key failed\n"); return -1; } printf("shmget: create key valuce is %x\n", key); shmid = shmget(key, 128, IPC_CREAT|0777); if(shmid < 0){ printf("create share memory failed\n"); return -2; } printf("get shmid is %d\n", shmid); wBuf = (struct writeBuf*)shmat(shmid,NULL,0); signal(SIGUSR1, pfunc); // write pid to share memory wBuf->pid = getpid(); printf("process pid is %d\n", getpid()); pause(); // read pid from share memory pid = wBuf->pid; // kill(pid, SIGUSR2); printf("get pid from other process %d\n", pid); while(1) { fgets(wBuf->buf,124,stdin); kill(pid,SIGUSR2); pause(); } system("ipcs -m"); return 0; }
client端
#include <stdio.h> #include <stdlib.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <signal.h> struct writeBuf{ pid_t pid; char buf[124]; }; void pfunc(int num){} int main() { int shmid; int key; pid_t pid; struct writeBuf* wBuf; key = ftok("./b.c", 'a'); if(key < 0){ printf("create key failed\n"); return -1; } printf("shmget: create key valuce is %x\n", key); shmid = shmget(key, 128, IPC_CREAT|0777); if(shmid < 0){ printf("create share memory failed\n"); return -2; } printf("get shmid is %d\n", shmid); wBuf =(struct writeBuf* )shmat(shmid,NULL,0); signal(SIGUSR2, pfunc); // read pid to share memory pid = wBuf->pid; printf("get other pid is %d\n", pid); // kill(pid, SIGUSR1); // write pid from share memory wBuf->pid = getpid(); kill(pid, SIGUSR1); printf("this processpid is %d\n", getpid()); // while(1) while(1) { // fgets(wBuf->buf,124,stdin); pause(); printf("from other process data: %s\n", wBuf->buf); kill(pid,SIGUSR1); // pause(); } system("ipcs -m"); return 0; }
2、IPC-消息队列通信
消息队列通信模型:前面提到的通信对象有共享内存,这里将共享内存对象换成消息队列。消息队列中根据消息结构体参数不同消息有类型;
查询内核中存在的消息队里使用ipcs -q,删除ipcrm -q msgid
简单使用消息队列通信:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> #include <string.h> struct msgBuf{ long type; char buf[128]; }; int main() { int msgid; int key; int ret = 0; struct msgBuf msg, msgs, msgRcv; char mBuf[64] = "hello linux msgget"; char mSnd[64] = "this is send string..."; int mLen = strlen(mBuf); int Slen = strlen(mSnd); msg.type = 10; memcpy(msg.buf, mBuf, mLen); msgs.type = 12; memcpy(msgs.buf, mSnd, Slen); //key = IPC_PRIVATE; key = ftok("./b.c", 'a'); if(key < 0){ printf("create key valuce failed\n"); return -1; } printf("create key value is %x\n",key); msgid = msgget(key, IPC_CREAT| 0777); if(msgid < 0){ printf("get message queue failure\n"); return -2; } // snd data to message queue msgsnd(msgid, (void*)&msg, mLen, IPC_NOWAIT); msgsnd(msgid, (void*)&msgs, Slen, IPC_NOWAIT); system("ipcs -q"); msgrcv(msgid, (void* )&msgRcv, 128,10,IPC_NOWAIT); printf("msgRcv data is %s\n", msgRcv.buf); memset(msgRcv.buf, 0, 64); msgrcv(msgid, (void*)&msgs, 128, 12, IPC_NOWAIT); printf("msgRcv msg snd data is %s\n", msgs.buf); ret = msgctl(msgid, IPC_RMID, NULL); if(ret < 0){ printf("delete msg id failed\n"); } printf("delete msg id success...\n"); system("ipcs -q"); return 0; }
使用消息队列通信:
server端写操作消息队列:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> #include <string.h> struct msgBuf{ long type; char buf[128]; }; int main() { int msgid; int key; int ret = 0; struct msgBuf msgSnd; msgSnd.type = 10; //key = IPC_PRIVATE; key = ftok("./b.c", 'a'); if(key < 0){ printf("create key valuce failed\n"); return -1; } //printf("create key value is %x\n",key); msgid = msgget(key, IPC_CREAT| 0777); if(msgid < 0){ printf("get message queue failure\n"); return -2; } while(1){ memset(msgSnd.buf,0, 128); printf("please input send message: \n"); fgets(msgSnd.buf, 128, stdin); // snd data to message queue msgsnd(msgid, (void*)&msgSnd, strlen(msgSnd.buf), IPC_NOWAIT); } return 0; }
client端读操作消息队列:
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> #include <string.h> struct msgBuf{ long type; char buf[128]; }; int main() { int msgid; int key; int ret = 0; struct msgBuf msgRcv; msgRcv.type = 10; //key = IPC_PRIVATE; key = ftok("./b.c", 'a'); if(key < 0){ printf("create key valuce failed\n"); return -1; } //printf("create key value is %x\n",key); msgid = msgget(key, IPC_CREAT| 0777); if(msgid < 0){ printf("get message queue failure\n"); return -2; } memset(msgRcv.buf,0, 128); // snd data to message queue msgrcv(msgid, (void*)&msgRcv, 128, 10, 0); printf("receive data from message: %s\n", msgRcv.buf); } return 0; }
3、IPC-信号灯通信
信号灯是信号量的集合;
sem_init()、sem_wait()、sem_post()
定义信号量sem_t sem、初始化信号量sem_init()、信号量P操作sem_wait()、信号量V操作sem_post();
创建信号灯:(指定参数说明信号集中信号量的个数),集合ID
semget()
删除信号量:
semctl()
#include <stdio.h> #include <sys/sem.h> #include <sys/ipc.h> int main() { int semid; semid = semget(IPC_PRIVATE, 3, 0777); if(semid<0){ printf("create sem failed\n"); return -1; } printf("create sem id %d\n", semid); system("ipcs -s"); semctl(semid, 0, IPC_RMID); // 修改信号灯semnum编号 system("ipcs -s"); return 0; }
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <semaphore.h> sem_t sem; // 信号量线程同步 void* thread_func(void* arg) { // V 操作 wait sem_wait(&sem); int i=0; for(i=0;i<10;i++){ usleep(100); printf("new thread running %d...\n",i); } return NULL; } int main() { pthread_t tid; int ret = 0; int j=0; void* retVal; // 信号量初始化 sem_init(&sem, 0, 0); // pshared非0用于进程之间同步,信号量初始化为0 ret = pthread_create(&tid, NULL,thread_func, NULL); if(ret < 0){ printf(" create new thread failed\n"); return -1; } for(j=0;j< 10;j++){ usleep(100); printf("main thread running %d...\n", j); } // V操作 sem_post(&sem); pthread_join(tid, &retVal); return 0; }

浙公网安备 33010602011771号