自学内容网 自学内容网

Linux-C/C++--初探linux应用编程概念

               

        对于大多数首次接触 Linux 应用编程的读者来说,可能对应用编程(也可称为系统编程)这个概念并不 太了解,所以在正式学习 Linux 应用编程之前,笔者有必要向大家介绍这些简单基本的概念,从整体上认识 到应用编程为何物?与驱动编程、裸机编程有何不同? 了解本章所介绍的内容是掌握应用编程的先决条件,所以本章主要内容便是对 Linux 应用编程进行一 个简单地介绍,让读者对此有一个基本的认识。 本章将会讨论如下主题内容。

何为系统调用;

何为库函数;

应用程序的 main()函数;

应用程序开发环境的介绍。

一、系统调用

        系统调用(system call)其实是 Linux 内核提供给应用层的应用编程接口(API),是 Linux 应用层进入 内核的入口。不止 Linux 系统,所有的操作系统都会向应用层提供系统调用,应用程序通过系统调用来使用 操作系统提供的各种服务。

        通过系统调用,Linux 应用程序可以请求内核以自己的名义执行某些事情,譬如打开磁盘中的文件、读 写文件、关闭文件以及控制其它硬件外设。

        通过系统调用 API,应用层可以实现与内核的交互,其关系可通过下图简单描述:

                                                                      

        内核提供了一系列的服务、资源、支持一系列功能,应用程序通过调用系统调用 API 函数来使用内核 提供的服务、资源以及各种各样的功能,如果大家接触过其它操作系统编程,想必对此并不陌生,譬如 Windows 应用编程,操作系统内核一般都会向应用程序提供应用编程接口 API,否则我们将我无法使用操 作系统。

应用编程与裸机编程、驱动编程有什么区别?

在学习应用编程之前,相信大家都有过软件开发经验,譬如 51STM32 等单片机软件开发、以及嵌入 式 Linux 硬件平台下的驱动开发等,51STM32 这类单片机的软件开发通常是裸机程序开发,并不会涉及 到操作系统的概念,那应用编程与裸机编程以及驱动开发有什么区别呢?

        就拿嵌入式 Linux 硬件平台下的软件开发来说,我们大可将编程分为三种,分别为裸机编程、Linux 驱 动编程以及 Linux 应用编程。首先对于裸机编程这个概念来说很好理解,一般把没有操作系统支持的编程环 境称为裸机编程环境,譬如单片机上的编程开发,编写直接在硬件上运行的程序,没有操作系统支持;狭义 上 Linux 驱动编程指的是基于内核驱动框架开发驱动程序,驱动开发工程师通过调用 Linux 内核提供的接口 完成设备驱动的注册,驱动程序负责底层硬件操作相关逻辑,如果学习过 Linux 驱动开发的读者,想必对此 并不陌生;而 Linux 应用编程(系统编程)则指的是基于 Linux 操作系统的应用编程,在应用程序中通过调 用系统调用 API 完成应用程序的功能和逻辑,应用程序运行于操作系统之上。通常在操作系统下有两种不 同的状态:内核态和用户态,应用程序运行在用户态、而内核则运行在内核态。

        关于应用编程这个概念,以上给大家解释得很清楚了,笔者以实现点亮一个 LED 功能为例,给大家简 单地说明三者之间的区别,LED 裸机程序如下所示:

static void led_on(void)
{
    /* 点亮 LED 硬件操作代码 */
}

static void led_off(void)
{
    /* 熄灭 LED 硬件操作代码 */
}

int main(void)
{
    /* 用户逻辑 */
    for ( ; ; ) 
    {
        led_on(); //点亮 LED
        delay(); //延时
        led_off(); //熄灭 LED
        delay(); //延时
    }
}

        可以看到在裸机程序当中,LED 硬件操作代码与用户逻辑代码全部都是在同一个源文件(同一个工程) 中实现的,硬件操作代码与用户逻辑代码没有隔离,没有操作系统支持,代码编译之后直接在硬件平台运 行,俗称“裸跑”。我们再来看一个 Linux 系统下的 LED 驱动程序示例代码,如下所示:

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
static void led_on(void)
{
    /* 点亮 LED 硬件操作代码 */
}

static void led_off(void)
{
    /* 熄灭 LED 硬件操作代码 */
}

static int led_open(struct inode *inode, struct file *filp)
{
    /* 打开设备时需要做的事情 */
}

static ssize_t led_write(struct file *filp, const char __user *buf,size_t size, loff_t *offt)
{
    int flag;
    /* 获取应用层 write 的数据,存放在 flag 变量 */
    if (copy_from_user(&flag, buf, size))
        return -EFAULT;
    /* 判断用户写入的数据,如果是 0 则熄灭 LED,如果是非 0 则点亮 LED */
    if (flag)
        led_on();
    else
        led_off();
    return 0;
}
static int led_release(struct inode *inode, struct file *filp)
{
    /* 关闭设备时需要做的事情 */
}

