【从零开始的嵌入式生活】并发程序设计3——进程间通信(2)

简介: 【从零开始的嵌入式生活】并发程序设计3——进程间通信(2)

设置信号相应方式——signal

#include  <unistd.h>
 #include <signal.h>
 void (*signal(int signo, void (*handler)(int)))(int);//函数指针定义
 typedef void (*sighandler_t)(int);
       sighandler_t signal(int signum, sighandler_t handler);


成功时返回原先的信号处理函数,失败时返回SIG_ERR

signo 要设置的信号类型

handler 指定的信号处理函数: SIG_DFL代表缺省方式; SIG_IGN 代表忽略信号;

示例代码:


#include <signal.h>
#include <stdio.h>
void handler(int sig){
        if(SIGINT == sig){
                printf("I got sigint signal\n");
        }else{
                printf("I got sighup signal\n");
        }
}
int main(){
        signal(SIGINT, handler);
        signal(SIGHUP, handler);
        while(1){
                sleep(1);
        }
        return 0;
}


应用:按ctrl +c一次继续执行,两次继续执行,第三次停止


#include <stdio.h>
#include <signal.h>
typedef void (*signt)(int);
signt oldhandle;
int count = 0;
void functh(int sig){
        ++count;
        printf("I catch ctrl+c!!!\n");
        if(count == 2){
                signal(SIGINT, oldhandle);//signal(SIGINT, SIG_DEL);功能类似
        }
}
int main(){
        oldhandle = signal(SIGINT,functh);
        while(1) sleep(1);
}

子进程结束信号

SIGCHLD


可以用来处理子进程退出的僵尸

示例代码:


#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void functh(int sig){
        wait(NULL);
}
int main(){
        pid_t pid;
        signal(SIGCHLD, functh);
        pid = fork();
        if(pid < 0 ){
                perror("fork");
                return -1;
        }else if(pid == 0){
                printf("this child \n");
                sleep(10);
                exit(0);
        }
        while(1){
                printf("I am working!!!\n");
                sleep(1);
        }
        return 0;
}


共享内存

System V IPC

IPC 对象包含: 共享内存、消息队列和信号灯集

每个IPC对象有唯一的ID

IPC对象创建后一直存在,直到被显式地删除

每个IPC对象有一个关联的KEY

ipcs / ipcrm可以查看和删除这个


System V IPC – ftok

#include  <sys/types.h>
#include <sys/ipc.h>
key_t  ftok(const char *path,  int proj_id);


成功时返回合法的key值,失败时返回EOF

path 存在且可访问的文件的路径

proj_id 用于生成key的数字,范围1-255。

共享内存

共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝

共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活

由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用

使用步骤

创建/打开共享内存

映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问

读写共享内存

撤销共享内存映射

删除共享内存对象

创建 - shmget

#include <sys/ipc.h>
 #include <sys/shm.h>
 int shmget(key_t key, int size, int shmflg);


成功时返回共享内存的id,失败时返回EOF

key 和共享内存关联的key,IPC_PRIVATE 或 ftok生成

shmflg 共享内存标志位 IPC_CREAT|0666

共享内存映射 – shmat

#include <sys/shm.h>
 void  *shmat(int shmid, const void *shmaddr, int shmflg);


成功时返回映射后的地址,失败时返回(void *)-1

shmid 要映射的共享内存id

shmaddr 映射后的地址, NULL表示由系统自动映射

shmflg 标志位 0表示可读写;SHM_RDONLY表示只读

共享内存映射 – shmat

#include <sys/ipc.h>
 #include <sys/shm.h>
 int  shmdt(void *shmaddr);


成功时返回0,失败时返回EOF

不使用共享内存时应撤销映射

进程结束时自动撤销

共享内存控制 – shmctl

#include <sys/ipc.h>
 #include <sys/shm.h>
 int  shmctl(int shmid, int cmd, struct shmid_ds *buf);


成功时返回0,失败时返回EOF

shmid 要操作的共享内存的id

