【学习嵌入式-day-27-进程间通信】
线程间循环打印ABC,使用信号量决定打印顺序
#include "../head.h"sem_t sem_a;
sem_t sem_b;
sem_t sem_c;void *thread1(void *arg)
{while(1){sem_wait(&sem_a);printf("A\n");sem_post(&sem_b);}return NULL;
}
void *thread2(void *arg)
{while(1){sem_wait(&sem_b);printf("B\n");sem_post(&sem_c);}return NULL;
}
void *thread3(void *arg)
{while(1){sem_wait(&sem_c);printf("C\n");sem_post(&sem_a);}return NULL;
}int main(void)
{pthread_t tid1;pthread_t tid2;pthread_t tid3;sem_init(&sem_a, 0, 1);sem_init(&sem_b, 0, 0);sem_init(&sem_c, 0, 0);pthread_create(&tid1, NULL, thread1, NULL);pthread_create(&tid2, NULL, thread2, NULL);pthread_create(&tid3, NULL, thread3, NULL);pthread_join(tid1, NULL);pthread_join(tid2, NULL);pthread_join(tid3, NULL);sem_destroy(&sem_a);sem_destroy(&sem_b);sem_destroy(&sem_c);return 0;
}
进程间通信
概念
- 进程空间是独立的,包含文本段、数据段和系统数据段
- 多个进程没有共享的用户空间,进程是操作系统资源分配的最小单元
- 只能利用内核实现进程间通信
进程间通信常用的方式
传统Unix系统中的通信方式
- 管道
- 信号
SYS V分支中的通信方式
- 消息队列
- 共享内存
- 信号量数组(信号灯)
BSD分支中的通信方式
- 本地域套接字
管道(内核缓存区)
分类
- 有名管道
- 无名管道(只能用于具有亲缘关系的进程间通信)
无名管道
原理:
- 无名管道是一段内核缓存区
- 父进程通过Pipe创建无名管道
- 只有该父进程的子进程才能继承文件描述符,
函数接口
- pipe :
创建无名管道,0读,1写
#include "../head.h"int main(void)
{int pipefd[2];int pipefd2[2];int ret = 0;pid_t pid;char tmpbuff[4096] = {0};char tmpbuff2[4096] = {0};ret = pipe(pipefd);if(-1 == ret || pipe(pipefd2) == -1){perror("fail to pipe");return -1;}pid = fork();if(-1 == pid){perror("fail to fork");return -1;}if(0 == pid) //子进程{strcpy(tmpbuff, "hello world");write(pipefd[1], tmpbuff, strlen(tmpbuff));read(pipefd2[0], tmpbuff2, sizeof(tmpbuff2));printf("tmpbuff2 = %s\n", tmpbuff2);}else if(pid > 0) //父进程{read(pipefd[0], tmpbuff, sizeof(tmpbuff));printf("tmpbuff = %s\n", tmpbuff);strcpy(tmpbuff2, "thank you");write(pipefd2[1], tmpbuff2, strlen(tmpbuff2));}close(pipefd[0]);close(pipefd[1]);close(pipefd2[0]);close(pipefd2[1]);return 0;
}
管道操作特性
- 管道中至少有一个写端
- 读取数据时,如果管道中有数据,则直接读出
- 管道中没有数据, 则阻塞等待有数据写入才能读出
- 管道中没有写端
- 读取数据时,管道中有数据,直接读出
- 没有数据,不阻塞等待直接向下执行
- 管道中至少有一个读端
- 写入数据时,如果管道没有写满,则直接写入
- 管道存满,则阻塞等待有数据读出,才能继续写入
- 管道中没有读端
- 写入数据时,会产生管道破裂的信号导致进程任务异常退出
- 管道中至少有一个写端
有名管道
与无名管道区别
- 有名管道有名字,可以通过名字找到该管道
- 可以用于任意进程间的通信
- 进程间通信最简单、易实现的方式
操作方式
- 进程一使用open以 读、写、读写方式打开管道文件
- 进程二使用open以读、写、读写方式打开管道文件
- 两个进程可以通过向管道文件中读写数据实现进程的通信
函数接口
mkfifo
循环实现,write.c从终端写字符串,read.c从终端接收,再往终端写字符串,write接收
发一次,收一次,循环
//read.c
#include "../head.h"int main(void)
{int fdr = 0;int fdw = 0;char tmpbuff[4096] = {0};mkfifo("./myfifo1", 0777);mkfifo("./myfifo2", 0777);fdr = open("./myfifo1", O_RDONLY);//可以直接设置为只读RDWRif(-1 == fdr){perror("fail to open");return -1;}fdw = open("./myfifo2", O_WRONLY);if(-1 == fdw){perror("fail to open");return -1;}printf("管道打开成功\n");while(1){memset(tmpbuff, 0, sizeof(tmpbuff));//清零read(fdr, tmpbuff, sizeof(tmpbuff));printf("RECV:%s\n", tmpbuff);memset(tmpbuff, 0, sizeof(tmpbuff));m_fgets(tmpbuff);write(fdw, tmpbuff, strlen(tmpbuff));}close(fdr);close(fdw);return 0;
}
//wirte.c#include "../head.h"int main(void)
{int fdr = 0;int fdw = 0;char tmpbuff[4096] = {0};mkfifo("./myfifo1", 0777);mkfifo("./myfifo2", 0777);fdw = open("./myfifo1", O_WRONLY);if(-1 == fdr){perror("fail to open");return -1;}fdr = open("./myfifo2", O_RDONLY);if(-1 == fdw){perror("fail to open");return -1;}printf("管道打开成功\n");while(1){memset(tmpbuff, 0, sizeof(tmpbuff));m_fgets(tmpbuff);write(fdw, tmpbuff, strlen(tmpbuff));memset(tmpbuff, 0, sizeof(tmpbuff));read(fdr, tmpbuff, sizeof(tmpbuff));printf("RECV:%s\n", tmpbuff);}close(fdr);close(fdw);return 0;
}
信号
概念
- linux系统中的信号,主要实现应用层和内核层之间的信号通知
- 应用层和内核层之间通信的信号,根据含义分为很多不同的信号
- 信号主要用于多个进程任务间的时间通知
- 信号可以用于异步通信
- 信号的类型
- 使用kill-l查看信号的类型
信号的处理方式
- 缺省:信号来了,按照默认方式处理
- 忽略:信号来了,不处理信号,忽略信号
- 捕捉:信号来了,按照用户自己定义的方式处理信号
- 9号信号(SIGKILL)和19号信号(SIGSTOP),不能被忽略和捕捉的
函数接口
signal
从终端输入 ctrl + C打印打印 SIGINT信号来了
输入 ctrl + \打印 SIGQUIT信号来了
输入 ctrl + z打印 SIGTSTP信号来了
#include "../head.h"void handler1(int signo)
{printf("SIGNINT信号来了\n");
}
void handler2(int signo)
{printf("SIGQUIT信号来了\n");
}
void handler3(int signo)
{printf("SIGTSTP信号来了\n");
}
int main(void)
{void(*pret1)(int) = NULL;void(*pret2)(int) = NULL;void(*pret3)(int) = NULL;pret1 = signal(SIGINT, handler1);if(SIG_ERR == pret1){perror("fail to signal");return -1;}pret2 = signal(SIGQUIT, handler2);if(SIG_ERR == pret2){perror("fail to signal");return -1;}pret3 = signal(SIGTSTP, handler3);if(SIG_ERR == pret3){perror("fail to signal");return -1;}while(1){}return 0;
}
alarm
#include "../head.h"void handler(int signo)
{printf("SIGALRM信号来了\n");alarm(5);return;
}int main(void)
{signal(SIGALRM, handler);alarm(5);while (1){printf("正在运行...\n");sleep(1);}return 0;
}
kill
#include "../head.h"pid_t pid;void handler_parent(int signo)
{if (SIGQUIT == signo){kill(pid, SIGUSR2);}else if (SIGUSR1 == signo){printf("父进程:收到子进程发送的信号了\n");}return;
}void handler_child(int signo)
{if (SIGINT == signo){kill(getppid(), SIGUSR1);}else if (SIGUSR2 == signo){printf("子进程:收到父进程发送的信号了\n");}return;
}int main(void)
{pid = fork();if (-1 == pid){perror("fail to fork");return -1;}if (0 == pid){signal(SIGUSR2, handler_child);signal(SIGINT, handler_child);signal(SIGQUIT, SIG_IGN);}else if (pid > 0){signal(SIGUSR1, handler_parent);signal(SIGQUIT, handler_parent);signal(SIGINT, SIG_IGN);}while (1){}return 0;
}