嵌入式学习---应用程序设计---进程的沟通方式篇

1、进程间的沟通方式有哪几种?

1.1、管道

(管道我在上一篇为大家介绍了,有兴趣的朋友可以看看我的上一篇文章)

1.2、消息队列

1.2.1、定义:消息队列是一些消息的列表

1.2.2、消息队列的操作函数:

创建:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
 

添加:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, void *msgp, int msgsz, int msgflg);
 

读取:

#include <sys/types>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgrcv(int msgid, void *msgp, int msgsz, long msgtyp, int msgflg);
 

控制:

#include <sys/types>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msgid,int cmd,struct msqid_ds *buf);

1.2.3:消息队列的特点:

①:支持非亲缘进程间通信;

②:可以共享大量数据;

③:具有先进先出的特点;

④:可以提供优先级;

1.2.4:消息队列的操作:

①:创建消息队列;

②:写入数据到消息队列;

③:读出消息;

④:销毁消息队列;

1.2.5、实例实现:

//p1.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct msg_buf
{
	long mtype;
	char mtext[100];
};

int main()
{
	//ftok
	key_t key = ftok(".",3);
	if(key == -1)
	{
		puts("ftok error.");
		return -1;
	}
	//msgget
	int msgid = msgget(key, IPC_CREAT | 0664);
	if(msgid == -1)
	{
		puts("msgget error.");
		return -1;
	}
	struct msg_buf msg;
	//msgsnd
	memset(&msg, 0, sizeof(msg));
	msg.mtype = 1;
	strcpy(msg.mtext,"PJZJDGG");
	msgsnd(msgid, &msg, 100, 0);
	return 0;
}
//p2.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>

struct msg_buf
{
	long mtype;
	char mtext[100];
};

int main()
{
	//ftok
	key_t key = ftok(".",3);
	if(key == -1)
	{
		puts("ftok error.");
		return -1;
	}
	//msgget
	int msgid = msgget(key, IPC_CREAT | 0664);
	if(msgid == -1)
	{
		puts("msgget error.");
		return -1;
	}
	struct msg_buf msg;
	//msgsnd
	memset(&msg, 0, sizeof(msg));
	
	int ret = msgrcv(msgid, &msg, 100, 1, 0);
	if(ret > 0)
	{
		puts(msg.mtext);
	}
	msgctl(msgid, IPC_RMID, NULL);
	return 0;
}

1.3、共享内存

1.3.1、共享内存的定义:共享内存是常用的进程间通信,两个进程可以直接共享访问同一块内存区域。

1.3.2、共享内存的特点:

①、访问高效(不需要从内核中复制数据到用户态(管道);不需要多次的系统调用);

②、容易产生静态问题,需要一种同步或者互斥机制来保证共享内存中数据的安全性;

③、可以使非亲缘进程通信;

1.3.3、

实现共享内存的步骤如下:

(1)、 创建内存共享区;

#include <sys/ipc.h>

#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);

(2)、 映射共享内存到用户态;

#include <sys/types.h>

#include <sys/shm.h>

void *shmat(int shmid, const void *shmaddr, int shmflg);

(3)、通过映射出来的指针操作共享内存;

#include <sys/types.h>

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

(4)、撤销内存映射关系;

#include <sys/types.h>

#include <sys/shm.h>

int shmdt(const void *shmaddr);

1.3.4、共享内存的实例:

#include <stddef.h>

#include <sys/shm.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <string.h>

#include "shmdata.h"

 

int main(int argc, char **argv)

{

    void *shm = NULL;

    struct shared_use_st *shared; // 指向shm

    int shmid; // 共享内存标识符

 

    // 创建共享内存

    shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666|IPC_CREAT);

    if (shmid == -1)

    {

        fprintf(stderr, "shmat failed\n");

        exit(EXIT_FAILURE);

    }

 

    // 将共享内存连接到当前进程的地址空间

    shm = shmat(shmid, 0, 0);

    if (shm == (void *)-1)

    {

        fprintf(stderr, "shmat failed\n");

        exit(EXIT_FAILURE);

    }

 

    printf("\nMemory attached at %X\n", (int)shm);

 

    // 设置共享内存

    shared = (struct shared_use_st*)shm; // 注意:shm有点类似通过 malloc() 获取到的内存,所以这里需要做个 类型强制转换

    shared->written = 0;

    while (1) // 读取共享内存中的数据

    {

        // 没有进程向内存写数据,有数据可读取

        if (shared->written == 1)

        {

            printf("You wrote: %s", shared->text);

            sleep(1);

 

            // 读取完数据,设置written使共享内存段可写

            shared->written = 0;

 

            // 输入了 end,退出循环(程序)

            if (strncmp(shared->text, "end", 3) == 0)

            {

                break;

            }

        }

        else // 有其他进程在写数据,不能读取数据

        {

            sleep(1);

        }

    }

 

    // 把共享内存从当前进程中分离

    if (shmdt(shm) == -1)

    {

        fprintf(stderr, "shmdt failed\n");

        exit(EXIT_FAILURE);

    }

 

    // 删除共享内存

    if (shmctl(shmid, IPC_RMID, 0) == -1)

    {

        fprintf(stderr, "shmctl(IPC_RMID) failed\n");

        exit(EXIT_FAILURE);

    }

 

    exit(EXIT_SUCCESS);

}
 