cmd 要执行的操作 IPC_STAT IPC_SET IPC_RMID

buf 保存或设置共享内存属性的地址

示例代码

send


#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
        key_t key;
        int shmid;
        char *addr;
        key = ftok(".", 23);
        if(key == -1){
                perror("ftok");
                return -1;
        }
        shmid = shmget(key, 1024, IPC_CREAT|0666);
        if(shmid == -1){
                perror("shmget");
                return -1;
        }
        addr = shmat(shmid, NULL, 0);
        strcpy(addr, "this my share memeory");
        shmdt(addr);
}


write


#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>
int main(){
        key_t key;
        int shmid;
        char *addr;
        key = ftok(".", 23);
        if(key == -1){
                perror("ftok");
                return -1;
        }
        shmid = shmget(key, 1024, IPC_CREAT|0666);
        if(shmid == -1){
                perror("shmget");
                return -1;
        }
        addr = shmat(shmid, NULL, 0);
        printf("get shar mem=%s\n",addr);
        shmdt(addr);
}



删除队列留给大家自己学习,当然gitee库里面有对应的示例代码。


消息队列

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

消息队列由消息队列ID来唯一标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等

消息队列可以按照类型来发送/接收消息

使用步骤

打开/创建消息队列 msgget

向消息队列发送消息 msgsnd

从消息队列接收消息 msgrcv

控制消息队列 msgctl

消息创建/打开 - msgget

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


成功时返回消息队列的id,失败时返回EOF

key 和消息队列关联的key IPC_PRIVATE 或 ftok

msgflg 标志位 IPC_CREAT|0666

消息发送 – msgsnd

#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msgid, const void *msgp, size_t size,
            int msgflg);


成功时返回0,失败时返回-1

msgid 消息队列id

msgp 消息缓冲区地址

通常如下

struct msgbuf{

long mtype;//消息类型

char mtext[1];///消息正文

}

size 消息正文长度不包含消息类型指针变量

msgflg 标志位 0 或 IPC_NOWAIT

消息接收 – msgrcv

#include <sys/ipc.h>
 #include <sys/msg.h>
 int msgrcv(int msgid, void *msgp, size_t size, long msgtype,
                   int msgflg);


成功时返回收到的消息长度,失败时返回-1

msgid 消息队列id

msgp 消息缓冲区地址

size 指定接收的消息长度

msgtype 指定接收的消息类型

0:接收消息队列中第一个

大于0:接收消息队列中第一个类型为msgtype的消息

小于0:接收队列中第一个类型值不小于msgtype绝对值且类型最小的消息

msgflg 标志位 0 或 IPC_NOWAIT

MSG_NOERROR:返回消息比msgsz字节多,则消息会截断,且不提示。

IPC_NOWAIT:无消息立即返回

0: 阻塞等到直接接收一条相应类型的消息

控制消息队列 – msgctl

#include <sys/ipc.h>
 #include <sys/msg.h>
 int msgctl(int msgid, int cmd, struct msqid_ds *buf);


成功时返回0,失败时返回-1

msgid 消息队列id

cmd 要执行的操作 IPC_STAT / IPC_SET / IPC_RMID

buf 存放消息队列属性的地址

示例代码

send


#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct{
        long type;
        char txt[64];
}MSG;
#define LEN sizeof(MSG) - sizeof(long)
int main(){
        key_t ipkey;
        int msgid;
        MSG msg_t;
        ipkey = ftok(".",23);
        if(ipkey == -1){
                perror("ftok");
                return -1;
        }
        msgid = msgget(ipkey, IPC_CREAT|0666);
        if(msgid == -1){
                perror("msgget");
                return -1;
        }
        msg_t.type = 1;
        strcpy(msg_t.txt, "msg type one");
        msgsnd(msgid, &msg_t, LEN, 0);
        return 0;
}

read:


