目录
2. std::chrono::steady_clock, std::chrono::duration_cast
4. __sync_bool_compare_and_swap原子函数
1. POSIX 消息队列 mq_open
让我们先来看看,一对使用POSIX消息队列进行通信的进程组的例子。
发送端:send.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
int main(int argc, char const *argv[])
{
mqd_t mqd;
uint8_t buf[100];
struct mq_attr mq_attr_param = {
.mq_flags = O_NONBLOCK,
.mq_maxmsg = 10,
.mq_msgsize = 100
}; // O_NONBLOCK 设置为非阻塞, 最大消息数为10, 每个消息最大为100字节
mqd = mq_open("/mq_test", O_CREAT|O_RDWR, S_IRWXU|S_IRWXG, &mq_attr_param); // 以读写模式新建一个消息队列,名字约定为/mq_test,
if (mqd == -1)
{
printf("mq open fail\r\n");
exit(EXIT_FAILURE);
}
mq_setattr( mqd, &mq_attr_param, NULL ); // 设置队列参数 设置为非阻塞
memset(&mq_attr_param, 0, sizeof(struct mq_attr));
mq_getattr( mqd, &mq_attr_param ); // 再获取队列参数,确认一下
printf("mq_attr_param.mq_flags = %d\r\n", (mq_attr_param.mq_flags &= O_NONBLOCK) != 0);
printf("mq_attr_param.mq_maxmsg = %ld\r\n", mq_attr_param.mq_maxmsg);
printf("mq_attr_param.mq_msgsize = %ld\r\n", mq_attr_param.mq_msgsize);
while (1)
{
printf("Please enter the content to be sent:\r\n");
gets(buf);
mq_send(mqd, buf, strlen(buf), 0); // buf为消息内容,优先级为0
if (strcmp(buf, "close") == 0) // 发送close,关闭队列
{
if (mq_close(mqd) == 0)
printf("mq_close ok\r\n");
else
printf("mq_close fail\r\n");
break;
}
else if (strcmp(buf, "unlink") == 0) // 发送unlink,销毁队列
{
if (mq_unlink("/mq_test") == 0)
printf("mq_unlink ok\r\n");
else
printf("mq_unlink fail\r\n");
break;
}
}
return 0;
}
接收端:receive.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
int main(int argc, char const *argv[])
{
mqd_t mqd;
uint8_t buf[100];
uint16_t len;
mqd = mq_open("/mq_test", O_RDWR); // mq_open是可变参数函数。
if (mqd == -1)
{
printf("mq open fail\r\n");
exit(EXIT_FAILURE);
}
while (1)
{
memset(buf, 0, 100);
len = mq_receive(mqd, buf, 100, NULL);
if (len > 0)
{
if (strcmp(buf, "close") == 0) // 接收到close,关闭队列
{
if (mq_close(mqd) == 0)
printf("mq_close ok\r\n");
else
printf("mq_close fail\r\n");
break;
}
else if (strcmp(buf, "unlink") == 0) // 接收到unlink,销毁队列
{
if (mq_unlink("/mq_test") == 0)
printf("mq_unlink ok\r\n");
else
printf("mq_unlink fail\r\n");
break;
}
else
{
printf("%s\r\n", buf);
}
}
else
{
printf("len = %d\r\n", len);
}
}
return 0;
}
编译这2个程序,注意:必须加上 -lrt 参数 (-lrt – 提供POSIX实时扩展)
gcc send.c -o send -lrt
运行结果:
犹如乒乓球一般,本地的2个独立进程,一发一收。
参考:POSIX消息队列详解与示例_无聊到发博客的菜鸟的博客-CSDN博客_posix消息队列
看看各个函数原型:
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
// 返回消息队列描述符,失败返回-1
其中参数结构体原型:
struct mq_attr
{
long mq_flags; //阻塞标志,0或O_NONBLOCK,只能通过mq_setattr()设置
long mq_maxmsg; //最大消息数,只能通过mq_open()设置
long mq_msgsize; //每个消息最大大小,只能通过mq_open()设置
long mq_curmsgs; //当前消息数
};
int mq_close(mqd_t mqdes);
int mq_unlink(const char *name);
int mq_getattr(mqd_t mqdes, struct mq_attr *attr);
// 成功返回0,失败返回-1
int mq_setattr(mq_t mqdes, const struct mq_attr *newattr, struct mq_attr *oldattr);
// 成功返回0,失败返回-1
int mq_send(mqd_t mqdes, const char *msg_ptr,size_t msg_len, unsigned int msg_prio);
// 成功返回0,失败返回-1
- msg_len参数指定了msg_ptr指向的消息的长度,其值必须小于或等于队列的mq_msgsize特性,否则mq_send()就会返回EMSGSIZE错误。长度为零的消息是允许的
- 每条消息都拥有一个用非负整数表示的优先级,它通过msg_prio参数指定。消息在队列中是按照优先级倒序排列的(即0表示优先级最低)。当一条消息被添加到队列中时,它会被放置在队列中具有相同优先级的所有消息之后。如果一个应用程序无需使用消息优先级,那么只需要将msg_prio指定为0即可
- 如果消息队列满了,阻塞模式下将一直阻塞,非阻塞模式下直接返回错误。
ssize_t mq_receive(mqd_t mqdes, char *mdg_ptr,size_t msg_len, unsigned int *msg_prio);
// 返回接收长度,失败返回-1
- mq_receive()函数从mqdes引用的消息队列中删除一条优先级最高、存在时间最长的消息并将删除的消息放置在msg_ptr指向的缓冲区
- 使用msg_len参数来指定msg_ptr指向的缓冲区中的可用字节数
- 不管消息的实际大小是什么,msg_len必须要大于或等于队列的mq_msgsize特性,否则mq_receive()就会失败并返回EMSGSIZE错误。如果不清楚一个队列的mq_msgsize特性的值,那么可以使用mq_getattr()来获取这个值
- 如果msg_prio不为NULL,那么接收到的消息的优先级会被复制到msg_prio指向的位置处
参考:POSIX消息队列详解与示例_无聊到发博客的菜鸟的博客-CSDN博客_posix消息队列
2. std::chrono::steady_clock, std::chrono::duration_cast
chrono 是C++11新加入的方便时间日期操作的标准库,它既是相应的头文件名称,也是std命名空间下的一个子命名空间,所有时间日期相关定义均在std::chrono命名空间下。
chrono库主要包含了三种类型:duration, time_point 和 clock。
先看个例子:
#include <iostream>
#include <string>
#include <chrono>
using namespace std;
int main()
{
chrono::hours hour_time = chrono::hours(1);
chrono::minutes minutes_time = chrono::duration_cast< chrono::minutes>(hour_time);
chrono::seconds seconds_time = chrono::duration_cast< chrono::seconds>(hour_time);
chrono::milliseconds milliseconds_time = chrono::duration_cast< chrono::milliseconds>(hour_time);
chrono::microseconds microseconds_time = chrono::duration_cast< chrono::microseconds>(hour_time);
cout << "1小时可转换为 \n"
<< minutes_time.count() << "分钟 \n"
<< seconds_time.count() << "秒 \n"
<< milliseconds_time.count() << "毫秒 \n"
<< microseconds_time.count() << "微秒" << endl;
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
for( int i = 0; i < 10000000; i++ );
chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
chrono::duration<double, milli> TimeSpan = chrono::duration<double, milli> (t2 - t1);
cout << "for 循环耗时:" << TimeSpan.count() << " ms." << endl;
return 0;
}
运行结果:
steady_clock 是 chrono命名空间下的结构体,包含成员类型time_point,成员函数now()。可以用来获取时间间隔。
duration_cast<> 是 chrono命名空间下的模板函数,可以进行时间类型转换。
duration<> 是 chrono命名空间下的结构体模板,可以转换时间间隔的单位和精度。
3. Linux系统调用之 syscall()
linux系统调用有四种调用请求方式:
- 使用 glibc 库函数。
- 使用 syscall函数。
- 通过linux系统调用宏。
- 使用软中断陷入。
笔者使用较多的是方式1-- 使用glibc库函数。glibc是GNU 发布的开源的标准 C 库,除了字符串处理、数学运算等用户态服务之外,重要的是封装了操作系统提供的系统服务,即系统调用的封装(本质上还是执行的系统调用)。
如:用户态的 open 调用了 内核态的 sys_open;
用户态的 printf 调用了 内核态的sys_open、sys_mmap、sys_write、sys_close;
用户态的 malloc, free 调用了 内核态的 sys_brk.
但是,某些情况下,glibc 没有封装某个内核提供的系统调用时,我们就没办法通过使用glibc的方法来调用该系统调用。假设我们自己通过编译内核增加了一个系统调用或者驱动,这时 glibc 不可能有我们自己新增系统调用的封装 API。
在这种情况下,可以利用 glibc 提供的 syscall 库函数(方式2)直接调用。syscall是一个通过特定子功能号和特定参数调用汇编语言接口的库函数。该函数定义在 unistd.h 头文件中,函数原型如下:
long int syscall (long int sysno, ...)
sysno :为系统调用号,每个系统调用都有唯一的系统调用号来标识。
… :可变长的参数,为系统调用所带的参数,不同的系统调用可带0~5个不等的参数。
返回值:该函数返回值为特定系统调用的返回值,如果系统调用失败则返回 -1。
举个例子:
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t tid;
tid = syscall(SYS_gettid);
printf("tid : %d\n",tid);
tid = syscall(SYS_mkdir, "./test");
printf("tid : %d ...\n",tid);
return 0;
}
执行结果:
SYS_gettid, SYS_mkdir都是系统调用的标识宏。syscall通过这些标识宏来找到内核态的调用并执行之。
参考:linux系统调用的来龙去脉(下)_liyinuo2017的博客-CSDN博客_linux系统调用的过程
4. __sync_bool_compare_and_swap原子函数
int __sync_bool_compare_and_swap(&value, old, new);
当且仅当 value值与 old 相等时,才把 new 赋给 value。
返回值:
写入新值成功返回1(即旧值与存储中当前的值一致时写入成功,返回1);
写入失败返回0.
例子:
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int ret = 0, old = 23, new = 89;
int value1 = 23;
ret = __sync_bool_compare_and_swap( &value1, old, new );
printf("ret: %d, value1 is now: %d \n", ret, value1);
int value2 = 24;
ret = __sync_bool_compare_and_swap( &value2, old, new );
printf("ret: %d, value2 is now: %d \n", ret, value2);
return 0;
}
执行结果:
虽然使用if语句判断一下也可以实现相同的逻辑,但是!
__sync_bool_compare_and_swap() 是一个原子操作,不会被中断。所以很适合用在多线程pthread 程序里面。