1.4、信号

1.4.1、信号是什么?

①、信号是进程间通信的一种手段;

②、是一种事件触发机制;

③、是一种异步通信(是进程间通信机制中唯一一种异步手段)

ps:  同步:是一种按照顺序依次执行的过程,若其环节被阻塞,则整个过程会被阻塞;

         异步:是一种使用信号或者中断来实现一种事件的触发过程,若没有信号或中断产生,并不会影响程序的正常执行;

④、信号可以由一个进程发送,另一个进程捕获,阻塞和忽略;

ps:  捕获:收到信号后会执行相应的动作;

          阻塞: 会阻塞信号的传递,直至解除阻塞,才会继续传递;

          忽略:收到信号后,进程不会做出 任何反应和处理;

1.4.2、常用的信号的特点:

①、信号无法传递大量数据;

②、信号可以在任意进程间通信;

③、信号是一种异步触发手段;

1.4.3、常用的信号列举:

SIGHUP:  1号信号,(在控制终端上挂起信号,或让进程结束);

SIGINT:    2号信号,(键盘输入中断,ctrl + c );

SIGQUIT: 3号信号,(键盘输入退出,ctrl+ | );

SIGABRT:6号信号,(非正常终止,double free);

SIGKILL:  9号信号,(杀死进程信号),该信号不能被阻塞、忽略、自定义处理;

SIGSEGV:11号信号,(无效的内存引用,解引用空指针、内存越界访问);

SIGPIPE:13号信号,(管道中止: 写入无人读取的管道,会导致管道破裂);

SIGCHLD:17号信号,(子进程发送给父进程的信号,但该信号为忽略处理的);

SIGSTOP:19号信号,(停止进程);

1.4.4、信号的操作

①、信号的发送

②、信号的捕获

1.4.5、信号操作实例:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

typedef void (*sighandler_t)(int);

//捕获函数
void func(int sig)
{
        if(SIGINT != sig)
        {
                return;
        }
        printf("func for signal: %d\n",sig);
}

int main(void)
{
        sighandler_t ret = (sighandler_t)-2;

        //sighandler_t signal(int signum, sighandler_t handler);函数原型
        //signal(SIGINT,func);
        //signal(SIGINT,SIG_DFL);       //让信号默认处理
        ret = signal(SIGINT,SIG_IGN);   //忽略处理
        //ret = signal(SIGKILL,SIG_IGN);
        printf("返回值为:%d\n",(int)ret);
        if(SIG_ERR == ret)      //判断信号处理是否成功
        {
                perror("signal");
                exit(-1);
        }
        printf("before while(1)\n");
        while(1);
        printf("after while(1)\n");

        return 0;
}

1.5、信号量

1.5.1、定义:它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据;

1.5.2、信号量的特点:

①、是一种同步机制(保证访问临界资源的有序性);

②、不能实现大量的数据共享;

③、支持非双亲缘进程间通信;

④、信号量可以由一个进程申请,其他进程释放;

1.5.3、操作信号量的流程:

①、创建或者获取信号值;

②、获得信号值当前的数值;

③、判断当前信号量的值是否是自己能访问的;

④、如果是自己可以操作的状态:(申请信号量资源(p操作);执行临界区代码;如果需要释放资源再释放(v操作))

ps:p原子操作:信号量的值减1(只有当信号量的值大于0时,才能成功),描述的是申请资源的过程,申请成功后,会操作临界区的代码,当信号量的值为0时,p操作会被阻塞,直至信号量值不为0,解除阻塞;

       v原子操作:信号量值加1;

1.5.4、信号量操作的函数

(1)、信号量的获取:sem_open;

(2)、获得信号量的值:sem_getvalue;

(3)、p操作:sem_wait;

(4)、v操作:sem_post;

(5)、关闭信号量:sem_break;

1.5.5:关于信号量操作的实例:

//p1.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>

