Linux-C/C++--初探linux应用编程概念
对于大多数首次接触 Linux 应用编程的读者来说,可能对应用编程(也可称为系统编程)这个概念并不 太了解,所以在正式学习 Linux 应用编程之前,笔者有必要向大家介绍这些简单基本的概念,从整体上认识 到应用编程为何物?与驱动编程、裸机编程有何不同? 了解本章所介绍的内容是掌握应用编程的先决条件,所以本章主要内容便是对 Linux 应用编程进行一 个简单地介绍,让读者对此有一个基本的认识。 本章将会讨论如下主题内容。
何为系统调用;
何为库函数;
应用程序的 main()函数;
应用程序开发环境的介绍。
一、系统调用
系统调用(system call)其实是 Linux 内核提供给应用层的应用编程接口(API),是 Linux 应用层进入 内核的入口。不止 Linux 系统,所有的操作系统都会向应用层提供系统调用,应用程序通过系统调用来使用 操作系统提供的各种服务。
通过系统调用,Linux 应用程序可以请求内核以自己的名义执行某些事情,譬如打开磁盘中的文件、读 写文件、关闭文件以及控制其它硬件外设。
通过系统调用 API,应用层可以实现与内核的交互,其关系可通过下图简单描述:
内核提供了一系列的服务、资源、支持一系列功能,应用程序通过调用系统调用 API 函数来使用内核 提供的服务、资源以及各种各样的功能,如果大家接触过其它操作系统编程,想必对此并不陌生,譬如 Windows 应用编程,操作系统内核一般都会向应用程序提供应用编程接口 API,否则我们将我无法使用操 作系统。
应用编程与裸机编程、驱动编程有什么区别?
在学习应用编程之前,相信大家都有过软件开发经验,譬如 51、STM32 等单片机软件开发、以及嵌入 式 Linux 硬件平台下的驱动开发等,51、STM32 这类单片机的软件开发通常是裸机程序开发,并不会涉及 到操作系统的概念,那应用编程与裸机编程以及驱动开发有什么区别呢?
就拿嵌入式 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(); //点亮 LEDdelay(); //延时led_off(); //熄灭 LEDdelay(); //延时}
} 可以看到在裸机程序当中,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();elseled_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 时熄灭 LED,write 写入非 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 点亮 LEDsleep(1); //延时 1 秒data = 0;write(fd, &data, sizeof(data)); //写 0 熄灭 LEDsleep(1); //延时 1 秒}close(fd);return 0;
} 此应用程序也非常简单,仅只需实现用户逻辑代码即可,循环点亮、熄灭 LED,并不需要实现硬件操 作相关,示例代码中调用了 open、write、close 这三个系统调用 API 接口,open 和 close 分别用于打开/关闭 LED 设备,write 写入数据传给 LED 驱动,传入 0 熄灭 LED,传入非 0 点亮 LED。
二、库函数
前面给大家介绍了系统调用,系统调用是内核直接向应用层提供的应用编程接口,譬如 open、write、 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);
sin、cos、tan:三角函数。
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/网址,如下所示:
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 是一个代码编辑器,提供了很多好用的插件,譬如语法检测、高亮显示、智能补全等,大家可以根据自己的选择安装插件。
相关文章:
Linux-C/C++--初探linux应用编程概念
对于大多数首次接触 Linux 应用编程的读者来说,可能对应用编程(也可称为系统编程)这个概念并不 太了解,所以在正式学习 Linux 应用编程之前,笔者有必要向大家介绍这些简单基本的概念,从整体上认识 到应用编…...
用sklearn运行分类模型,选择AUC最高的模型保存模型权重并绘制AUCROC曲线(以逻辑回归、随机森林、梯度提升、MLP为例)
诸神缄默不语-个人CSDN博文目录 文章目录 1. 导入包2. 初始化分类模型3. 训练、测试模型,绘图,保存指标 1. 导入包 from sklearn.linear_model import LogisticRegression from sklearn.ensemble import RandomForestClassifier, GradientBoostingClass…...
动手学大数据-3社区开源实践
目录 数据库概览: MaxComput: HAWQ: Hologres: TiDB: Spark: ClickHouse: Apache Calcite 概览 Calcite RBO HepPlanner 优化规则(Rule) 内置有100优化规则 …...
使用Pydantic驾驭大模型
本文介绍Pydantic 库,首先介绍其概念及优势,然后通过基本示例展示如何进行数据验证。后面通过多个示例解释如何在LangChain中通过Pydantic进行数据验证,保证与大模型进行交互过程中数据准确性,并显示清晰的数验证错误信息。 Pydan…...
【HarmonyOS之旅】基于ArkTS开发(二) -> UI开发之常见布局
目录 1 -> 自适应布局 1.1 -> 线性布局 1.1.1 -> 线性布局的排列 1.1.2 -> 自适应拉伸 1.1.3 -> 自适应缩放 1.1.4 -> 定位能力 1.1.5 -> 自适应延伸 1.2 -> 层叠布局 1.2.1 -> 对齐方式 1.2.2 -> Z序控制 1.3 -> 弹性布局 1.3.1…...
【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界
目录 前言 一、Python—— 网络爬虫的绝佳拍档 二、网络爬虫基础:揭开神秘面纱 (一)工作原理:步步为营的数据狩猎 (二)分类:各显神通的爬虫家族 三、Python 网络爬虫核心库深度剖析 &…...
队列的基本用法
以下是关于 C 语言中队列的详细知识,包括队列的生成、相关函数使用以及其他重要概念: 一、队列的概念 队列是一种线性数据结构,它遵循先进先出(First In First Out,FIFO)的原则,就像日常生活中…...
网络安全VS数据安全
关于网络安全和数据安全,我们常听到如下两种不同声音: 观点一:网络安全是数据安全的基础,把当年做网络安全的那一套用数据安全再做一遍。 观点二:数据安全如今普遍以为是网络安全的延伸,实际情况是忽略数据…...
Linux(NFS服务)
赛题拓扑: 题目: NFS: 共享/webdata/目录。用于存储AppSrv主机的WEB数据。仅允许AppSrv主机访问该共享。 [rootstoragesrv ~]# yum install nfs-utils -y [rootstoragesrv ~]# mkdir /webdata [rootstoragesrv ~]# chmod -R ow /webdata …...
python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)边缘检测
OpenCV中边缘检测四种常用算子: (1)Sobel算子 Sobel算子是一种基于梯度的边缘检测算法。它通过对图像进行卷积操作来计算图像的梯度,并将梯度的大小作为边缘的强度。它使用两个3x3的卷积核,分别用于计…...
SSM课设-学生管理系统
【课设者】SSM课设-学生管理系统 技术栈: 后端: SpringSpringMVCMybatisMySQLJSP 前端: HtmlCssJavaScriptEasyUIAjax 功能: 学生端: 登陆 学生信息管理 个人信息管理 老师端: 多了教师信息管理 管理员端: 多了班级信息管理 多了年级信息管理 多了系统用户管理...
【Pytorch实用教程】TCN(Temporal Convolutional Network,时序卷积网络)简介
文章目录 TCN的基本特点TCN的优点TCN的应用场景典型的TCN架构总结TCN(Temporal Convolutional Network,时序卷积网络)是一种用于处理序列数据的深度学习模型,尤其适用于时间序列预测、语音识别、自然语言处理等任务。它利用卷积神经网络(CNN)来处理时序数据,相比于传统的…...
网络安全 | 什么是正向代理和反向代理?
关注:CodingTechWork 引言 在现代网络架构中,代理服务器扮演着重要的角色。它们在客户端和服务器之间充当中介,帮助管理、保护和优化数据流。根据代理的工作方向和用途,代理服务器可分为正向代理和反向代理。本文将深入探讨这两种…...
3 前端(中):JavaScript
文章目录 前言:JavaScript简介一、ECMAscript(JavaScript基本语法)1 JavaScript与html结合方式(快速入门)2 基本知识(1)JavaScript注释(和Java注释一样)(2&am…...
VIT论文阅读与理解
transform网络结构 vision transform网络结构 图1:模型概述。我们将图像分割成固定大小的补丁,线性嵌入每个补丁,添加位置嵌入,并将结果向量序列馈送到标准Transformer编码器。为了执行分类,我们使用标准方法向序列中添…...
JavaScript笔记APIs篇01——DOM获取与属性操作
黑马程序员视频地址:黑马程序员前端JavaScript入门到精通全套视频教程https://www.bilibili.com/video/BV1Y84y1L7Nn?vd_source0a2d366696f87e241adc64419bf12cab&spm_id_from333.788.videopod.episodes&p78https://www.bilibili.com/video/BV1Y84y1L7Nn?…...
SQL表间关联查询详解
简介 本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(left join)、右连接(right join)、全连接(full join)、内连接(inner join)、交叉连接&…...
select函数
系统调用 select()可用于执行 I/O 多路复用操作,调用 select()会一直阻塞,直到某一个或多个文件描述符成为就绪态(可以读或写)。其函数原型如下所示: #include <sys/select.h> int select(int nfds, fd_set *re…...
建造者模式(或者称为生成器(构建器)模式)
一、什么是建造者模式? 将复杂对象的构建与表示进行分离,使得统一的构建过程,可以创建出不同的对象表现模式 就是将复杂对象里面的成员变量,设置不同的值,使得生成出来的对象拥有不同的属性值; 二、特点…...
【深度学习】Huber Loss详解
文章目录 1. Huber Loss 原理详解2. Pytorch 代码详解3.与 MSELoss、MAELoss 区别及各自优缺点3.1 MSELoss 均方误差损失3.2 MAELoss 平均绝对误差损失3.3 Huber Loss 4. 总结4.1 优化平滑4.2 梯度较好4.3 为什么说 MSE 是平滑的 1. Huber Loss 原理详解 Huber Loss 是一种结合…...
告别复杂配置:用MS-Swift + vLLM 5分钟搞定Qwen2.5-VL的API服务部署与调用
5分钟极速部署Qwen2.5-VL多模态API:MS-Swift与vLLM实战指南 当我们需要将多模态大模型快速集成到智能客服、内容审核或教育工具中时,传统部署流程往往让人望而却步——从环境配置到模型优化,再到API封装,每一步都可能成为项目落地…...
Phi-4-mini-reasoning vLLM安全加固:输入SQL注入防护、XSS过滤、沙箱隔离
Phi-4-mini-reasoning vLLM安全加固:输入SQL注入防护、XSS过滤、沙箱隔离 1. 模型与部署概述 Phi-4-mini-reasoning 是一个基于合成数据构建的轻量级开源模型,专注于高质量、密集推理的数据处理能力。作为Phi-4模型家族的一员,它特别强化了…...
Wan2.2-I2V-A14B镜像免配置方案:单卡24G显存+120GB内存开箱即用部署指南
Wan2.2-I2V-A14B镜像免配置方案:单卡24G显存120GB内存开箱即用部署指南 1. 镜像概述与核心优势 Wan2.2-I2V-A14B是一款专为文生视频任务优化的私有部署镜像,针对RTX 4090D 24GB显存显卡进行了深度优化。这个镜像最大的特点就是"开箱即用"——…...
Qwen3-VL-4B Pro应用场景:电商商品识别、学习资料解读,真实案例分享
Qwen3-VL-4B Pro应用场景:电商商品识别、学习资料解读,真实案例分享 1. 项目简介与核心能力 Qwen3-VL-4B Pro是基于阿里通义千问Qwen3-VL-4B-Instruct模型构建的高性能视觉语言模型服务。相比轻量版2B模型,4B版本在视觉语义理解和逻辑推理能…...
淘宝算法升级背后:主图视觉标准重构与 AI 工具降本增效全复盘
最近与淘宝做搜索流量的高手深度复盘之后,大家普遍发现一个扎心的事实:以前那种“大红大绿、卖点堆满”的暴力主图,在现在的淘宝算法面前几乎失效了 现在的淘宝正处于一个“视觉大调头”的阶段。如果还在用两年前的套路做图,你会…...
别再死记硬背了!用Python代码复现Photoshop 27种混合模式(附完整源码)
用Python代码实现Photoshop混合模式的终极指南 在数字图像处理领域,Photoshop的混合模式就像魔术师的调色板,能够创造出令人惊叹的视觉效果。但你是否想过这些看似神秘的混合效果背后,其实是一系列精确的数学公式在起作用?本文将带…...
终极Tree of Thoughts实战指南:10个复杂问题解决案例详解
终极Tree of Thoughts实战指南:10个复杂问题解决案例详解 【免费下载链接】tree-of-thoughts Plug in and Play Implementation of Tree of Thoughts: Deliberate Problem Solving with Large Language Models that Elevates Model Reasoning by atleast 70% 项目…...
Javadoc自动生成终极指南:告别手动注释的烦恼
Javadoc自动生成终极指南:告别手动注释的烦恼 【免费下载链接】easy_javadoc IntelliJ IDEA 插件,自动生成javadoc文档注释 项目地址: https://gitcode.com/gh_mirrors/ea/easy_javadoc 作为Java开发者,你是否还在为编写规范的Javadoc…...
AutoAgent全新升级:告别流程说明,实现自主决策
在企业数字化与 AI 深度融合的当下,AI 不再是简单的效率工具,而是要成为能自主思考、主动执行、闭环优化的 “数字员工”。 此前,汉得灵猿(大圣)AI中台推出的 AutoAgent 节点V1版本 ,通过基础自主规划能力&…...
Z-Image-GGUF开发利器:IntelliJ IDEA远程调试与项目管理
Z-Image-GGUF开发利器:IntelliJ IDEA远程调试与项目管理 你是不是也遇到过这种情况?本地跑一个图像生成模型,要么显卡带不动,要么环境配置折腾半天。好不容易在云端服务器上部署好了Z-Image-GGUF服务,结果开发调试又成…...
