目录
一、fork 和 vfork区别
fork()父子进程的执行次序不确定。
fork()子进程拷贝父进程的数据段和代码段,这里通过拷贝页表实现。
vfork()子进程与父进程共享地址空间,无需拷贝页表,效率更高。
vfork()保证子进程先运行,在调用 exec 或 exit 之前与父进程数据是共享的。父进程在子进程调用 exec 或 exit 之后才可能被调度运行,如果在调用这两个函数之前子进程依赖于父进程的进一步动作,则会导致死锁。
二、vfork的创建
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
函数作用:
创建一个子进程,子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的)
特点:
1、子进程共享父进程的地址空间(准确来说,在调用 exec(进程替换) 或 exit(退出进程) 之前与父进程数据是共享的)
2、一定是子进程先运行,而且是等子进程结束之后,父进程才开始运行
3、当子进程调用exit之后,父进程才会往下执行
4、你在引用的时候,最好尽快结束子进程
5、用 vfork() 创建进程,子进程里一定要调用 exec(进程替换) 或 exit(退出进程),否则程序则会导致死锁,是有问题的程序,没有意义。
例子:
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int a = 100;
//创建一个子进程
pid_t id = vfork();
if(id == -1)
{
printf("fork error\n");
return -1;
}
else if(id >0)//父进程
{
printf("a1=%d\n",a);
//休眠
wait(NULL);
printf("a3=%d\n",a);
}
else if(id == 0)//子进程,vfork是先执行子进程先
{
a = 250; //父进程修改变量a的值
printf("a2=%d\n",a);
sleep(1);
exit(0);//让子进程到这里就结束
}
return 0;
}
三、进程之间通信方式
1、 为什么要实现进程之间的通信
例如:
./project -> 开启了一个名字叫project的进程。
./test -> 开启了一个名字叫test的进程。
通过学习进程之间的通信,使得不同的进程之间能够实现数据的交换,例如test进程发送数据给project进程,project进程收到数据之后,根据数据做出相应的事情。 (test进程控制project进程)
2、在linux下,进程之间通信方式和特点有哪些
以下几种进程之间的通信有一个共通的特点,都是只能在同一台主机内部的进程使用。
1)管道通信。
管道通信分为有名管道(类型:p)与无名管道,管道是一个特殊的文件,进程通过将数据写入到管道中,另外一个进程从管道中读取数据出来。
2)信号
在linux下,有非常多的信号,例如:暂停,继续,停止...,某一个进程通过发送信号给另外一个进程,从而控制另外一个进程的运行状态。
3)消息队列
某一个进程把消息发送到队列上,另外一个进程就可以读取队列上的数据,消息队列好处:进程可以读取队列上某一个特定的数据。
4)共享内存
多个进程访问同一片内存空间。
四、进程间通信--无名管道
1、创建无名管道
无名管道只能作用于亲缘关系的进程之间的通信,例如父子进程。无名管道就是一个没有名字的管道文件,相当于一个队列结构,fd[1]为写入端(入队),fd[0]为读出端(出队)。其中信息读出后即删除,再次读取时即为下一个信息。
#include <unistd.h>
int pipe(int pipefd[2]); -> 执行这个函数之后,得到两个文件描述符
函数作用:
创建一个无名管道文件
参数:
pipefd 一个具有2个int类型变量的数组。
返回值:
成功:0 失败:-1
2、程序测试
例子(实现两个进程间的通信):
#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
int main()
{
int fd[2],fd1[2];
//创建一个无名管道文件,pipefd[0] 读端 pipefd[1] 写端
pipe(fd);
pipe(fd1);
//创建一个子进程
pid_t id = fork();
if(id == -1)
{
perror("failed");
return -1;
}
//父进程
else if(id > 0)
{
char buf[512]={0};
char buf1[512]={0};
close(fd[0]); //关闭管道的读
close(fd1[1]); //关闭另一个管道的写
while(1)
{
//等待输入
printf("父亲说:");
scanf("%s",buf);
//写进管道
write(fd[1],buf,sizeof(buf));
//判断如果对于quit则退出
if(strcmp(buf, "quit") == 0)
{
break;;
}
//清空
bzero(buf,0);
//读取管道
read(fd1[0],buf1,sizeof(buf));
printf("父亲听到:%s\n",buf1);
//清空
bzero(buf1,0);
}
//关闭无名管道
close(fd[1]);
close(fd1[0]);
//进程休眠
wait(NULL);
}
//子进程
else if(id == 0)
{
close(fd[1]); //关闭管道的写
close(fd1[0]); //关闭别一个管道的读
char buf[512]={0};
char buf1[512]={0};
sleep(1);
while(1)
{
//清空
bzero(buf,0);
//读取管道
read(fd[0],buf,sizeof(buf));
printf("儿子听到:%s\n",buf);
//判断如果对于quit则退出
if(strcmp(buf, "quit") == 0)
{
break;;
}
//清空
bzero(buf,0);
//等待输入
printf("儿子说:");
scanf("%s",buf1);
//写入管道
write(fd1[1],buf1,sizeof(buf));
//清空
bzero(buf1,0);
}
//关闭无名管道
close(fd[0]);
close(fd1[1]);
//退出进程
exit(0);
}
return 0;
}
结果:
五、进程间通信--有名管道
1、创建有名管道
有名管道文件就是一个有名字的管道文件。在linux下,所有的进程都是可以看到这个文件,所以有名管道作用范围是整个linux系统下任意的两个进程。
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
函数作用:
创建一个有名管道文件
参数:
pathname 有名管道文件的路径+名字。 例如: /home/gec/fifo_test
mode 管道文件的权限 0777
返回值:
成功:0 失败:-1
2、程序测试
例子(实现两个进程间的通信):
写入数据.c
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_FILE "/home/gec/fifo1"
int main(void)
{
//先判断文件是否存在,如果存在则不用创建了
if(access(FIFO_FILE, F_OK) == -1)//access 判断文件是否存在,如果不存在则返回 -1
{
//创建一个有名管道文件
if(mkfifo(FIFO_FILE,0777) == -1)
{
perror("mkfifo error");
return -1;
}
}
//发送数据 ,往有名管道文件中写入数据
//1、打开有名管道文件
int fd = open(FIFO_FILE,O_RDWR);
if(fd == -1)
{
perror("open fifo error");
return -1;
}
//2、写入数据
while(1)
{
printf("请输入数据:");
char sendbuf[1024]={0};
scanf("%s",sendbuf);
write(fd,sendbuf,strlen(sendbuf));
if(strcmp(sendbuf,"exit") == 0)
break;
}
//3、关闭文件
close(fd);
return 0;
}
读取数据.c
#include<stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define FIFO_FILE "/home/gec/fifo1"
int main()
{
//先判断文件是否存在,如果存在则不用创建了
if(access(FIFO_FILE, F_OK) == -1)//access 判断文件是否存在,如果不存在则返回 -1
{
//创建一个有名管道文件
if(mkfifo(FIFO_FILE,0777) == -1)
{
perror("mkfifo error");
return -1;
}
}
//从管道文件中读取数据,并且打印出来
//1、打开有名管道文件
int fd = open(FIFO_FILE,O_RDWR);
if(fd == -1)
{
perror("open fifo error");
return -1;
}
//2、从管道文件中读取数据
while(1)
{
char recvbuf[1024]={0};
read(fd,recvbuf,sizeof(recvbuf));
printf("recvbuf:%s\n",recvbuf);
if(strcmp(recvbuf,"exit") == 0)
break;
}
//3、关闭文件
close(fd);
六、总结
1.管道是创建在内存中,进程结束空间释放,管道不复存在。
2.无名管道和有名管道都是半双工通信,实现双向通信需要建立两个管道。
3.无名管道是linux特殊文件。
4.无名管道只用于父子进程之间,有名管道可用于任意两个进程之间。