linux进程通信
1.无名管道
1.无名管道是基于文件描述符的通信方式。当创建一个管道(用pipe创建)时会创建两个文件描述符fd[0](读管道)、fd[1](写管道)并且打开了这两个读、写管道。这里规定了fd[0]为读管道。fd[1]写管道。当两个血缘关系的进程通信时他们的文件描述符是一样的并且是同步的要协调工作 。管道通信不能一端同时又读又写。
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret=pipe(fd); //创建无名管道
if(ret<0)
{
perror("创建管道失败");
return -1;
}
pid_t id=fork(); //创建子进程
if(id<0)
{
perror("创建子进程失败");
return -1;
}
if(id==0) //子进程执行
{
char buff[127]="";
int len=read(fd[0],buff,sizeof(buff)-1); //如果没有数据会阻塞
printf("%d--%s\n",len,buff);
}
if(id>0)//父进程执行
{
const char *str="hello world";
int len=write(fd[1],str,strlen(str));
if(len>0)
printf("%d\n",len);
wait(NULL); //等待子进程退出
close(fd[0]); //关闭这两个文件描述符
close(fd[1]);
}
return 0;
}
无名管道读特征:写入一次,读多次,会边读边清除。写多次,读一次: 写特征:追加写。读时能不能关闭写,写时能不能关闭读 管道通信必须同步
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret=pipe(fd); //创建无名管道
if(ret<0)
{
perror("创建管道失败");
return -1;
}
pid_t id=fork(); //创建子进程
if(id<0)
{
perror("创建子进程失败");
return -1;
}
if(id==0) //子进程执行
{
char buff[127]="";
int len=read(fd[0],buff,sizeof(buff)-1); //如果没有数据会阻塞
printf("%d--%s\n",len,buff);
// memset(buff,0,sizeof(buff)); //清空数组 //无名管道读特征:写入 一次,读多次,1)边读边清除,
// len=read(fd[0],buff,sizeof(buff)-1); //如果没有数据会阻塞
// printf("%d--%s\n",len,buff);
}
if(id>0)//父进程执行
{
const char *str="helloworld";
int len=write(fd[1],str,strlen(str));
if(len>0)
printf("%d\n",len); //写多次,读一次: 写特征:追加写
len=write(fd[1],str,strlen(str));
if(len>0)
printf("%d\n",len);
wait(NULL); //等待子进程退出
close(fd[0]); //关闭这两个文件描述符
close(fd[1]);
}
return 0;
}
先子进程读当有写端打开时会一直阻塞直到父进程写入
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret=pipe(fd); //创建无名管道
if(ret<0)
{
perror("创建管道失败");
return -1;
}
pid_t id=fork(); //创建子进程
if(id<0)
{
perror("创建子进程失败");
return -1;
}
if(id==0) //子进程执行
{
char buff[127]="";
int len=read(fd[0],buff,sizeof(buff)-1); //如果没有数据会阻塞
printf("%d--%s\n",len,buff);
}
if(id>0)//父进程执行
{
sleep(1); //先让子进程执行
const char *str="helloworld";
int len=write(fd[1],str,strlen(str));
if(len>0)
printf("%d\n",len);
wait(NULL); //等待子进程退出
close(fd[0]); //关闭这两个文件描述符
close(fd[1]);
}
return 0;
}
2.进程的互斥问题–信号量解决
信号量作用解决多个进程对同一个资源同时访问的竞争问题。信号量(信号灯)非负整数,表示同时间能访问资源的进程数或线程数 。
信号量使用步骤:
1.ftok();获取系统中唯一的key
函数原型: key_t ftok(const char *pathname, int proj_id)
函数功能:获取系统中唯一的key:文件的设备编号和节点与子序号产生唯一 的key。
函数参数:参数1:路径 参数2:子序号(0~127)
函数返回值:On success, the generated key_t value is returned. On failure -1 is returned。
2.创建或获取一个信号量数组
函数原型:int semget(key_t key, int nsems, int semflg);
函数功能:创建或获取一个信号量数组(如果存在就获取,不存在就创建)
函数参数:参数1:唯一key 参数2:信号量数组中信号量的个数 参数3:标志位
函数返回值:成功返回信号量数组标识 ,否则-1
3.semctl();初始化信号量
函数原型:int semctl(int semid, int semnum, int cmd, …) …可变成参数
函数功能:初始化信号量
函数参数:参数1:信号量数组标识 参数2:要操作的信号量在信号量数组的下标 参数3:一些命令如:GETVAL获取信号量值,GETPID获取当前进程号
SETVAL设置信号量值。 根据第三个参数进行第四个参数的选择
函数返回值:成功根据cmd返回不同的值 ,否则-1。
4.semop进行P操作
函数原型: int semop(int semid, struct sembuf *sops, size_t nsops)
函数功能:P操作
函数参数:参数1:信号量数组标识 参数2:结构体数组struct sembuf 参数3:要操作的信号量个数
函数返回值:成功信号量标识符 ,否则-1。
struct sembuf{
unsigned short sem_num; //信号量下标
short sem_op; // -1 p操作, 1:v操作
short sem_flg; // 0默认值
};
5.semop进行V操作
semop有阻塞功能当为0时不能进行P操作
就把struct sembuf.sem_num=1即可。
6. semctl(ms_arr,0,IPC_RMID); //删除信号量数组
把信号量用到父子进程无名管道通信中
#include<stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include<stdlib.h>
union semun {
int val;
};
#include <sys/types.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret=pipe(fd); //创建无名管道
if(ret<0)
{
perror("创建管道失败");
return -1;
}
key_t key=ftok(".",100); //获取系统中唯一的key
if(key<0)
{
perror("获取key值失败");
return -1;
}
int ms_arr=semget(key,1,IPC_CREAT|0600); //创建或获取一个信号量数组
if(ms_arr<0)
{
perror("创建或获取一个信号量数组失败");
return -1;
}
union semun sem={0};
sem.val=1; //设置信号量初始化值为1
if(semctl(ms_arr,0,SETVAL,sem)<0) //初始化信号量
{
perror("初始化信号量失败");
semctl(ms_arr,0,IPC_RMID); //删除信号量数组
return -1;
}
pid_t id=fork(); //创建子进程
if(id<0)
{
perror("创建子进程失败");
return -1;
}
struct sembuf semb;
semb.sem_flg=0;
semb.sem_num=0;
if(id==0) //子进程执行
{
char buff[127]="";
semb.sem_op=-1; //p操作
semop(ms_arr,&semb,1);
int len=read(fd[0],buff,sizeof(buff)-1); //如果没有数据会阻塞
printf("%d--%s\n",len,buff);
semb.sem_op=1;//v操作
semop(ms_arr,&semb,1);
}
if(id>0)//父进程执行
{
const char *str="helloworld";
semb.sem_op=-1; //p操作
semop(ms_arr,&semb,1);
int len=write(fd[1],str,strlen(str));
if(len>0)
printf("%d\n",len);
semb.sem_op=1;//v操作
semop(ms_arr,&semb,1);
wait(NULL); //等待子进程退出
semctl(ms_arr,0,IPC_RMID); //读完后删除信号量数组
close(fd[0]); //关闭这两个文件描述符
close(fd[1]);
}
return 0;
}
3.有名管道
1.有名管道可以使用mkfifo函数创建。当成功后就可以使用open、read、write进行操作。对于为读打开的设置O_RDONLY,对于为写打开的设置为O_WRONLY。
#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
unlink("fofo");
int ret=mkfifo("fofo",0666); //创建有名管道 参数1:管道名 参数2:权限
char buff[1024]={0};
int fd,fd1;
int flag=0;
if(ret<0)
{
perror("失败\n");
return -1;
}
pid_t id=fork();
if(id<0)
{
perror("创建失败\n");
exit(-1);
}
if(id==0)
{
fd1=open("fofo",O_RDONLY);//只读
if(fd<0)
{
perror("打开失败");
exit(-1);
}
int ret=read(fd1,buff,sizeof(buff));
printf("%d\n",ret);
printf("%s\n",buff);
close(fd1);
}
if(id>0)
{
fd=open("./fofo",O_WRONLY);//只写
if(fd<0)
{
perror("打开失败");
exit(-1);
}
write(fd,"子进程你好",strlen("子进程你好"));
close(fd);
}
wait(NULL);
printf("-------\n");
return 0;
}