»
(008)CubeIDE实现USB外设*
(018)C99 的指针*
(019)C99 只有值赋值,没有move*
(022)POSIX线程库pthread的同步锁*
(023)POSIX线程库pthread的多线程*
(027)C99标准库中的计时与等待 *
(028)C语言中的高精度计算库GMP *
(029)C语言中的Web服务*
(030)C语言中的字符转码ICU*
(031)从几个方面显然C语言比C++效率高*
🍓(012)Linux系统中的进程间通信
Linux系统中的进程间通信:
消息队列与信号量在Linux中都是文件描述符
1)信号量
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
name:信号量在文件系统上的文件名;
oflag:可以为O_CREAT|O_EXCL(仅创建)、O_CREAT(创建或打开)、0(仅打开已存在);
mode:信号量的权限位,比如:0644;
value:信号量的初值,比如:1,不能大于SEM_VALUE_MAX;
int sem_close(sem_t *sem);//关闭信号量指针
int sem_unlink(const char *name);//删除此进程对信号量的引用
int sem_post(sem_t *sem);//V操作,将信号量的值+1
int sem_wait(sem_t *sem);//P操作,如果当前信号量小于等于0则阻塞,将信号量的值-1,否则直接将信号量-1
int sem_trywait(sem_t *sem);//非阻塞的sem_wait
int sem_getvalue(sem_t *sem, int *sval);//获取当前信号量的值,如果当前信号量上锁则返回0或者负值,其绝对值为信号量的值
2)POSIX消息队列操作
#include <mqueue.h>
1. 创建/打开消息队列
使用 mq_open() 来创建或打开一个消息队列。
2. 发送消息
使用 mq_send() 来将消息发送到消息队列中。
3. 接收消息
使用 mq_receive() 从消息队列中接收消息。
4. 关闭消息队列
使用 mq_close() 来关闭消息队列。
5. 删除消息队列
使用 mq_unlink() 删除消息队列(通常在所有进程不再使用队列时删除)。
3)例子(epoll实时消息传递):
实时读取数据进程:
sem_t* serious_alarm_sem = sem_open("/serious-alarm-high-water-level",O_CREAT,0644,0); //创建信号量,初值为0
sem_t* alarm_sem = sem_open("/alarm-high-water-level",O_CREAT,0644,0); //创建信号量,初值为0
int epoll_fds = epoll_create(10); //epoll缓冲区,可监听10个fd
struct epoll_event alarm_ev_descr; //alarm事件
alarm_ev_descr.data.fd = fd; //fd 即为open设备返回的句柄////////////////////////////////用于后续代码中的文件描述符的传递////////////////////////////////
alarm_ev_descr.events = EPOLLPRI; //或者EPOLLIN,EPOLLIN表示文件描述符(FD)可以进行读取操作;EPOLLPRI表示设备的紧急数据可读取。////////////////////////////////指定监听的事件////////////////////////////////
epoll_ctl(epoll_fds,EPOLL_CTL_ADD,fd,&alarm_ev_descr); //注册新的fd到epoll_fds中,监听fd的EPOLLPRI事件
int len=0;
struct epoll_event* events = malloc(10, sizeof(struct epoll_event));//分配容纳10个事件的存储空间
while(true){ //实时读取主循环
int n = epoll_wait(epoll_fds,events,10,-1); //一直阻塞直到数据到来,-1表示一直等待;只要有未读完的数据就会直接返回
for(int i=0;i<n;i++){ //等到n个事件
if (events[i].events & EPOLLPRI) { //如果当前是EPOLLPRI事件,那么做以下事情
len=read(events[i].data.fd,buff,sizeof(buff)); //读取事件数据(每次读取最大的数据量大小与设备的缓冲区大小有关,比如TCP为8K)
.... //拼装数据形成设备指令
if(level>5.1){ //如果指定值大于5.1
post_sem(serious_alarm_sem); //发完信号量就进入下一读取过程,以提高实时性(严重告警信号量+1)
}else if(level>2.1){
post_sem(alarm_sem); //发完信号量就进入下一读取过程,以提高实时性(告警信号量+1)
}
}
}
//主循环中使用epoll后不需要使用usleep(1),不会导致CPU100%,因为epoll在设备缓冲区没有数据的时候会wait、会阻塞、会释放CPU
}
告警服务进程:
sem_t* alarm_sem=sem_open("/alarm-high-water-level",O_CREAT,0644,0);//创建信号量,初值为0
while(true){
sem_wait(alarm_sem);
mq_send(...);//立即响应,发送到消息队列:要求播放一次"/home/reservoir-water-level-alarm.mp3"
}
严重告警服务进程:
sem_t* serious_alarm_sem=sem_open("/serious-alarm-high-water-level",O_CREAT,0644,0);//创建信号量,初值为0
while(true){
sem_wait(serious_alarm_sem);
mq_send(...);//立即响应,发送到消息队列:要求播放一次"/home/reservoir-water-level-serious-alarm.mp3"
}
播放音频服务进程:
bool bIsPlaying=false;
char last_play_filename[1024]={0};
while(true){
mq_receive(...);//接收消息队列
if(bIsPlaying&&strcmp(play_filename, last_play_filename)==0){
//正在播放且曲目一致,则不做任何事
}else if(bIsPlaying&&strcmp(play_filename, last_play_filename)!=0){
//正在播放,但曲目不一致,则立即响应为新曲目
stop_play_thread();
start_play_once_thread(play_filename);
}else{
//如果未在播放,则立即播放
start_play_once_thread(play_filename);
}
}
无论是sched_yield的尝试,还是对while(true){}的资源消耗的错误理解,都是为了一直想推广的、提高计算机系统实时性的去sleep化:
epoll与一般阻塞模型的比较:epoll不需要多子进程、epoll的并发更高。
epoll与select的比较:epoll会wait、不需要sleep或者usleep,epoll的实时性更高。