int main()
{
	//ftok()
	key_t key = ftok(".",2);
	if(key == -1)
	{
		puts("ftok error.");
		return -1;
	}
	//shm_get()
	int shmid = shmget(key, 100, IPC_CREAT | 0664);
	if(shmid == -1)
	{
		puts("shmget error.");
		return -1;
	}
	//sem_open()
	sem_t *sem = NULL;
	sem = sem_open("mysem", O_CREAT | O_RDWR, 0664, 3);
	if(NULL == sem)
	{
		puts("sem_open error.");
		return -1;
	}
	//shmat()
	volatile char *shmaddr = NULL;
	shmaddr = shmat(shmid, shmaddr, 0);
	if(NULL == shmaddr)
	{
		puts("shmat error.");
		sem_close(sem);
		return -1;
	}
	//do_work
	while(1)
	{
		int sval = 0;
		sem_getvalue(sem, &sval);
		//printf("sval:%d\n",sval);
		if(sval == 3)
		{
			sem_wait(sem);
			char buf[] = "准备包装盒";
			memset(shmaddr, 0, 100);
			memcpy(shmaddr, buf, sizeof(buf));
			if(strcmp(buf, shmaddr) == 0)
			{
				puts(shmaddr);
			}
		}
	}
	//shmdt()
	shmdt(shmaddr);
	//sem_close()
	sem_close(sem);
	return 0;
}
//p2.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>

int main()
{
	//ftok()
	key_t key = ftok(".",2);
	if(key == -1)
	{
		puts("ftok error.");
		return -1;
	}
	//shm_get()
	int shmid = shmget(key, 100, IPC_CREAT | 0664);
	if(shmid == -1)
	{
		puts("shmget error.");
		return -1;
	}
	//sem_open()
	sem_t *sem = NULL;
	sem = sem_open("mysem", O_RDWR);
	if(NULL == sem)
	{
		puts("sem_open error.");
		return -1;
	}
	//shmat()
	volatile char *shmaddr = NULL;
	shmaddr = shmat(shmid, shmaddr, 0);
	if(NULL == shmaddr)
	{
		puts("shmat error.");
		sem_close(sem);
		return -1;
	}
	//do_work
	while(1)
	{
		int sval = 0;
		sem_getvalue(sem, &sval);
		if(sval == 2)
		{
			sem_wait(sem);
			char buf[] = "放入礼品";
			memset(shmaddr, 0, 100);
			memcpy(shmaddr, buf, sizeof(buf));
			if(strcmp(buf, shmaddr) == 0)
			{
				puts(shmaddr);
			}
		}
	}
	//shmdt()
	shmdt(shmaddr);
	//sem_close()
	sem_close(sem);
	return 0;
}
//p3.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <semaphore.h>

int main()
{
	//ftok()
	key_t key = ftok(".",2);
	if(key == -1)
	{
		puts("ftok error.");
		return -1;
	}
	//shm_get()
	int shmid = shmget(key, 100, IPC_CREAT | 0664);
	if(shmid == -1)
	{
		puts("shmget error.");
		return -1;
	}
	//sem_open()
	sem_t *sem = NULL;
	sem = sem_open("mysem", O_RDWR);
	if(NULL == sem)
	{
		puts("sem_open error.");
		return -1;
	}
	//shmat()
	volatile char *shmaddr = NULL;
	shmaddr = shmat(shmid, shmaddr, 0);
	if(NULL == shmaddr)
	{
		puts("shmat error.");
		sem_close(sem);
		return -1;
	}
	//do_work
	while(1)
	{
		int sval = 0;
		sem_getvalue(sem, &sval);
		if(sval == 1)
		{
			sem_wait(sem);
			char buf[] = "打包";
			memset(shmaddr, 0, 100);
			memcpy(shmaddr, buf, sizeof(buf));
			if(strcmp(buf, shmaddr) == 0)
			{
				puts(shmaddr);
			}
			sem_post(sem);
			sem_post(sem);
			sem_post(sem);
			//sleep(1);
		}
#if 0	
		sem_getvalue(sem, &sval);
		printf("sval:%d\n",sval);
#endif
	}
	//shmdt()
	shmdt(shmaddr);
	//sem_close()
	sem_close(sem);
	return 0;
}

1.5.6:信号量异常实验现象的原因:

①、Linux高版本内核对高性能CPU的兼容性有bug;

②、实现的代码依赖于进程上下文切换,如果上下文切换出问题就导致代码跳跃性执行;

③、如果虚拟机配置多核模式,可能出现上述问题,如果配置的是单核问题应该不大;

2、总结:昨天课堂上结束了对于进程的学习,但对于我来说,不管我有没有完全掌握关于进程的知识点,进程的内容远远不止我所接触的知识面 ,所以我会好好复习以前学过的知识,至少做到吃进去的东西得消化吧,还有我所分享的内容如果有错误还请大家帮我多多指正,这周的分享就到此结束了,谢谢大家观看。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条小白码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值