【Linux进程/线程间通信】实现荔枝派zero与电脑串口通信

本文采取两种方法来实现:

<1>System V IPC 的消息队列(message queue)

<2>有名信号量/灯的同步操作(semaphore)

一、System V IPC 的消息队列

消息队列

概念:

消息队列是System V IPC对象的一种

消息队列的使用:

发送端:

1 申请Key

2打开/创建消息队列   msgget

3向消息队列发送消息   msgsnd

 

接收端:

1打开/创建消息队列   msgget

2从消息队列接收消息   msgrcv

3 控制(删除)消息队列   msgctl

实验现象 

$ ./message_snd xxx(字符串)  //发送一条消息

 循环接收

源代码:

message_snd.c

发送端:发送一个消息

#include "message.h"

int main(int argc, const char *argv[])
{
    if(argc != 2)
    {
        printf("Don't forget to input your message.\n");
        return -1;
    }

    key_t key;
    int msgid, ret;
    msgbuf M;

    if((key = ftok(".", 100)) < 0)
    {
        perror("ftok");
        return -1;
    }

    if((msgid = msgget(key, IPC_CREAT | 0666)) < 0)
    {
        perror("msgget");
        return -1;
    }

    M.mtype = 1;
    strcpy(M.mtext, argv[1]);
    if((ret = msgsnd(msgid, &M, MSGLEN, 0)) < 0)
    {
        perror("msgnd");
        return -1;
    }       


    return 0;
}

message_snd.c

接收端:循环接收消息

#include "message.h"

int main(void)
{
    key_t key;
    int msgid, ret;
    msgbuf M;

    if((key = ftok(".", 100)) < 0)
    {
        perror("ftok");
        return -1;
    }

    if((msgid = msgget(key, IPC_CREAT | 0666)) < 0)
    {
        perror("msgget");
        return -1;
    }
    while(1)
    {   
        if((ret = msgrcv(msgid, &M, MSGLEN, 0, 0)) < 0)
        {
            perror("msgrcv");
            return -1;
        }
        printf("Got a %d message:%s\n", (int)M.mtype, M.mtext);
    }
    

    return 0;
}

message.h

头文件

#ifndef __MESSAGE_H__
#define __MESSAGE_H__

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>

typedef struct {
    long mtype;         /* message type, must be > 0 */
    char mtext[512];    /* message data */
}msgbuf;

#define MSGLEN (sizeof(msgbuf) - sizeof(long))

#endif

二、有名信号量/灯(semaphore)的同步操作

信号量/灯(semaphore)

定义:在同步两个 进程/线程 来使用有限的资源的时候来做的一种机制,主要用在生产者和消费者这种模型。

信号量代表某一类资源,其值表示系统中该资源的数量

信号量是一个受保护的变量,只能通过三种操作来访问

初始化

P操作(申请资源)  ->消费者

V操作(释放资源)  ->生产者

P(S) 含义如下:

     if  (信号量的值大于0) {  

          申请资源的任务继续运行;

          信号量的值减一

}

     else {   申请资源的任务阻塞,直到系统把资源分配给该任务;}

V(S) 含义如下:

    信号量的值加一

     if (有任务在等待资源) {

          唤醒等待的任务,让其继续运行

 }

sem_w.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <signal.h>

/*
    编译时一定要记得 -lpthread
*/

//捕获ctrl+c信号,删除/dev/shm里面创建的sem文件,以便程序能重复使用
void del_semaphore(int sig_no)
{
    if(sig_no == SIGINT)
    {
        sem_unlink("my_semaphore_write");
        exit(0);
    }
}

int main()
{
    key_t key;
    int shmid;
    char *shmaddr;
    sem_t *sem_r, *sem_w;
    struct sigaction act;
    act.sa_handler = del_semaphore;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGINT, &act, NULL);

    if((key = ftok(".", 100)) < 0)
    {
        perror("ftok");
        return -1;
    }

    if((shmid = shmget(key, 512, IPC_CREAT |0666)) < 0)
    {
        perror("shmget");
        return -1;
    }

    if((shmaddr = shmat(shmid, NULL, 0)) < 0)
    {
        perror("shmat");
        return -1;
    }

    sem_r = sem_open("my_semaphore_read", O_CREAT | O_RDWR, 0666, 0);
    sem_w = sem_open("my_semaphore_write", O_CREAT | O_RDWR, 0666, 1);

    while(1)
    {
        /* P操作 检查资源能不能用,占用资源,准备执行操作,信号量-1*/
        sem_wait(sem_w); //检查能不能写

        printf(">");
        fgets(shmaddr, 512, stdin); //这个函数要常记

        /* V操作 执行完毕,释放资源(读),信号量+1 */
        sem_post(sem_r);
    }

    return 0;
}

sem_r.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <signal.h>

/*
    编译时一定要记得 -lpthread
*/

//捕获ctrl+c信号,删除/dev/shm里面创建的sem文件,以便程序能重复使用
void del_semaphore(int sig_no)
{
    if(sig_no == SIGINT)
    {
        sem_unlink("my_semaphore_read");
        exit(0);
    }
}

int main()
{
    key_t key;
    int shmid;
    char *shmaddr;
    sem_t *sem_r, *sem_w;
    struct sigaction act;
    act.sa_handler = del_semaphore;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGINT, &act, NULL);

    if((key = ftok(".", 100)) < 0)
    {
        perror("ftok");
        return -1;
    }

    if((shmid = shmget(key, 512, IPC_CREAT |0666)) < 0)
    {
        perror("shmget");
        return -1;
    }

    if((shmaddr = shmat(shmid, NULL, 0)) < 0)
    {
        perror("shmat");
        return -1;
    }

    sem_r = sem_open("my_semaphore_read", O_CREAT | O_RDWR, 0666, 0);
    sem_w = sem_open("my_semaphore_write", O_CREAT | O_RDWR, 0666, 1);

    while(1)
    {
        /* P操作 占用资源,准备执行操作,信号量-1*/
        sem_wait(sem_r);    //检查能不能读

        printf("%s\n", shmaddr);

        /* V操作 执行完毕,释放资源(写),信号量+1 */
        sem_post(sem_w);
    }

    return 0;
}

 实验现象 

1.x86端

在这里捕捉SIGINT信号的原因:sem_open()会在/dev/shm下创建两个文件,我们需要在信号捕捉函数中执行sem_unlink()函数来在程序执行完毕后删除他们,否则这个程序就只能执行一次,不能反复执行。

2.ARM端

 

这种方法的好处就是可以打空格了哈哈哈哈哈哈哈哈哈哈

以上

猜你喜欢

转载自blog.csdn.net/imysy_22_/article/details/127197418