进程间通信
- ipc:interprocess communication
通信方式
-
管道通信
- 无名管道:通过pipe创建出来的管道,只能在父子进程或子进程间使用,创建该管道的进程一旦结束,则该无名管道也会销毁
- 父进程输出关闭子进程的输出,打开输入。子进程输出关闭父进程的输出,打开输入。
- 父进程与子进程之间的通信
#include <unistd.h>//pipe #include <stdio.h> #include <string.h> //通过pipe创建的管道属于无名管道 //只能在父子进程或子进程间使用 //创建该管道的进程一旦结束,则该无名管道也会销毁 int main() { int pipefd[2]={-1};//管道文件描述符 int ret=-1; ret=pipe(pipefd);//创建的管道是位于内核空间的,管道两端的描述符存储到pipe数组 //pipefd[0]表示数据流出段,可以从此端读取数据 //pipefd[1]表示数据进入段,可以从此端写入数据 if(ret==-1)//创建管道失败 { perror("pipe"); return -1; } //创建一个进程 pid_t pid=-1; //管道的创建是创建在内核中,不属于独立进程 //fork产生的子进程是不会再创建一个管道 //只是对管道文件进行了一次拷贝 pid=fork(); if(pid>0)//parent { int iSign=0; char caBuf[32]={'\0'}; while(1) { memset(caBuf,'\0',sizeof(caBuf)); if(iSign==0) { printf("parent input data\n"); scanf("%s",caBuf); write(pipefd[1],caBuf,sizeof(caBuf)); iSign=1; sleep(1); } else if(iSign==1) { read(pipefd[0],caBuf,sizeof(caBuf)); printf("child says:%s\n",caBuf); iSign=0; } } } else if(pid==0)//child { int iSign=1; char caBuf[64]={'\0'}; while(1) { memset(caBuf,'\0',sizeof(caBuf)); if(iSign==1) { read(pipefd[0],caBuf,sizeof(caBuf)); printf("parent says:%s\n",caBuf); iSign=0; } else if(iSign==0) { printf("child input data\n"); scanf("%s",caBuf); write(pipefd[1],caBuf,sizeof(caBuf)); iSign=1; sleep(1); } } } else if(pid==-1)//fork failed { perror("fork"); return -1; } return 0; }
- 子进程与子进程之间的通信
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/wait.h> int main() { int pipefd[2]={0}; int ret=-1; ret=pipe(pipefd); if(ret==-1) { perror("pipe"); return -1; } pid_t pid=fork(); if(pid>0) { pid_t pid2=fork(); if(pid2>0) { waitpid(pid,NULL,0); waitpid(pid2,NULL,0); } else if(pid2==0) { int iSign=1; char caBuf[64]={'\0'}; while(1) { memset(caBuf,'\0',sizeof(caBuf)); if(iSign==1) { read(pipefd[0],caBuf,sizeof(caBuf)); printf("child2 says:%s\n",caBuf); iSign=0; } else if(iSign==0) { printf("child1 input data\n"); scanf("%s",caBuf); write(pipefd[1],caBuf,sizeof(caBuf)); iSign=1; sleep(1); } } } else if(pid2==-1) { perror("first fork"); return -1; } } else if(pid==0) { int iSign=0; char caBuf[64]={'\0'}; while(1) { memset(caBuf,'\0',sizeof(caBuf)); if(iSign==0) { printf("child2 input data\n"); scanf("%s",caBuf); write(pipefd[1],caBuf,sizeof(caBuf)); iSign=1; sleep(1); } else if(iSign==1) { read(pipefd[0],caBuf,sizeof(caBuf)); printf("child1 says:%s\n",caBuf); iSign=0; } } } else if(pid==-1) { perror("first fork"); return -1; } return -1; }
- 命名管道:
- write fifo
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define FIFO_PATHNAME "myFifo" int main() { int ret=-1; ret=mkfifo(FIFO_PATHNAME,0777); if(ret==-1) { if(errno!=EEXIST) { perror("mkfifo"); exit(EXIT_FAILURE); } } printf("fifo is ok\n");//管道创建成功 int fd=-1; fd=open(FIFO_PATHNAME,O_WRONLY);//只写打开 if(errno!=EEXIST) { perror("mkfifo"); exit(EXIT_FAILURE); } ret=write(fd,"hello world",11); if(ret>0) { printf("write %d bytes to fifo success\n",ret); } close (fd); //当没有文件进行读的时候就会阻塞在那里 return 0; }
- read fifo
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #define FIFO_PATHNAME "myFifo" int main() { int ret=-1; ret=mkfifo(FIFO_PATHNAME,0777); if(ret==-1) { if(errno!=EEXIST) { perror("mkfifo"); exit(EXIT_FAILURE); } } printf("fifo is ok\n");//管道创建成功 int fd=-1; fd=open(FIFO_PATHNAME,O_RDONLY);//只写打开 if(errno!=EEXIST) { perror("mkfifo"); exit(EXIT_FAILURE); } char caBuf[64]={'\0'}; ret=read(fd,caBuf,sizeof(caBuf)); if(ret>0) { printf("read %d bytes to fifo success\n",ret); } close (fd); printf("%s",caBuf); //当没有文件进行读的时候就会阻塞在那里 return 0; }
- 无名管道:通过pipe创建出来的管道,只能在父子进程或子进程间使用,创建该管道的进程一旦结束,则该无名管道也会销毁
-
共享内存:
-
定义:共享内存是被多个进程共享的一部分物理内存。共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
- int shmget(key_t key, size_t size, int shmflg);
- 第一个参数:共享内存块的名字
- 第二个参数:这块共享内存的大小
#include <sys/ipc.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <stdlib.h> int main() { int shmid=-1; //int shmget(key_t key, size_t size, int shmflg); //第一个参数:内存块名字 //第二个参数:共享内存的大小 shmid=shmget(0x1024,1024,IPC_CREAT|S_IRUSR|S_IWUSR);//内存块名字,内存块大小,创建 if(shmid==-1) { perror("shmget"); exit(EXIT_FAILURE); } printf("shmget ok=%d\n",shmid); return 0; }
向内存存储数据
void *pRet=NULL; pRet=shmat(shmid,NULL,0);//返回内存地址 if((void *)-1==pRet)//返回值类型就是(void*)-1 { perror("shmat"); exit(EXIT_FAILURE); } char *pData = "I want my tears back"; memcpy(pRet,pData,strlen(pData)); shmdt(pRet);//斩断关联 return 0;
读取内存中数据
void *pRet=NULL; pRet=shmat(shmid,NULL,SHM_RDONLY);//0:读写,SHM_RDONLY:读,没有只写 if((void *)-1==pRet)//返回值类型就是(void*)-1 { perror("shmat"); exit(EXIT_FAILURE); } char caBuf[32]={'\0'}; memset(caBuf,'\0',sizeof(caBuf)); //数据读取之后任然留在共享内存里面 //直到下次写数据时被覆盖 memcpy(caBuf,pRet,sizeof(caBuf)); printf("%s\n",caBuf); struct shmid_ds shmInfo; shmctl(shmid,IPC_STAT,&shmInfo); printf("shm size=%ld\n",shmInfo.shm_segsz); shmdt(pRet);//斩断关联
释放内存空间
shmctl(shmid,IPC_RMID,NULL);//RMID表示删除 //shmdt(pRet);//斩断关联
-
内存映射
- 定义:mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int fd=-1;
fd=open("test",O_RDWR);
if(fd==-1)
{
perror("open");
exit(-1);
}
//映射
void *pRet=NULL;
pRet=mmap(NULL,32,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//最后一个偏移量
if((void *)-1==pRet)
{
perror("mmap");
exit(-1);
}
//将数据格式化的保存到字符数组里去
sprintf((char*)pRet,"%s %d %.2f","老司机开车拉",1024,3.14);//(char*)pRet用在pRet的空间用char型保存
munmap(pRet,32);
return 0;
}
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
int fd=-1;
fd=open("test",O_RDWR);
if(fd==-1)
{
perror("open");
exit(-1);
}
//映射
void *pRet=NULL;
pRet=mmap(NULL,32,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);//最后一个偏移量
if((void *)-1==pRet)
{
perror("mmap");
exit(-1);
}
//将数据格式化的保存到字符数组里去
char caBuf[32]={'\0'};
int iData=0;
float fData=0;
sscanf(pRet,"%s%d%f",caBuf,&iData,&fData);
printf("%s %d %.2f\n",caBuf,iData,fData);
munmap(pRet,32);
close(fd);
return 0;
}
- 信号:signal
- kill:发送一个信号到进程里去
-
kill -l
- 前32个信号非可靠信号,后32个可靠信号
-
- 信号的产生:分为硬件产生的信号和软件产生的信号。
- 平时用的kill -9 就是SIGKILL
/*kill()*/
#include <sys/types.h>
#include <signal.h>
/*fork()*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
pid_t pid=-1;
pid=fork();
if(pid>0)
{
while(1)
{
printf("come on!kill me please!\n");
sleep(1);
}
}
else if(pid==0)
{
int i=5;
while (1)
{
if(i==0)
{
printf("time for killing\n");
kill(getppid(),SIGKILL);//getppid()得到父进程进城号
break;
}
else
{
printf("still have %d sec to kill that sb\n",i);
sleep(1);
}
i--;
}
}
else if(pid==-1)
{
perror("fork");
exit(-1);
}
}
- alarm信号:闹钟
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//在指定的秒数给本进程发送一个SIGALRM信号
//该信号的默认处理是结束进程
alarm(5);
while(1)
{
printf("hahahahahahahahah...\n");
sleep(1);
}
return 0;
}
- raise
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
int main()
{
int i=5;
//在指定的秒数给本进程发送一个SIGALRM信号
//该信号的默认处理是结束进程
while(1)
{
if(i==0)
{
raise(SIGKILL);
}
else
{
printf("kill me ...\n");
}
i--;
sleep(1);
}
return 0;
}