Linux进程间通信的方式:
管道、信号
消息队列、共享内存、信号灯
套接字
1.管道:
1.无名管道
只能用于具有亲缘关系的进程间通信无名管道创建后,进程会获得操作管道的两个文件描述符,创建子进程时,子进程会拷贝得到父进程的操作无名管道的两个文件描述符,
两个进程任务操作同一管道实现通信。int pipe(int pipefd[2]);
功能:
创建一个用来通信的管道
参数:
pipefd:存放管道两端(读写)的文件描述符数组
成功返回0
失败返回-11.如果管道中至少有一个写端:
如果管道中有数据,直接读出
如果管道中没有数据,会阻塞等待直到有数据写入后读出
2.如果管道中没有写端:
如果管道中有数据,直接读出
如果管道中没有数据,不会阻塞直接继续向下执行3.如果管道中至少有一个读端:
如果管道没有写满,直接写入
如果管道中写满(64k),则阻塞等待,等有数据读出才能继续写入4.如果管道中没有读端:
向管道中写入数据会产生管道破裂信号进程管道通信代码如下:表示为父子进程通过管道实现数据交换。 --无名管道
#include "../head.h" char tmpbuff[4096] = {0}; int main(void) { pid_t pid; int pipefd[2]; ssize_t nsize = 0; pipe(pipefd); pid = fork(); if (-1 == pid) { perror("fail to fork"); return -1; } if (0 == pid) { close(pipefd[0]); #if 0 strcpy(tmpbuff, "hello world"); write(pipefd[1], tmpbuff, strlen(tmpbuff)); read(pipefd[0], tmpbuff, sizeof(tmpbuff)); #endif while (1) { } } else if (pid > 0) { int cnt = 0; sleep(2); close(pipefd[0]); while (1) { printf("write前!\n"); write(pipefd[1], tmpbuff, sizeof(tmpbuff)); printf("write后!\n"); cnt++; printf("cnt = %d\n", cnt); } #if 0 nsize = read(pipefd[0], tmpbuff, sizeof(tmpbuff)); printf("读到 %ld 个字节,内容为: %s\n", nsize, tmpbuff); #endif } return 0; }
2.有名管道
通过管道在文件系统中的路径找到管道名,两个进程以读写的方式打开同一管道完成通信mkfifo:
int mkfifo(const char *pathname, mode_t mode);
功能:
创建一个管道
参数:
pathname:管道文件的路径
mode:权限
返回值:
成功返回0
失败返回-1有名管道必须读写两端同时加入后,才能继续向下执行
read.c #include "../head.h" extern int errno; int main(void) { int ret = 0; int fd = 0; ssize_t nsize = 0; char tmpbuff[4096] = {0}; ret = mkfifo("/tmp/myfifo", 0664); if (-1 == ret && errno != EEXIST) { perror("fail to mkfifo"); return -1; } fd = open("/tmp/myfifo", O_RDONLY); if (-1 == fd) { perror("fail to open"); return -1; } printf("打开管道成功!\n"); nsize = read(fd, tmpbuff, sizeof(tmpbuff)); printf("读取到 %ld 个字节, 内容: %s\n", nsize, tmpbuff); close(fd); return 0; }
write.c #include "../head.h" extern int errno; int main(void) { int ret = 0; int fd = 0; ret = mkfifo("/tmp/myfifo", 0664); if (-1 == ret && errno != EEXIST) { perror("fail to mkfifo"); return -1; } fd = open("/tmp/myfifo", O_WRONLY); if (-1 == fd) { perror("fail to open"); return -1; } printf("打开管道成功!\n"); write(fd, "hello world", 11); close(fd); return 0; }
上述表示两个文件执行不同操作,通过有名管道,就是创建了一个内核文件,这个文件在内核中,这段缓存区。最多存储64kb的大小,通过文件IO实现打开写入,其他文件的读取,实现不同进程之间的通信。上述代码的描述。
实现对播放器的控制的实验。
mplayer -slave -input file=/tmp/myfifo 视频路径 //将mplayer作为管道文件的从属文件,实现对播放器的控制。
#include "../head.h"
extern int errno;
int main(int argc, const char *argv[])
{
int ret = 0;
int fd = 0;
char commandbuf[256] = {0};
if (argc < 2)
{
fprintf(stderr, "Usage:./a.out command arg\n");
return -1;
}
ret = mkfifo("/tmp/myfifo", 0664);
if (-1 == ret && errno != EEXIST)
{
perror("fail to mkfifo");
return -1;
}
fd = open("/tmp/myfifo", O_RDWR);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
sprintf(commandbuf, "%s %s\n", argv[1], argv[2]);
write(fd, commandbuf, strlen(commandbuf));
close(fd);
return 0;
}
作业: 1.创建3个线程任务,线程1负责循环打印A 线程2负责循环打印B 线程3负责循环打印C 要求打印出来的内容总是 ABC
同步问题,线程执行,定义全局变量信号量
#include "../head.h"
sem_t sem_a;
sem_t sem_b;
sem_t sem_c;
void *threadafun(void *arg)
{
while (1)
{
sem_wait(&sem_a);
printf("A");
sem_post(&sem_b);
}
return NULL;
}
void *threadbfun(void *arg)
{
while (1)
{
sem_wait(&sem_b);
printf("B");
sem_post(&sem_c);
}
return NULL;
}
void *threadcfun(void *arg)
{
while (1)
{
sem_wait(&sem_c);
printf("C");
sem_post(&sem_a);
}
return NULL;
}
int main(void)
{
pthread_t tida;
pthread_t tidb;
pthread_t tidc;
sem_init(&sem_a, 0, 1);
sem_init(&sem_b, 0, 0);
sem_init(&sem_c, 0, 0);
pthread_create(&tida, NULL, threadafun, NULL);
pthread_create(&tidb, NULL, threadbfun, NULL);
pthread_create(&tidc, NULL, threadcfun, NULL);
pthread_join(tida, NULL);
pthread_join(tidb, NULL);
pthread_join(tidc, NULL);
sem_destroy(&sem_a);
sem_destroy(&sem_b);
sem_destroy(&sem_c);
return 0;
}
2.创建以下4个线程任务, 线程1 负责循环间隔1s打印"采集线程正在执行" 线程2 负责循环间隔2s打印"存储线程正在执行" 线程3 负责循环间隔5s打印"告警线程正在执行" 线程4 负责循环间隔10s打印"日志线程正在执行" 主进程负责从终端接收'A'、'B'、'C'、'D'
实现对线程1、2、3、4的打印控制 根据标志位做控制 点击一下实现执行,再次点击实现关闭。
定义全局变量标志位和四把锁,四把锁用于防止同一个信号下锁的互斥问题,进入线程1的If中,返回执行判断语句,导致关闭标志位后,仍然打印任务数据。
#include "../head.h"
pthread_mutex_t CollectLock;
pthread_mutex_t StorageLock;
pthread_mutex_t AlarmLock;
pthread_mutex_t LogLock;
int IsCollectThreadWork = 0;
int IsStorageThreadWork = 0;
int IsAlarmThreadWork = 0;
int IsLogThreadWork = 0;
int IsExist = 0;
void *CollectMainThread(void *arg)
{
while (!IsExist)
{
pthread_mutex_lock(&CollectLock);
if (IsCollectThreadWork)
{
printf("采集线程正在执行\n");
pthread_mutex_unlock(&CollectLock);
sleep(1);
}
else
{
pthread_mutex_unlock(&CollectLock);
}
}
return NULL;
}
void *StorageMainThread(void *arg)
{
while (!IsExist)
{
pthread_mutex_lock(&StorageLock);
if (IsStorageThreadWork)
{
printf("存储线程正在执行\n");
pthread_mutex_unlock(&StorageLock);
sleep(2);
}
else
{
pthread_mutex_unlock(&StorageLock);
}
}
return NULL;
}
void *AlarmMainThread(void *arg)
{
while (!IsExist)
{
pthread_mutex_lock(&AlarmLock);
if (IsAlarmThreadWork)
{
printf("告警线程正在执行\n");
pthread_mutex_unlock(&AlarmLock);
sleep(5);
}
else
{
pthread_mutex_unlock(&AlarmLock);
}
}
return NULL;
}
void *LogMainThread(void *arg)
{
while (!IsExist)
{
pthread_mutex_lock(&LogLock);
if (IsLogThreadWork)
{
printf("日志线程正在执行\n");
pthread_mutex_unlock(&LogLock);
sleep(10);
}
else
{
pthread_mutex_unlock(&LogLock);
}
}
return NULL;
}
int main(void)
{
pthread_t tid[4];
void *(*p[4])(void *) = {CollectMainThread, StorageMainThread, AlarmMainThread, LogMainThread};
int i = 0;
char ch = 0;
pthread_mutex_init(&CollectLock, NULL);
pthread_mutex_init(&StorageLock, NULL);
pthread_mutex_init(&AlarmLock, NULL);
pthread_mutex_init(&LogLock, NULL);
for (i = 0; i < 4; i++)
{
pthread_create(&tid[i], NULL, p[i], NULL);
}
while (1)
{
ch = getchar();
if ('a' == ch || 'A' == ch)
{
pthread_mutex_lock(&CollectLock);
IsCollectThreadWork = !IsCollectThreadWork;
pthread_mutex_unlock(&CollectLock);
}
else if ('b' == ch || 'B' == ch)
{
pthread_mutex_lock(&StorageLock);
IsStorageThreadWork = !IsStorageThreadWork;
pthread_mutex_unlock(&StorageLock);
}
else if ('c' == ch || 'C' == ch)
{
pthread_mutex_lock(&AlarmLock);
IsAlarmThreadWork = !IsAlarmThreadWork;
pthread_mutex_unlock(&AlarmLock);
}
else if ('d' == ch || 'D' == ch)
{
pthread_mutex_lock(&LogLock);
IsLogThreadWork = !IsLogThreadWork;
pthread_mutex_unlock(&LogLock);
}
else if ('q' == ch || 'Q' == ch)
{
IsExist = 1;
break;
}
}
for (i = 0; i < 4; i++)
{
pthread_join(tid[i], NULL);
}
pthread_mutex_destroy(&CollectLock);
pthread_mutex_destroy(&StorageLock);
pthread_mutex_destroy(&AlarmLock);
pthread_mutex_destroy(&LogLock);
return 0;
}
对于上述问题的解决,远古程序员才会这样写,累死你个龟孙。以下为一种结构体方案实现方法,参考即可。下图参考:
#include "../head.h"
int IsExit = 0;
#define TASKLIST_MAXNUM 10
/* 线程任务类型 */
typedef struct task
{
char TaskName[256]; //任务名称
void *(*pTaskFun)(void *); //任务入口地址
int WorkFlag; //工作状态标志
pthread_mutex_t Lock; //锁
pthread_t tid; //线程ID
int second; //工作间隔
}task_t;
/* 所有线程任务列表 */
typedef struct tasklist
{
task_t tasklist[TASKLIST_MAXNUM]; //所有任务列表
int curtaskcnt; //当前任务个数
}tasklist_t;
/* 添加任务 */
int AddTask(tasklist_t *ptasklist, char *pThreadName, void *(*pThreadFun)(void *), int seconds)
{
task_t *ptask = NULL;
ptask = &ptasklist->tasklist[ptasklist->curtaskcnt];
strcpy(ptask->TaskName, pThreadName);
ptask->pTaskFun = pThreadFun;
ptask->WorkFlag = 0;
ptask->second = seconds;
pthread_mutex_init(&ptask->Lock, NULL);
pthread_create(&ptask->tid, NULL, ptask->pTaskFun, ptask);
ptasklist->curtaskcnt++;
return 0;
}
/* 任务线程 */
void *MainThread(void *arg)
{
task_t *ptask = arg;
while (!IsExit)
{
pthread_mutex_lock(&ptask->Lock);
if (ptask->WorkFlag)
{
printf("%s 线程正在执行\n", ptask->TaskName);
pthread_mutex_unlock(&ptask->Lock);
sleep(ptask->second);
}
else
{
pthread_mutex_unlock(&ptask->Lock);
}
}
return NULL;
}
int SwitchWorkMode(tasklist_t *ptasklist, char *pThreadName)
{
int i = 0;
for (i = 0; i < ptasklist->curtaskcnt; i++)
{
if (0 == strcmp(ptasklist->tasklist[i].TaskName, pThreadName))
{
pthread_mutex_lock(&ptasklist->tasklist[i].Lock);
ptasklist->tasklist[i].WorkFlag = !ptasklist->tasklist[i].WorkFlag;
pthread_mutex_unlock(&ptasklist->tasklist[i].Lock);
break;
}
}
return 0;
}
int WaitAllTaskEnd(tasklist_t *plist)
{
int i = 0;
for (i = 0; i < plist->curtaskcnt; i++)
{
pthread_join(plist->tasklist[i].tid, NULL);
}
return 0;
}
int main(void)
{
tasklist_t list;
char ch = 0;
AddTask(&list, "采集线程", MainThread, 1);
AddTask(&list, "存储线程", MainThread, 2);
AddTask(&list, "告警线程", MainThread, 5);
AddTask(&list, "日志线程", MainThread, 10);
while (1)
{
ch = getchar();
if ('A' == ch || 'a' == ch)
{
SwitchWorkMode(&list, "采集线程");
}
else if ('B' == ch || 'b' == ch)
{
SwitchWorkMode(&list, "存储线程");
}
else if ('C' == ch || 'c' == ch)
{
SwitchWorkMode(&list, "告警线程");
}
else if ('D' == ch || 'd' == ch)
{
SwitchWorkMode(&list, "日志线程");
}
else if ('Q' == ch || 'q' == ch)
{
IsExit = 1;
break;
}
}
WaitAllTaskEnd(&list);
return 0;
}
定义了两种结构体类型,第一个为具体结构体,第二个为结构体的集合和记录数量。