进程间通信方式:IPC(管道、共享内存、信号量) 、信号。
IPC:
管道:分为标准流管道、无名管道、有名管道。
共享内存:将两个进程的虚拟地址空间。映射到同一块物理内存。
信号量:
信号:约定好了,什么信号干什么事。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
管道分:
1.标准流管道
2.无名管道
3.命名管道(有名管道)
1.标准流管道(用的比较少)
FILE* popen(const char* command, const char* open_mode);
popen
允许一个程序将另一个程序作为新进程来启动。command字符串是要运行的程序名(即,被调用程序)。open_mode必须是“r”或“w”。调用popen函数,会做两件事:
1.创建新的进程command。2.在程序和被调用程序之间建立一条标准流管道。
如果open_mode是“r”:则调用函数对管道就是读,被调用函数对管道就是写,并且把
被调用函数的标准输出重定向到了写端。
如果open_mode是“w”:则调用函数对管道就是写、被调用函数对管道就是读,并且把
被调用函数的标准输入重定向到了读端。
例1:
第一步:vim printf.c
int main(){
printf("Hello world\n");
//输出到标准输出上。因为这里printf是popen新建的进程。而新创建的进程的
标准输出重定
向到了
写端。所以也就是往管道里输出了字符串。
return 0;
}
第二步:gcc printf.c -o printf
//这就是要执行的程序
第三步:vim popen_r.c
int main(int argc,char *argv[]){
if(argc!=2){
printf("error args\n");
return -1;
}
FILE * fp=
popen(argv[1],"r");
//将argv[1]作为新的进程来启动。即,新建的进程为:printf。
if(NULL==fp){
perror("popen");
return -1;
}
char buf[128]={0};
fread(buf,sizeof(buf),sizeof(buf),fp);
//读管道的东西到buf中。新建的进程printf向标准输出写了"Hello world\n",而新建的进程的
标准输出重定向到了管道。所以读管道的内容就是"Hello world\n"。
printf("%s",buf);
//打印出读出的内容:Hello world
fclose(fp);
return 0;
}
第四步:gcc popen_r.c -o popen_r
第五步:./popen_r ./printf //将命令"./printf"作为参数传进去。
例2:
第一步:vim fgets.c
int main(){
char buf[128]={0};
fgets(buf,sizeof(buf),stdin);
printf("%s\n",buf);
return 0;
}
第二步:gcc fgets.c fgets
第三步:vim popen_w.c
int main(int argc,char* argv[])
{
if(argc !=3){
printf("error args\n");
return -1;
}
FILE* fp;
fp=popen(argv[1],"w");
//打开一个新的进程argv[1],并建立一条管道。自己是写端。被调用进程是读端。被调用进程的
标准输入重定向到了管道。即,从标准输入读就是从管道读。
if(NULL==fp){
perror("popen");
return -1;
}
fwrite(argv[2],sizeof(char),strlen(argv[2]),fp);
//往管道fp中写argv[2].
fclose(fp);
//写完之后,如果还没来得及读就关闭了管道,没有关系。但是这样管道不能正常退出。
return 0;
}
第四步:gcc popen_w.c -o popen_w
第五步:./popen_w ./fgets hello
2.无名管道
无名管道只能在亲缘关系进程间通信(父子或兄弟)。
int fds[2];pipe(fds);后内核里面只有一条管道,父进程和子进程同时各有了一个管道的读端和写端。但是:每个进程只能用其中的一个。要么:父进程从fd[1]写,子进程从fd[0]读。要么子进程从fd[1]写,父进程就从fd[0]读。
例:
int fds[2];
pipe(fds);
//新建了一条管道,并把读端和写端打开。
if(!fork()){
//子进程
close(fds[1]);
//fds[0]是读,fds[1]是写。子进程关闭写端。对于新创建的
char buf[128]={0};
read(fds[0],buf,sizeof(buf));
//从读端读
printf("%s\n",buf);
close(fds[0]);
//关闭读端
exit(0);
}else{
//父进程
close(fds[0]);
//父进程关闭读端。
write(fds[1],"Hello",5);
//向写端写。
wait(NULL);
//防止子进程变成僵尸进程或孤儿进程
close(fds[1]);
//关闭写端
return 0;
}
3.命名管道(有名管道):
有名管道的使用跟前面学习的管道文件一样,只不过是用函数创建的管道。
例:创建有名管道:
int main(){
int ret=
mkfifo("1.fifo",0666);
if(-1==ret){
perror("mkfifo");
return -1;
}
return 0;
}