Linux应用编程(2)

目录

1. POSIX 消息队列  mq_open

2. std::chrono::steady_clock, std::chrono::duration_cast

3. Linux系统调用之 syscall()

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_clockchrono命名空间下的结构体,包含成员类型time_point,成员函数now()。可以用来获取时间间隔

duration_cast<>chrono命名空间下的模板函数,可以进行时间类型转换

duration<>chrono命名空间下的结构体模板,可以转换时间间隔的单位和精度。

参考:C++11 - std::chrono - 使用std::chrono::duration_cast进行时间转换_HW140701的博客-CSDN博客_std::chrono::duration_cast


3. Linux系统调用之 syscall()

linux系统调用有四种调用请求方式:

  1. 使用 glibc 库函数。
  2. 使用 syscall函数。
  3. 通过linux系统调用宏。
  4. 使用软中断陷入。

笔者使用较多的是方式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_gettidSYS_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 程序里面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux 应用编程是指在 Linux 操作系统上开发应用程序的过程。 Linux 操作系统是一种开源的操作系统,支持多种编程语言,如 C, C++, Python, Java 等。 在 Linux 上开发应用程序的过程包括以下几个步骤: 1. 安装必要的开发工具: 在 Linux 上开发应用程序,需要安装一些必要的开发工具,如编译器、调试器、版本控制系统等。 2. 选择合适的编程语言: Linux 支持多种编程语言,可以根据自己的喜好选择合适的语言进行开发。 3. 编写代码: 根据应用程序的功能和需求,编写代码。 4. 编译代码: 使用编译器将代码编译成可执行文件。 5. 调试代码: 如果程序中存在错误或者 bug,可以使用调试器来查找并修复问题。 6. 打包应用程序: 将应用程序打包成可以在其他计算机上安装的格式,方便分发和使用。 ### 回答2: Linux 应用编程指的是在Linux操作系统下开发和编写应用程序的过程。Linux作为一个开源的操作系统,拥有广泛的应用领域,包括服务器、嵌入式系统、移动设备等,因此掌握Linux应用编程有着重要的意义。 首先,Linux应用编程需要熟悉Linux操作系统的特性和架构。Linux的核心理念是多任务、多用户的设计哲学,因此在编写应用程序时需要充分利用Linux的进程、线程、信号等机制,实现程序的并发执行和资源的合理管理。 其次,Linux提供了丰富的系统和网络编程接口,使得应用程序可以直接访问底层的系统资源和网络功能。例如,可以利用Linux提供的文件操作接口读写文件,使用套接字编程实现网络通信,调用系统调用实现进程间通信等。 另外,Linux应用编程还需要掌握一些常用的开发工具和库。例如,用C/C++编写应用程序时,可以使用GNU工具链来进行编译和调试;可以使用标准库和第三方库来简化开发过程。此外,还可以使用Shell脚本编写一些自动化任务,提高开发效率。 在开发过程中,还需要注意Linux的安全性和权限管理。Linux提供了严格的权限控制机制,要求应用程序必须拥有适当的权限才能执行某些操作。因此,在编写应用程序时要遵循最小权限原则,以及避免一些常见的安全漏洞。 总而言之,Linux应用编程是一门有挑战性的技术,需要掌握Linux操作系统的原理和特性,熟悉系统和网络编程接口,以及使用相关的开发工具和库。掌握了这些知识和技能,可以为Linux平台上的应用程序开发和优化提供支持,提高开发效率和程序性能。 ### 回答3: Linux 应用编程是指在Linux操作系统上进行软件开发和编程的过程。Linux是一种开源的操作系统,它提供了丰富的开发工具和库,使得开发人员可以轻松地编写各种应用程序。 Linux 应用编程可以分为两个主要方面:系统编程应用编程。 系统编程是指与操作系统直接进行交互,使用系统调用和库函数来开发底层的系统功能。如文件和进程管理,网络编程,设备驱动程序等。系统编程需要深入了解操作系统的内部工作原理和相关概念。 应用编程是指开发各种应用程序,例如桌面应用、服务器应用、嵌入式应用等。开发者可以使用各种编程语言如C、C++、Python等来进行应用编程,在Linux操作系统上构建功能丰富、高效稳定的应用程序。Linux提供了众多开发工具和库,如GTK+、Qt等图形界面开发库,以及MySQL等数据库工具,方便开发者进行应用程序开发。 Linux应用编程的好处有很多。首先,Linux是开源的,开发者可以使用自己的方式自由定制和修改系统。这使得开发者能够更好地理解系统和进行调试。其次,Linux提供了丰富的开发工具和库,使得开发者可以轻松地编写各种应用程序。再次,Linux系统的稳定性和高效性使得开发的应用程序能够在一个可靠的环境下运行。 总结来说,Linux应用编程是基于Linux操作系统进行软件开发和编程的过程。通过系统编程应用编程,开发者可以构建功能丰富、高效稳定的应用程序。Linux的开源特性和丰富的开发工具使得开发者能够更好地掌握和使用系统,从而开发出更好的应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值