static struct file_operations led_fops = {
 .owner = THIS_MODULE,
 .open = led_open,
 .write = led_write,
 .release = led_release,
};

static int led_probe(struct platform_device *pdev)
{
    /* 驱动加载时需要做的事情 */
}

static int led_remove(struct platform_device *pdev)
{
    /* 驱动卸载时需要做的事情 */
}

static const struct of_device_id led_of_match[] = {
    {.compatible = "alientek,led", },
    { /* sentinel */ },
};

MODULE_DEVICE_TABLE(of, led_of_match);
static struct platform_driver led_driver = {
 .driver = {
 .owner = THIS_MODULE,
 .name = "led",
 .of_match_table = led_of_match,
 },
 .probe = led_probe,
 .remove = led_remove,
};
module_platform_driver(led_driver);
MODULE_DESCRIPTION("LED Driver");
MODULE_LICENSE("GPL");


        以上并不是一个完整的 LED 驱动代码,如果没有接触过 Linux 驱动开发的读者,看不懂也没有关系, 并无大碍,此驱动程序使用了最基本的字符设备驱动框架编写而成,非常简单;led_fops 对象中提供了 open、 write、release 方法,当应用程序调用 open 系统调用打开此 LED 设备时会执行到 led_open 函数,当调用 close 系统调用关闭 LED 设备时会执行到 led_release 函数,而调用 write 系统调用时会执行到 led_write 函数,此 驱动程序的设定是当应用层调用 write 写入 0 时熄灭 LEDwrite 写入非 0 时点亮 LED

         驱动程序属于内核的一部分,当操作系统启动的时候会加载驱动程序,可以看到 LED 驱动程序中仅仅 实现了点亮/熄灭 LED 硬件操作相关逻辑代码,应用程序可通过 write 这个系统调用 API 函数控制 LED 亮 灭;接下来我们看看 Linux 系统下的 LED 应用程序示例代码,如下所示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char **argv)
{
 int fd;
 int data;
 fd = open("/dev/led", O_WRONLY);//打开 LED 设备(假定 LED 的设备文件为/dev/led)
 if (0 > fd)
 return -1;
 for ( ; ; ) {
    data = 1;
    write(fd, &data, sizeof(data)); //写 1 点亮 LED
    sleep(1); //延时 1 秒
    data = 0;
    write(fd, &data, sizeof(data)); //写 0 熄灭 LED
    sleep(1); //延时 1 秒
 }
 close(fd);
 return 0;
}

此应用程序也非常简单,仅只需实现用户逻辑代码即可,循环点亮、熄灭 LED,并不需要实现硬件操 作相关,示例代码中调用了 openwriteclose 这三个系统调用 API 接口,open close 分别用于打开/关闭 LED 设备,write 写入数据传给 LED 驱动,传入 0 熄灭 LED,传入非 0 点亮 LED

二、库函数

        前面给大家介绍了系统调用,系统调用是内核直接向应用层提供的应用编程接口,譬如 openwrite、 read、close 等,关于这些系统调用后面会给大家进行详细介绍。编写应用程序除了使用系统调用之外,我们 还可以使用库函数,本小节来聊一聊库函数。

        在Linux环境下,C语言程序的开发通常需要使用各种库函数来简化编程任务。Linux系统为C语言程序提供了丰富的标准库函数,涵盖了输入输出、字符串处理、内存管理、文件操作、时间和日期、数学运算等多个方面。

        以下是一些常用的C语言库函数的介绍:

2.1、标准输入输出库(stdio.h

该库包含了进行输入输出操作的函数。

printf:用于格式化输出。
int printf(const char *format, ...);
scanf:从标准输入读取数据,按照指定的格式。
int scanf(const char *format, ...);

fopen:打开一个文件。

FILE *fopen(const char *filename, const char *mode);

fclose:关闭打开的文件。

int fclose(FILE *stream);

fread:从文件中读取数据。

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

fprintf:向文件输出格式化数据。

int fprintf(FILE *stream, const char *format, ...);

2.2、字符串处理库(string.h

该库包含处理字符串和内存的常用函数。
strlen:返回字符串的长度。
size_t strlen(const char *str);

strcpy:将一个字符串复制到另一个字符串。

char *strcpy(char *dest, const char *src);

strcat:将两个字符串连接起来。

char *strcat(char *dest, const char *src);

strcmp:比较两个字符串。

int strcmp(const char *str1, const char *str2);

strtok:分割字符串为多个子字符串。

char *strtok(char *str, const char *delim);

memcpy:从源地址拷贝指定字节到目标地址。

void *memcpy(void *dest, const void *src, size_t n);

memset:将指定值的字节填充到内存块。

void *memset(void *str, int c, size_t n);

2.3、内存管理库(stdlib.h

该库包含了动态内存分配、进程控制等功能的函数。
malloc:分配指定大小的内存块。
void *malloc(size_t size);

calloc:分配内存块并初始化为零

void *calloc(size_t num, size_t size);

free:释放之前分配的内存块。

void free(void *ptr);
realloc:重新调整已分配内存的大小。
void *realloc(void *ptr, size_t new_size);

exit:终止程序并返回指定状态。

void exit(int status);

atoi:将字符串转换为整数。

int atoi(const char *str);

rand:生成随机数。

int rand(void);

srand:设置随机数种子。

void srand(unsigned int seed);

2.4、 数学库(math.h

该库提供了常用的数学函数。
sqrt:计算平方根。
double sqrt(double x);

pow:计算幂。

double pow(double base, double exponent);

sincostan:三角函数。

double sin(double x);
double cos(double x);
double tan(double x);

log:计算自然对数。

double log(double x);

exp:计算e的x次方。

double exp(double x);

ceil:返回大于或等于参数的最小整数。

double ceil(double x);

floor:返回小于或等于参数的最大整数。

double floor(double x);

2.5、 时间和日期库(time.h

该库包含了处理日期和时间的函数。

time:返回当前时间的秒数。

time_t time(time_t *t);

localtime:将时间转换为本地时间。

struct tm *localtime(const time_t *timep);

strftime:格式化日期和时间。

size_t strftime(char *str, size_t max, const char *format, const struct tm *tm);

difftime:计算两个时间点的差异(单位为秒)。

double difftime(time_t end, time_t beginning);

三、标准 C 语言函数库

        在 Linux 系 统 下 , 使 用 的 C 语 言 库 为 GNU C 语 言 函 数 库 ( 也 叫 作 glibc , 其 网 址 为 http://www.gnu.org/software/libc/),作为 Linux 下的标准 C 语言函数库。

进入到 http://www.gnu.org/software/libc/网址,如下所示:

点击上面的 Sources 选项可以查看它的源码实现:

glibc 源码的获取方式很简单,直接直接从 git 仓库下载,也可以通过 ftp 下载,如果大家有兴趣、或者 想要了解某一个库函数它的具体实现,那么就可以获取到它源码来进行分析,好了,这里就不再多说了!

确定 Linux 系统的 glibc 版本

前面提到过了,C 语言库是以动态库文件的形式提供的,通常存放在/lib 目录,它的命名方式通常是 libc.so.6,不过这个是一个软链接文件,它会链接到真正的库文件。 进入到 Ubuntu 系统的/lib 目录下,笔者使用的 Ubuntu 版本为 16.04,在我的/lib 目录下并没有发现 libc.so.6 这个文件,其实是在/lib/x86_64-linux-gnu 目录下,进入到该目录:

可以看到 libc.so.6 链接到了 libc-2.23.so 库文件,2.23 表示的就是这个 glibc 库的版本号为 2.23。除此之 外,我们还可以直接运行该共享库来获取到它的信息,如下所示:

从打印信息可以看到,笔者所使用的 Ubuntu 系统对应的 glibc 版本号为 2.23

四、main函数

        对学习过 C 语言编程的读者来说,譬如单片机编程、Windows 应用编程等,main 函数想必大家再熟悉 不过了,很多编程开发都是以 main 函数作为程序的入口函数,同样在 Linux 应用程序中,main 函数也是作 为应用程序的入口函数存在,main 函数的形参一般会有两种写法,如果执行应用程序无需传参,则可以写 成如下形式:

int main(void)
{
    /* 代码 */
}

如果在执行应用程序的时候需要向应用程序传递参数,则写法如下:

int main(int argc, char **argv)
{
    /* 代码 */
}

argc 形参表示传入参数的个数,包括应用程序自身路径和程序名,譬如运行当前目录下的 hello 可执行 文件,并且传入参数,如下所示:

./hello 112233

那么此时参数个数为 2,并且这些参数都是作为字符串的形式传递给 main 函数:

argv[0]等于"./hello"

argv[1]等于"112233"

有传参时 main 函数的写法并不只有这一种,只是这种写法最常用,对于其它的写法,后面学习过程中 如果遇到了再给大家进行讲解,这里暂时先不去管。

五、开发环境

        对于编程开发,大家可能都比较关心开发环境的问题,譬如本书将使用什么 IDE 编写应用程序之类的 问题,本小节将对开发环境的问题进行一个简单介绍。

        在 Linux 操作系统下,也有很多比较好用的 IDE 软件,可以帮助我们更为轻松的进行软件开发,譬如 Eclipse、vscode 等,如果你会使用 Eclipse,可以在 Ubuntu 系统下安装 Eclipse 进行 Linux 应用开发

        小编使用的是VSCode,vscode 是一个代码编辑器,提供了很多好用的插件,譬如语法检测、高亮显示、智能补全等,大家可以根据自己的选择安装插件。

本小节内容到此结束。2025年决定把linux-C/C++相关内容重温分享给大家。欢迎留言评论,感谢!!!

原文地址:https://blog.csdn.net/m0_46436890/article/details/145197292

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!