#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
typedef struct{
        long type;
        char txt[64];
}MSG;
#define LEN sizeof(MSG) - sizeof(long)
int main(){
        int re;
        key_t ipkey;
        int msgid;
        MSG msg_t;
        ipkey = ftok(".",23);
        if(ipkey == -1){
                perror("ftok");
                return -1;
        }
        msgid = msgget(ipkey, IPC_CREAT|0666);
        if(msgid == -1){
                perror("msgget");
                return -1;
        }
        while(1){
                re = msgrcv(msgid, &msg_t, LEN, 0, 0);
                printf("receive msg = %s\n",msg_t.txt);
                if(re < 0){
                        break;
                }
        }
        return 0;
}


信号灯

信号灯也叫信号量,用于进程/线程同步或互斥的机制

信号灯的类型


Posix 无名信号灯(前面线程学过)

Posix有名信号灯

System V 信号灯

信号灯的含义


计数信号灯

信号灯特点

System V 信号灯是一个或多个计数信号灯的集合

可同时操作集合中的多个信号灯

申请多个资源时避免死锁

使用步骤

打开/创建信号灯 semget

信号灯初始化 semctl

P/V操作 semop

删除信号灯 semctl

信号灯创建/打开 – semget

#include <sys/ipc.h>
 #include <sys/sem.h>
 int semget(key_t key, int nsems, int semflg);



成功时返回信号灯的id,失败时返回-1

key 和消息队列关联的key IPC_PRIVATE 或 ftok

nsems 集合中包含的计数信号灯个数

semflg 标志位 IPC_CREAT|0666 IPC_EXCL

信号灯初始化 – semctl

#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, …);


成功时返回0,失败时返回EOF

semid 要操作的信号灯集id

semnum 要操作的集合中的信号灯编号

cmd 执行的操作 SETVAL IPC_RMID

union semun 取决于cmd

信号灯P/V操作 – semop

#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sembuf, unsigned nsops);


成功时返回0,失败时返回-1

semid 要操作的信号灯集id

sembuf 描述对信号灯操作的结构体(数组)

nsops 要操作的信号灯的个数

信号灯操作 – sembuf

struct  sembuf 
 {
     short  sem_num;
     short  sem_op;
     short  sem_flg;
 };


semnum 信号灯编号

sem_op -1:P操作 1:V操作

sem_flg 0 / IPC_NOWAIT

使用示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <errno.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <string.h>
#define SEM_READ 0
#define SEM_WRITE 0
union semun{
        int Val;
};
void poperation(int index, int semid){
        struct sembuf sop;
        sop.sem_num = index;
        sop.sem_op = -1;
        sop.sem_flg = 0;
        semop(semid, &sop,1);
}
void voperation(int index, int semid){
        struct sembuf sop;
        sop.sem_num = index;
        sop.sem_op = 1;
        sop.sem_flg = 0;
        semop(semid, &sop,1);
}
int main(){
        key_t key;
        int semid,shmid;
        char *shmaddr;
        pid_t pid;
        key = ftok(".",123);
        semid = semget(key, 2, IPC_CREAT|0777);
        if(semid < 0){
                perror("semget");
                return -1;
        }
        shmid = shmget(key, 256, IPC_CREAT|0777);
        if(shmid < 0){
                perror("shmget");
                return -1;
        }
        union semun myun;
        myun.Val = 0;
        semctl(semid, SEM_READ, SETVAL, myun);
        myun.Val = 1;
        semctl(semid, SEM_WRITE, SETVAL, myun);
        pid = fork();
        if(pid < 0){
                perror("fork");
                return -1;
        }else if(pid == 0){
                shmaddr = (char *)shmat(shmid, NULL, 0);
                while(1){
                        poperation(SEM_READ, semid);
                        printf("getshm:%s",shmaddr);
                        voperation(SEM_WRITE, semid);
                }
        }else{
                shmaddr = (char *)shmat(shmid, NULL, 0);
                while(1){
                        poperation(SEM_WRITE, semid);
                        printf("please input char to shm:");
                        fgets(shmaddr, 32, stdin);
                        voperation(SEM_READ, semid);
                }
        }
}


