Linux 系统编程 :select/epoll 多路复用与 SQLite 数据库实战

1. 引言

在 Linux 系统编程的学习中,今天我们进入两个非常实用的主题:I/O 多路复用 (select 与 epoll)和 SQLite 数据库。前者是构建高并发网络服务器的基石,后者则是轻量级嵌入式数据库的典范。本文将系统梳理 select 与 epoll 的核心区别,并完整介绍 SQLite 从安装、SQL 语法到 C 语言 API 调用的全流程。


2. select 和 epoll 的区别

2.1 select 特点

  1. select 监听的文件描述符集合本质是一个数组,有上限限制(通常为 1024)。
  2. 监听集合存放在应用层;有事件发生时,内核要把数据拷贝到用户空间,资源开销大
  3. 只支持水平触发(LT,低速模式)。
  4. 用户程序必须手动遍历查找哪些 fd 产生事件。

2.2 epoll 特点

  1. epoll 事件表没有文件描述符数量上限。
  2. 事件表放在内核中,内核直接检测事件,减少用户态-内核态拷贝,开销小。
  3. 支持水平触发 LT(默认) + 边沿触发 ET(高速模式)
  4. 内核直接返回就绪的 fd,不需要全部遍历。

白话总结:select 适合少量 fd;epoll 适合高并发大量文件描述符,网络服务器用 epoll。


3. 数据库基础

数据库:专门用来对大批量数据做增删改查管理的软件。

3.1 数据库分类

  1. 关系型数据库 :数据存磁盘文件
    • Oracle、MySQL、SQLite
  2. 非关系型数据库 :大多放内存
    • Redis、NoSQL

3.2 安装 SQLite3

bash 复制代码
sudo apt-get install sqlite3

3.3 SQLite3 点命令(终端专用,不是 SQL 语句)

命令 作用
.tables 查看表名
.databases 列出数据库文件
.headers on/off 打开 / 关闭列名显示
.mode 设置输出格式
.schema ?TABLE? 查看建表语句
.width 设置显示宽度

3.4 SQLite 支持的数据类型

  1. INTEGER 整数
  2. REAL 浮点数
  3. TEXT 字符串文本
  4. BLOB 二进制原始字节

4. SQL 基础语句

4.1 create table 创建表

sql 复制代码
create table 表名(列1 类型1, 列2 类型2,...);

示例:

sql 复制代码
create table student (id integer, name text, sex text, age integer, score integer);

4.2 insert into 插入数据

sql 复制代码
insert into 表名 values(值1,值2,...);
-- 指定列插入
insert into 表名(列1,列2) values(值1,值2);
sql 复制代码
insert into student values (1,"张三","男",18,90);
insert into student values (3,"王二",NULL,NULL,NULL);
insert into student (id,name,sex) values (4,"赵五","男");

4.3 select 查询

sql 复制代码
select 列名1,列名2... from 表名;
-- *代表全部列
select * from student;

4.4 where 条件过滤

比较符号= 等于、<> 不等于、> 大于、< 小于、>= 大于等于、<= 小于等于;BETWEEN 在区间内;LIKE 模糊匹配,% 代表任意字符。

示例:

sql 复制代码
select name,score from student where name like "赵%";
select name from student where score >= 90;
select name,score from student where name like "赵%" and score>95;

4.5 update 更新

sql 复制代码
UPDATE 表名 SET 列名=新值 WHERE 列名=某值;
sql 复制代码
update student set score=91 where name="赵五";

4.6 delete 删除行

sql 复制代码
delete from 表名 where 列名=值;
sql 复制代码
delete from student where name = "王二";

4.7 order by 排序

asc 升序,desc 降序。

sql 复制代码
select name,score from student where sex="男" order by score desc;

4.8 drop table 删除整张表

sql 复制代码
drop table student;

4.9 多表联合查询 join

  1. cross join 交叉连接:笛卡尔积,全部两两组合
  2. inner join 内连接:只取出两边匹配成功的数据
  3. outer join 外连接:保留一侧全部数据,匹配不到填 NULL
sql 复制代码
-- 内连接示例
select * from 学生信息 inner join 成绩信息 on 学生信息.ID = 成绩信息.学生ID;

5. C 语言操作 SQLite3 库函数

5.1 sqlite3_open|打开数据库

c 复制代码
int sqlite3_open(
  const char *filename,   /*数据库文件名*/
  sqlite3 **ppDb          /*输出:数据库句柄指针*/
);
  • 参数:
    • filename:数据库文件名,不存在则自动创建
    • ppDb:数据库句柄,输出参数
  • 返回值:成功返回 SQLITE_OK,错误返回错误码

5.2 sqlite3_exec|执行 SQL 语句

c 复制代码
int sqlite3_exec(
  sqlite3* pDb,               /*数据库句柄*/
  const char *sql,            /*SQL字符串*/
  int (*callback)(void*,int,char**,char**),  /*查询回调函数,非查询填NULL*/
  void *arg,                  /*传给回调的第一个参数*/
  char **errmsg               /*错误信息输出*/
);

