Linux软件编程(Day8)-进程通信,管道

Linux进程间通信的方式:
    管道、信号 
    消息队列、共享内存、信号灯
    套接字 

1.管道: 
    1.无名管道
        只能用于具有亲缘关系的进程间通信

        无名管道创建后,进程会获得操作管道的两个文件描述符,创建子进程时,子进程会拷贝得到父进程的操作无名管道的两个文件描述符,
        两个进程任务操作同一管道实现通信。

        int pipe(int pipefd[2]);
        功能:
            创建一个用来通信的管道
        参数:
            pipefd:存放管道两端(读写)的文件描述符数组
        成功返回0 
        失败返回-1 

        1.如果管道中至少有一个写端:
            如果管道中有数据,直接读出
            如果管道中没有数据,会阻塞等待直到有数据写入后读出
        
        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;
}

        定义了两种结构体类型,第一个为具体结构体,第二个为结构体的集合和记录数量。

  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值