笔记
进程
三.进程间通信(IPC)
3.1进程间通信的引入
1> 对于多个线程之间通信,我们可以使用临界资源来完成,通过一个线程任务对临界资源进行修改,另一个线程也可以使用已经修改过的临界资源,但是要注意使用同步互斥机制完成对临界资源的保护
2> 多个进程之间不能使用全局变量来进行进程间通信,多个进程之间的用户空间是相互独立的,每个进程的全局变量是各个进程独立拥有的,更改一个进程的全局变量,另一个进程不会受到影响,故而不能使用全局变量来完成通信
3> 使用文件来完成进程间通信是可行的,但是要求写进程先完成向文件中写入数据,然后读进程才能读取数据,必须要加入进程的同步操作
4> 多个进程之间内核空间是共享的,我们可以通过将数据放入到内核空间,来完成两个进程之间信息的交流
5> 进程间通信方式:
1、内核提供的原始通信方式
无名管道 有名管道 信号
2、system V提供三种
消息队列 共享内存 信号量集
3.2无名管道
1> 顾名思义就是没有名字的管道文件,所以只能适用于亲缘进程间的通信
2> 原理:通过内核,在内存中创建出一个管道文件,存放在内存空间,并不在文件系统中真实存在。当打开该文件时,会返回该文件的两个文件描述符,分别对应读端和写端。可以通过读端从管道中读取数据,从写端向管道中写入数据。当该文件的读写两端全部被关闭后,该管道文件会在内存中消失
3> 注意:由于打开管道时返回的是文件描述符,所以,对该文件的操作,只能使用文件IO
对于存入管道中的数据的读取是一次性的,当数据读取结束后,数据就不存在于管道文件中了
4> 无名管道的API接口函数
#include <unistd.h>
int pipe(int pipefd[2]);
功能:在通过内核在内存中创建一个无名管道,并通过参数将该管道文件的两个文件描述符返回
参数:接收文件描述符的数组,pipefd[0]表示管道文件的读端,pipefd[1]表示管道的写端
返回值:成功返回0,失败返回-1并置位错误码
3.3有名管道
1> 顾名思义就是有名字的管道文件,会在文件系统中创建一个有名字的管道文件
2> 可以用于亲缘进程间的通信,也可以用于非亲缘进程间的通信
3> 有名管道的API函数
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
功能:在文件系统中创建一个有名管道,但是并没有打开该文件
参数1:有名管道的名称
参数2:管道文件的权限
返回值:成功返回0,失败返回-1并置位错误码
3.4信号通信
1> 信号通信原理图:两个异步通信的进程之间,通过发送相关信号,完成任务间的通信
2> 信号是linux中软件模拟硬件的“中断”的一种方式。信号是软件部分的名词,中断是硬件部分的名词
3> 能够发送的信号可以通过指令:kill -l进行查看
4> 对于信号的处理方式有三种:默认、捕获、忽略
有两个信号既不能被捕获,也不能被忽略:SIGKILL、SIGSTOP
5> 上面的信号触发条件以及默认处理方式可以通过指令 man 7 signal进行查看
6> 信号的发送者可以是内核、其他进程、用户自己
7> 将信号与信号处理方式连接函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:将信号与信号处理函数绑定到一起
参数1:要绑定的信号
参数2:信号处理函数
SIG_IGN:表示忽略信号
SIG_DFL:表示默认处理
填自定义函数的入口地址
返回值:成功返回处理方式的起始地址,失败返回 SIG_ERR
3.5特殊的信号处理
1> SIGCHLD信号:以非阻塞的形式回收僵尸进程
#include<myhead.h>
//自定义信号处理函数
void handler(int signo)
{
if(signo == SIGCHLD)
{
while(1)
{
//判断是否将僵尸进程全部回收了
if(waitpid(-1, NULL, WNOHANG) == 0)
{
break;
}
}
}
}
/******************主程序**********************/
int main(int argc, const char *argv[])
{
//将SIGCHLD信号与信号处理函数绑定到一起
if(signal(SIGCHLD, handler) == SIG_ERR)
{
perror("signal error");
return -1;
}
for(int i=0; i<10; i++)
{
if(fork() == 0)
{
exit(EXIT_SUCCESS);
}
}
while(1);
/*
for(;;)
{
printf("我是小妖怪,逍遥游自在\n");
sleep(1);
}*/
return 0;
}
2> SIGALRM:定时器信号
程序允许启动一个定时器,当所定的时间到位后,会发送一个SIGALRM信号,我们可以将该信号绑定到对应的信号处理函数中,从而导致,给定时间后,处理自定义函数。该信号需要使用一个函数来发送超时信号:alarm闹钟函数
#include <unistd.h>
unsigned alarm(unsigned seconds);
功能:给进程设置一个定时器,以秒为单位,当定时器到位后,后向该进程发送一个SIGALRM的信号
参数:秒数,如果参数设置成0,表示删除定时器
返回值:>0:表示返回的上一个定时器剩余的秒数,并且重置上一个定时器
0:表示之前没有设置定时器
#include<myhead.h>
//定义信号处理函数
void handler(int signo)
{
if(signo == SIGALRM)
{
printf("阎王叫你三更死,怎可留人到五更\n");
sleep(2);
exit(EXIT_SUCCESS);
}
}
/********************主程序******************/
int main(int argc, const char *argv[])
{
//将SIGALRM信号与信号处理函数绑定
if(signal(SIGALRM, handler) == SIG_ERR)
{
perror("signal error");
return -1;
}
printf("%d\n", alarm(10)); //0
sleep(5);
printf("%d\n", alarm(10)); //5
int count = 0;
while(1)
{
printf("能活一秒是一秒\n");
sleep(1);
/*
count++;
if(count == 3)
{
alarm(0); //删除定时器
}
*/
}
return 0;
}
3> 信号的发送函数
#include <signal.h>
int kill(pid_t pid, int sig);
功能:向指定的进程发送指定的信号
参数1:要发送的进程号
>0:表示向指定的进程发送信号
=0:表示向某个进程组(当前进程所在的进程组)中发送信号
=-1:向所有进程发送信号
<-1:表示向其他进程组(组id为给定pid的绝对值)发送信号
参数2:要发送的信号号
返回值: 成功返回0,失败返回-1并置位错误码
#include <signal.h>
int raise(int sig);
功能:向自己所在的进程发送指定的信号
参数:要发送的信号
返回值:成功返回0,失败返回非0数字
作业
1> 使用有名管道实现,一个进程用于给另一个进程发消息,另一个进程收到消息后,展示到终端上,并且将消息保存到文件上一份
#include<myhead.h>
int main(int argc, char const *argv[])
{
if(mkfifo("./fifo",0664)==-1)
{
perror("mkfifo error");
return -1;
}
return 0;
}
#include<myhead.h>
int main(int argc, char const *argv[])
{
int fp=-1;
if((fp=open("./fifo",O_WRONLY))==-1)
{
perror("open fp error");
return -1;
}
char buf[128]="";
printf("对方已上线\n");
while(1)
{
printf("请输入:\n");
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1]=0;
write(fp,buf,strlen(buf));
if(strcmp(buf,"quit")==0)
{
break;
}
}
close(fp);
return 0;
}
#include<myhead.h>
int main(int argc, char const *argv[])
{
int fp=-1;
int fd=-1;
if((fp=open("./fifo",O_RDONLY))==-1)
{
perror("open fp error");
return -1;
}
if((fd=open("./1.txt",O_WRONLY|O_APPEND|O_CREAT,0664))==-1)
{
perror("open fd error");
return -1;
}
char buf[128]="";
printf("对方已上线\n");
while(1)
{
bzero(buf,sizeof(buf));
read(fp,buf,sizeof(buf));
if(strcmp(buf,"quit")==0)
{
break;
}
printf("输出:%s\n",buf);
fflush(stdout);
write(fd,buf,strlen(buf));
}
close(fp);
close(fd);
return 0;
}
2> 使用有名管道实现两个进程间相互通信
#include<myhead.h>
int main(int argc, char const *argv[])
{
if(mkfifo("./fifo1",0664)==-1)
{
perror("mkfifo error");
return -1;
}
if(mkfifo("./fifo2",0664)==-1)
{
perror("mkfifo error");
return -1;
}
return 0;
}
#include<myhead.h>
void*output(void *fd)
{
int fp=*((int*)fd);
char sbuf[128]="";
while(1)
{
bzero(sbuf,sizeof(sbuf));
read(fp,sbuf,sizeof(sbuf));
if(strcmp(sbuf,"quit")==0)
{
break;
}
printf("\t\t\t%s:user2\n",sbuf);
}
close(fp);
pthread_exit(NULL);
}
void*input(void *fd)
{
int fp=*((int*)fd);
printf("对方已上线\n");
char sbuf[128]="";
while(1)
{
usleep(10);
fgets(sbuf,sizeof(sbuf),stdin);
sbuf[strlen(sbuf)-1]=0;
write(fp,sbuf,strlen(sbuf));
if(strcmp(sbuf,"quit")==0)
{
break;
}
}
close(fp);
pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
int outfd=open("./fifo1",O_RDONLY);
if(outfd==-1)
{
perror("open error");
return-1;
}
int infd=open("./fifo2",O_WRONLY);
if(infd==-1)
{
perror("open error");
return-1;
}
pthread_t tid1=-1;
if(pthread_create(&tid1,NULL,output,&outfd)!=0)
{
printf("pthread_create error\n");
return-1;
}
pthread_t tid2=-1;
if(pthread_create(&tid2,NULL,input,&infd)!=0)
{
printf("pthread_create error\n");
return-1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
#include<myhead.h>
void*output(void *fd)
{
int fp=*((int*)fd);
char sbuf[128]="";
while(1)
{
bzero(sbuf,sizeof(sbuf));
read(fp,sbuf,sizeof(sbuf));
if(strcmp(sbuf,"quit")==0)
{
break;
}
printf("\t\t\t%s:user1\n",sbuf);
}
close(fp);
pthread_exit(NULL);
}
void*input(void *fd)
{
int fp=*((int*)fd);
printf("对方已上线\n");
char sbuf[128]="";
while(1)
{
usleep(10);
fgets(sbuf,sizeof(sbuf),stdin);
sbuf[strlen(sbuf)-1]=0;
write(fp,sbuf,strlen(sbuf));
if(strcmp(sbuf,"quit")==0)
{
break;
}
}
close(fp);
pthread_exit(NULL);
}
int main(int argc, char const *argv[])
{
int infd=open("./fifo1",O_WRONLY);
if(infd==-1)
{
perror("open error");
return-1;
}
int outfd=open("./fifo2",O_RDONLY);
if(outfd==-1)
{
perror("open error");
return-1;
}
pthread_t tid1=-1;
if(pthread_create(&tid1,NULL,output,&outfd)!=0)
{
printf("pthread_create error\n");
return-1;
}
pthread_t tid2=-1;
if(pthread_create(&tid2,NULL,input,&infd)!=0)
{
printf("pthread_create error\n");
return-1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}