参数说明

  1. pDb:sqlite3_open 拿到的句柄
  2. sql:要执行的 SQL 字符串
  3. callback:select 查询时必须写回调,每读到一行数据就调用一次;insert/update/delete 填 NULL
  4. arg:用户自定义参数,传递给回调函数第一个形参
  5. errmsg:出错的时候保存错误字符串,用完记得释放

返回值 :成功 SQLITE_OK

回调函数原型

c 复制代码
int callback(void *arg, int ncol, char **value, char **name);
// arg:exec传进来的用户参数
// ncol:这一行有多少列
// value[]:每一列数据字符串
// name[]:每一列的表头名字

5.3 sqlite3_close|关闭数据库

c 复制代码
int sqlite3_close(sqlite3* pDb);
  • 功能:关闭数据库连接,用完必须调用。

6. 综合示例:C 语言操作 SQLite 完整流程

下面是一个完整的示例,演示了打开数据库、建表、插入、查询(带回调)和关闭的完整流程:

c 复制代码
#include <stdio.h>
#include <sqlite3.h>

// 查询回调函数
int callback(void *arg, int ncol, char **value, char **name) {
    for (int i = 0; i < ncol; i++) {
        printf("%s = %s\n", name[i], value[i] ? value[i] : "NULL");
    }
    printf("---\n");
    return 0;
}

int main() {
    sqlite3 *db = NULL;
    char *errmsg = NULL;

    // 1. 打开数据库
    if (sqlite3_open("test.db", &db) != SQLITE_OK) {
        fprintf(stderr, "打开数据库失败: %s\n", sqlite3_errmsg(db));
        return -1;
    }

    // 2. 建表
    const char *sql_create = "create table if not exists student (id integer, name text, score integer);";
    if (sqlite3_exec(db, sql_create, NULL, NULL, &errmsg) != SQLITE_OK) {
        fprintf(stderr, "建表失败: %s\n", errmsg);
        sqlite3_free(errmsg);
        sqlite3_close(db);
        return -1;
    }

    // 3. 插入数据
    const char *sql_insert = "insert into student values (1, '张三', 90);";
    if (sqlite3_exec(db, sql_insert, NULL, NULL, &errmsg) != SQLITE_OK) {
        fprintf(stderr, "插入失败: %s\n", errmsg);
        sqlite3_free(errmsg);
    }

    // 4. 查询数据(带回调)
    const char *sql_query = "select * from student;";
    if (sqlite3_exec(db, sql_query, callback, NULL, &errmsg) != SQLITE_OK) {
        fprintf(stderr, "查询失败: %s\n", errmsg);
        sqlite3_free(errmsg);
    }

    // 5. 关闭数据库
    sqlite3_close(db);
    return 0;
}

编译时需链接 sqlite3 库:

bash 复制代码
gcc demo.c -o demo -lsqlite3

7. 总结

  • select vs epoll:select 实现简单但有 fd 数量上限、需用户态遍历;epoll 由内核维护事件表、支持 ET 模式,是高并发场景的首选。
  • SQLite :轻量级关系型数据库,支持标准 SQL 语法,通过 sqlite3_open / sqlite3_exec / sqlite3_close 三个核心 API 即可在 C 语言中完成大部分数据库操作。
  • 回调机制sqlite3_exec 的查询回调是理解 SQLite C 接口的关键,每行数据触发一次回调,通过 value[]name[] 获取列值与列名。

掌握这两块内容,你就具备了编写高并发网络服务和本地数据持久化程序的基础能力,继续加油!

相关推荐
俊昭喜喜里1 小时前
c#中的构造函数
开发语言·数据库·c#
Mortalbreeze1 小时前
深入理解 Linux IO 模型(二):多路复用——select
linux·运维·服务器·网络·tcp/ip
养生技术人1 小时前
Oracle OCP认证考试题目详解082系列第17题
运维·数据库·sql·oracle·ocp
koi77u1 小时前
嵌入式学习———sqlite数据表
linux·数据库·学习·sqlite
笑梦无境1 小时前
nginx安装(5)
linux·运维·nginx
xx~t1 小时前
嵌入式——Linux软件编程——数据库
linux·c语言·数据库·oracle·sqlite
ACP广源盛139246256731 小时前
M6/M5 Pro Mac mini 端侧 AI 爆发@ACP#YLB3118 存储扩展芯片在本地 AI 服务中的机会与落地场景
大数据·网络·数据库·人工智能·嵌入式硬件·macos
流浪0011 小时前
Linux系统篇26——通信(三)补:共享内存访问真的不进内核?
linux·运维·服务器
不会就选b1 小时前
Linux之socket编程(七)----序列化与反序列化(1)
java·linux·服务器