写在最后

今天讲完了进程相关的内容,这部分内容比较枯燥,感觉过于复杂,想要重构,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!明天开始进入网络编程!最后三连即可提高学习效率!!!


另外我在更新的就是算法笔记的一些例题笔记,这个系列是用于提高我的算法能力,如果有兴趣对算法领域感兴趣找不到合适的入门文章也可以追更,如果我更新的太慢了请大家点赞收藏,一键三连才能更有更新的动力呀0.0


相关文章
|
4月前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
72 3
|
4月前
|
Python
解锁Python并发新世界:线程与进程的并行艺术,让你的应用性能翻倍!
【7月更文挑战第9天】并发编程**是同时执行多个任务的技术,提升程序效率。Python的**threading**模块支持多线程,适合IO密集型任务,但受GIL限制。**multiprocessing**模块允许多进程并行,绕过GIL,适用于CPU密集型任务。例如,计算平方和,多线程版本使用`threading`分割工作并同步结果;多进程版本利用`multiprocessing.Pool`分块计算再合并。正确选择能优化应用性能。
36 1
|
2月前
|
网络协议 C语言
C语言 网络编程(十三)并发的TCP服务端-以进程完成功能
这段代码实现了一个基于TCP协议的多进程并发服务端和客户端程序。服务端通过创建子进程来处理多个客户端连接,解决了粘包问题,并支持不定长数据传输。客户端则循环发送数据并接收服务端回传的信息,同样处理了粘包问题。程序通过自定义的数据长度前缀确保了数据的完整性和准确性。
|
3月前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
2月前
|
C语言
C语言 网络编程(八)并发的UDP服务端 以进程完成功能
这段代码展示了如何使用多进程处理 UDP 客户端和服务端通信。客户端通过发送登录请求与服务端建立连接,并与服务端新建的子进程进行数据交换。服务端则负责接收请求,验证登录信息,并创建子进程处理客户端的具体请求。子进程会创建一个新的套接字与客户端通信,实现数据收发功能。此方案有效利用了多进程的优势,提高了系统的并发处理能力。
|
2月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
57 0
|
3月前
|
存储 缓存 NoSQL
进程内缓存助你提高并发能力!
进程内缓存助你提高并发能力!
|
4月前
|
数据库 数据安全/隐私保护 C++
Python并发编程实战:线程(threading)VS进程(multiprocessing),谁才是并发之王?
【7月更文挑战第10天】Python并发对比:线程轻量级,适合I/O密集型任务,但受GIL限制;进程绕过GIL,擅CPU密集型,但通信成本高。选择取决于应用场景,线程利于数据共享,进程利于多核利用。并发无“王者”,灵活运用方为上策。
54 2
|
4月前
|
大数据 API 数据处理
Python高手都在用的并发秘籍:解锁线程与进程的终极奥义,性能飙升不是梦!
【7月更文挑战第8天】Python并发编程提升性能,线程(threading)适合I/O密集型任务,如网络请求,通过`start()`和`join()`实现并发。进程(multiprocessing)利用多核CPU,适用于CPU密集型任务,如大数据处理。结合两者可优化混合任务,实现最佳并发效果。
34 1
|
4月前
|
消息中间件 算法 Java
(十四)深入并发之线程、进程、纤程、协程、管程与死锁、活锁、锁饥饿详解
本文深入探讨了并发编程的关键概念和技术挑战。首先介绍了进程、线程、纤程、协程、管程等概念,强调了这些概念是如何随多核时代的到来而演变的,以满足高性能计算的需求。随后,文章详细解释了死锁、活锁与锁饥饿等问题,通过生动的例子帮助理解这些现象,并提供了预防和解决这些问题的方法。最后,通过一个具体的死锁示例代码展示了如何在实践中遇到并发问题,并提供了几种常用的工具和技术来诊断和解决这些问题。本文旨在为并发编程的实践者提供一个全面的理解框架,帮助他们在开发过程中更好地处理并发问题。

热门文章

最新文章