LLC协议格式

LLC协议格式

IEEE 802.2标准定义了逻辑链路控制(LLC,Logical Link Control)协议,它是数据链路层的一部分,主要用于管理和控制局域网(LAN)中的数据传输。LLC层位于MAC(介质访问控制)子层之上,负责向网络层提供统一的接口,并屏蔽底层物理网络的差异。LLC协议的设计目标是为不同的网络协议(如IP、IPX等)提供通用的数据传输服务。
LLC协议的核心功能包括帧格式定义、流量控制、差错控制以及协议多路复用。LLC帧格式通常由三个主要字段组成:

  1. DSAP(Destination Service Access Point):表示目标服务访问点,用于标识接收方的上层协议。
  2. SSAP(Source Service Access Point):表示源服务访问点,用于标识发送方的上层协议。
  3. 控制字段(Control Field):用于定义帧的类型(如信息帧、监督帧或无编号帧)以及相关的控制信息,例如序列号、确认号等。
    LLC协议支持三种主要的帧类型:
  • 信息帧(I帧):用于传输用户数据,并包含序列号和确认号,支持可靠的面向连接的数据传输。
  • 监督帧(S帧):用于流量控制和差错控制,但不携带用户数据。
  • 无编号帧(U帧):用于建立和释放连接,或执行其他控制功能,例如测试和诊断。
    LLC协议通过这些机制实现了对数据链路层的抽象,使得上层协议可以独立于具体的物理网络技术进行通信。例如,在IEEE 802.3以太网中,LLC层可以与MAC子层结合使用,通过802.2 LLC头部封装IP数据报,从而实现与上层协议的交互。
    此外,LLC协议还支持无连接和面向连接两种操作模式。在无连接模式下,数据帧直接发送而不需要建立连接;而在面向连接模式下,LLC协议会在数据传输前建立连接,并在传输结束后释放连接,以确保可靠的数据传输。

LLC与Ethernet II的区别

LLC协议通常与IEEE 802.3标准一起使用,而Ethernet II则采用了一种更简单的帧格式。Ethernet II帧中没有LLC头部,而是直接使用类型字段(EtherType)来标识上层协议(如IPv4、ARP等)。相比之下,IEEE 802.3帧需要通过LLC头部来标识上层协议,因此在实际应用中,Ethernet II更为常见。
以下是一个典型的LLC帧封装示例(基于IEEE 802.3帧结构):

1
2
3
4
5
+----------------+----------------+----------------+----------------+----------------+
| 前导码(Preamble) | 目的MAC地址 | 源MAC地址 | 长度/类型字段 | 数据字段 |
+----------------+----------------+----------------+----------------+----------------+
| CRC校验字段 | DSAP | SSAP | 控制字段 | 上层协议数据 |
+----------------+----------------+----------------+----------------+----------------+

在实际网络中,LLC协议的使用频率有所下降,尤其是在现代以太网环境中,Ethernet II因其简洁性和高效性而被广泛采用。然而,在某些传统网络协议(如NetBIOS、IPX/SPX)中,LLC仍然发挥着重要作用。

Python二进制文件编码探测工具

Python二进制文件编码探测工具

背景Python二进制文件编码探测工具,基于Python实现,精准识别文件编码,提升数据处理效率。实现基于python语言cchardet库的二进制文件分析程序,按照预设分段参数对文件进行读取和cchardet的文本编码探测。脚本具备跳过文件头n字节,按照m字节分段二进制文件及分段后数据连续4字节探测功能。结果输出会展示每段的序号,偏移起始,片内置信度识别偏移字节,片大小,编码方式,置信度,高置信度提示信息字段;

https://www.calcguide.tech/2025/08/06/python二进制文件编码探测工具/

如何使用脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. 基本用法:分析整个文件
python encoding_detector.py myfile.bin

# 2. 指定块大小
python encoding_detector.py -s 512 myfile.bin

# 3. 跳过每个块的前 10 个字节
python encoding_detector.py -s 100 -h 10 myfile.bin

# 4. 从文件偏移 1116 开始分析
python encoding_detector.py -s 100 -o 1116 ../ftp-pcap/ftp-utf8-long.pcap

# 5. 结合使用:从偏移 1000 开始,每块 256 字节,跳过每块前 20 字节
python encoding_detector.py -s 256 -h 20 -o 1000 myfile.bin

# 6. 通过管道输入
cat myfile.bin | python encoding_detector.py -s 512

Python脚本是实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
#!/usr/bin/env python3
import cchardet
import sys
import os

def print_hex(data, width=16):
"""以十六进制和ASCII形式打印字节数据"""
for i in range(0, len(data), width):
# 十六进制部分
hex_part = ' '.join(f'{byte:02x}' for byte in data[i:i+width])
# ASCII部分 (可打印字符或'.')
ascii_part = ''.join(chr(byte) if 32 <= byte <= 126 else '.' for byte in data&#91;i:i+width])
# 打印地址偏移、十六进制和ASCII
print(f'{i:08x}: {hex_part:<{width*3}} |{ascii_part}|')

def detect_chunks_from_file(filename, chunk_size=1024, from_head_bytes=0, from_file_offset=0):
"""
将文件按指定大小切块,并对每个块进行编码检测。
如果检测置信度为0,则尝试偏移1-4字节重新检测。
from_file_offset: 从文件的哪个字节偏移开始读取。
"""
if not os.path.exists(filename):
print(f"Error: File '{filename}' does not exist.", file=sys.stderr)
return

try:
file_size = os.path.getsize(filename)
print(f"Analyzing file: {filename} (Total size: {file_size} bytes)")
print(f"Chunk size: {chunk_size} bytes")
if from_head_bytes > 0:
print(f"Skipping first {from_head_bytes} bytes of each chunk for detection.")
if from_file_offset > 0:
print(f"Starting analysis from file offset: {from_file_offset}")
print("-" * 50)

with open(filename, 'rb') as f:
# 定位到文件的起始偏移
if from_file_offset > 0:
f.seek(from_file_offset)

chunk_number = 0
while True:
chunk_data = f.read(chunk_size)
if not chunk_
break

# 计算当前块在原始文件中的基础偏移量
offset = from_file_offset + chunk_number * chunk_size

# 裁剪用于检测的数据(跳过头部字节)
detection_data = chunk_data&#91;from_head_bytes:] if len(chunk_data) > from_head_bytes else b''

# --- 初始检测 ---
encoding = None
confidence = 0.0
offset_by_used = 0 # 记录最终使用的偏移量

if len(detection_data) > 0:
try:
result = cchardet.detect(detection_data)
if isinstance(result, dict):
encoding = result.get('encoding')
temp_confidence = result.get('confidence')
if temp_confidence is None:
confidence = 0.0
else:
confidence = temp_confidence

if encoding is not None and not isinstance(encoding, str):
print(f"Warning: Unexpected encoding type in chunk {chunk_number}: {type(encoding)}", file=sys.stderr)
encoding = str(encoding) if encoding is not None else None
else:
print(f"Warning: cchardet returned unexpected type in chunk {chunk_number}: {type(result)}", file=sys.stderr)
except Exception as e:
print(f"Warning: cchardet failed on chunk {chunk_number}: {e}", file=sys.stderr)
encoding = "Error"
confidence = 0.0

# --- 偏移优化逻辑 ---
max_offset_attempts = 4
if confidence == 0.0 and len(detection_data) > max_offset_attempts:
for offset_by in range(1, max_offset_attempts + 1):
if len(detection_data) > offset_by:
adjusted_detection_data = detection_data&#91;offset_by:]
if len(adjusted_detection_data) > 0:
try:
adjusted_result = cchardet.detect(adjusted_detection_data)
if isinstance(adjusted_result, dict):
adjusted_confidence = adjusted_result.get('confidence')
if adjusted_confidence is None:
adjusted_confidence = 0.0

if adjusted_confidence > confidence:
encoding = adjusted_result.get('encoding')
confidence = adjusted_confidence
offset_by_used = offset_by # 记录使用的偏移量

if confidence > 0.0:
break
except Exception:
pass
else:
break

# --- 格式化输出 ---
encoding_display = encoding if encoding is not None else "N/A"
output_line = (f"Chunk {chunk_number:4d} | Offset {offset:8d} | "
f"offset_by {offset_by_used:2d} | "
f"Size {len(chunk_data):4d} | "
f"Encoding: {encoding_display:>12} | "
f"Confidence: {confidence:6.4f}")

# 可以根据置信度调整输出格式,例如高亮高置信度结果
if confidence >= 0.75:
print(output_line) # 或用不同颜色/符号标记,这里简化为普通打印
else:
print(output_line)

# 如果置信度为0,可以选择打印数据内容(当前被注释掉)
# if confidence == 0.0 and len(chunk_data) > 0:
# print ("\n")
# print_hex(chunk_data)
# print ("\n")

chunk_number += 1

# 文件读取结束后的检查
# f.tell() 在 seek 后返回的是绝对位置
absolute_tell = f.tell()
if absolute_tell < file_size:
print(f"Warning: Stopped reading before end of file '{filename}'. "
f"Read up to file offset {absolute_tell} bytes out of {file_size} bytes.", file=sys.stderr)

except IOError as e:
print(f"Error reading file '{filename}': {e}", file=sys.stderr)
except Exception as e:
print(f"An unexpected error occurred while processing '{filename}': {e}", file=sys.stderr)

print("-" * 50 + f" Analysis of '{filename}' finished. " + "-" * 10 + "\n")

def detect_chunks_from_bytes(data, source_name="Byte Input", chunk_size=1024, from_head_bytes=0):
"""
将字节数据按指定大小切块,并对每个块进行编码检测。
如果检测置信度为0,则尝试偏移1-3字节重新检测。
"""
data_len = len(data)
print(f"Analyzing data from: {source_name} (Total size: {data_len} bytes)")
print(f"Chunk size: {chunk_size} bytes")
if from_head_bytes > 0:
print(f"Skipping first {from_head_bytes} bytes of each chunk for detection.")
print("-" * 50)

if data_len == 0:
print("Input data is empty.")
return

chunk_number = 0
for i in range(0, data_len, chunk_size):
chunk_data = data&#91;i:i + chunk_size]
if not chunk_
break

offset = i
detection_data = chunk_data&#91;from_head_bytes:] if len(chunk_data) > from_head_bytes else b''

encoding = None
confidence = 0.0

if len(detection_data) > 0:
try:
result = cchardet.detect(detection_data)
if isinstance(result, dict):
encoding = result.get('encoding')
temp_confidence = result.get('confidence')
if temp_confidence is None:
confidence = 0.0
else:
confidence = temp_confidence

if encoding is not None and not isinstance(encoding, str):
print(f"Warning: Unexpected encoding type in chunk {chunk_number}: {type(encoding)}", file=sys.stderr)
encoding = str(encoding) if encoding is not None else None
else:
print(f"Warning: cchardet returned unexpected type in chunk {chunk_number}: {type(result)}", file=sys.stderr)
except Exception as e:
print(f"Warning: cchardet failed on chunk {chunk_number}: {e}", file=sys.stderr)
encoding = "Error"
confidence = 0.0

# --- 偏移优化逻辑 (针对 bytes 输入)---
max_offset_attempts = 3
offset_by_used = 0
if confidence == 0.0 and len(detection_data) > max_offset_attempts:
for offset_by in range(1, max_offset_attempts + 1):
if len(detection_data) > offset_by:
adjusted_detection_data = detection_data&#91;offset_by:]
if len(adjusted_detection_data) > 0:
try:
adjusted_result = cchardet.detect(adjusted_detection_data)
if isinstance(adjusted_result, dict):
adjusted_confidence = adjusted_result.get('confidence')
if adjusted_confidence is None:
adjusted_confidence = 0.0

if adjusted_confidence > confidence:
encoding = adjusted_result.get('encoding')
confidence = adjusted_confidence
offset_by_used = offset_by

if confidence > 0.0:
break
except Exception:
pass
else:
break

# 格式化输出 (bytes 输入也显示 offset_by)
encoding_display = encoding if encoding is not None else "N/A"
print(f"Chunk {chunk_number:4d} | Offset {offset:8d} | "
f"offset_by {offset_by_used:2d} | " # 添加 offset_by 显示
f"Size {len(chunk_data):4d} | "
f"Encoding: {encoding_display:>12} | "
f"Confidence: {confidence:6.4f}")

# 如果置信度为0,打印数据内容
# if confidence == 0.0 and len(chunk_data) > 0:
# print ("\n")
# print_hex(chunk_data)
# print ("\n")

chunk_number += 1

print("-" * 50 + f" Analysis of '{source_name}' finished. " + "-" * 10 + "\n")

def main():
"""
主函数,处理命令行参数并调用相应的检测函数。
"""
if len(sys.argv) < 2:
print("No filename provided. Reading binary data from STDIN...", file=sys.stderr)
try:
data = sys.stdin.buffer.read()
detect_chunks_from_bytes(data, source_name="STDIN", chunk_size=1024)
except KeyboardInterrupt:
print("\nInterrupted by user.", file=sys.stderr)
except Exception as e:
print(f"Error reading from STDIN: {e}", file=sys.stderr)
sys.exit(0)

# 默认参数
chunk_size = 1024
from_head_bytes = 0
from_file_offset = 0 # 新增默认参数
filenames = &#91;]

# 解析命令行参数
i = 1
while i < len(sys.argv):
if sys.argv&#91;i] == '-s':
if i + 1 < len(sys.argv):
try:
chunk_size = int(sys.argv&#91;i + 1])
if chunk_size <= 0:
raise ValueError("Chunk size must be positive.")
i += 2
except ValueError as e:
print(f"Error: Invalid chunk size '-s {sys.argv&#91;i + 1]}': {e}", file=sys.stderr)
sys.exit(1)
else:
print("Error: Option '-s' requires an argument.", file=sys.stderr)
sys.exit(1)
elif sys.argv&#91;i] == '-h':
if i + 1 < len(sys.argv):
try:
from_head_bytes = int(sys.argv&#91;i + 1])
if from_head_bytes < 0:
raise ValueError("Head bytes to skip must be non-negative.")
i += 2
except ValueError as e:
print(f"Error: Invalid head bytes '-h {sys.argv&#91;i + 1]}': {e}", file=sys.stderr)
sys.exit(1)
else:
print("Error: Option '-h' requires an argument.", file=sys.stderr)
sys.exit(1)
# --- 新增:解析 -o 参数 ---
elif sys.argv&#91;i] == '-o':
if i + 1 < len(sys.argv):
try:
from_file_offset = int(sys.argv&#91;i + 1])
if from_file_offset < 0:
raise ValueError("File offset must be non-negative.")
i += 2
except ValueError as e:
print(f"Error: Invalid file offset '-o {sys.argv&#91;i + 1]}': {e}", file=sys.stderr)
sys.exit(1)
else:
print("Error: Option '-o' requires an argument.", file=sys.stderr)
sys.exit(1)
# --- 新增结束 ---
else:
filenames.append(sys.argv&#91;i])
i += 1

if not filenames:
print("Error: No filename provided.", file=sys.stderr)
sys.exit(1)

# 对每个提供的文件进行处理
for filename in filenames:
# --- 修改:传递 from_file_offset 参数 ---
detect_chunks_from_file(filename, chunk_size, from_head_bytes, from_file_offset)

if __name__ == "__main__":
main()

Uchardet Code Analysis

Uchardet Code Analysis: nsUTF8Prober Confidence Calculation

Uchardet库中的utf-8的置信度计算方法

1. Core Logic

The detector’s core principle is: ​​Verify UTF-8 encoding rules to determine if text is UTF-8​​. It uses a state machine (mCodingSM) to track byte sequence compliance with UTF-8 specifications.

  • ​​Reset()​​: Initializes detector state, resets state machine, multi-byte character counter (mNumOfMBChar), and detection state (mState).

​​HandleData()​​: Primary function for processing input byte streams:

  • Processes bytes sequentially through the state machine (mCodingSM->NextState(aBuf[i]))

  • eItsMe state return indicates definite UTF-8 rule violation → detector state becomes eFoundIt (effectively “confirmed not UTF-8”)

eStart state return indicates successful recognition of a complete UTF-8 character:

  • For multi-byte characters (mCodingSM->GetCurrentCharLen() >= 2), increments mNumOfMBChar

  • Includes logic to build Unicode code points (currentCodePoint) stored in codePointBuffer

​​Key optimization​​: At HandleData’s end: if (mState == eDetecting) if (mNumOfMBChar > ENOUGH_CHAR_THRESHOLD && GetConfidence(0) > SHORTCUT_THRESHOLD) mState = eFoundIt; This allows early termination when sufficient valid multi-byte characters are found (mNumOfMBChar > 256) with high confidence.

2. Confidence Calculation (GetConfidence)

Core calculation logic:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define ONE_CHAR_PROB   (float)0.50

float nsUTF8Prober::GetConfidence(int candidate)
{
if (mNumOfMBChar < 6) // Fewer than 6 multi-byte characters
{
float unlike = 0.5f; // Initial 50% probability of not being UTF-8

// Each valid multi-byte character has 50% probability of being coincidental
// Combined probability for N characters: (0.5)^N
for (PRUint32 i = 0; i < mNumOfMBChar; i++)
unlike *= ONE_CHAR_PROB; // Multiply by 0.5 per character

// Confidence = 1 - probability of coincidence
return (float)1.0 - unlike;
}
else // 6+ multi-byte characters
{
return (float)0.99; // High-confidence threshold
}
}

3. Confidence Calculation Methodology

The algorithm uses a ​​statistical significance heuristic​​:

​​Low-Confidence Mode (<6 MB characters)​​:

  • Models probability that N valid UTF-8 sequences appear coincidentally in non-UTF8 text as (0.5)^N

  • ONE_CHAR_PROB=0.5 is an empirical estimate of random byte sequences accidentally matching UTF-8 rules

  • Confidence = 1 - (0.5)^N

​​Examples​​:

  • 0 MB chars: 50% confidence

  • 1 MB char: 75% confidence

  • 3 MB chars: 93.75% confidence

  • 5 MB chars: 98.4375% confidence

​​High-Confidence Mode (≥6 MB characters)​​:

  • Returns fixed 99% confidence

  • Optimization based on empirical observation that 6 valid sequences provide near-certain detection

  • Minimizes false positives while maintaining efficiency

4. Key Characteristics

AspectDescription​​Detection Basis​​Multi-byte character count (mNumOfMBChar)​​Calculation Approach​​Statistical model of coincidental matches​​Probability Constant​​Empirical value (0.5)​​Threshold​​6 multi-byte characters​​Strengths​​Simple computation, fast rejection of invalid sequences​​Detection Philosophy​​Focuses on disproving non-UTF8 through rule validation

5. Practical Implications

  • ​​Short text sensitivity​​: Confidence builds slowly with character count

  • ​​Language dependence​​: More effective for languages requiring frequent multi-byte characters

  • ​​Error resilience​​: Single invalid sequence resets confidence building

  • ​​Performance tradeoff​​: Threshold value balances accuracy vs processing time

This confidence model exemplifies Uchardet’s practical approach - using statistically-informed heuristics to achieve efficient encoding detection without complex probabilistic modeling. The 0.5 probability constant and 6-character threshold represent carefully balanced empirical values refined through real-world testing.

​​What Programmers Should Consider When Choosing a Side Hustle​​

​​What Programmers Should Consider When Choosing a Side Hustle

For programmers choosing a side hustle, projects with low marginal costs (meaning costs barely increase as your user base grows) often lead to genuine “passive revenue streams.” Here are some high-value directions and concrete examples:

​​Develop Lightweight SaaS Tools​​

  • ​​Core Appeal:​​ Build once, collect recurring subscriptions; server costs scale automatically with users.

​​Examples:​​

  • SEO Checker Tool: Automatically generates optimization reports from a user’s URL (Tech Stack: Python + AWS Lambda).

  • Social Media Scheduler: Batch-create and post to multiple platforms (using APIs like Twitter/Facebook).

  • Freelancer Tax Calculator: Tool for estimating taxes for independent contractors.

​​Cost Control:​​ Use Serverless Architecture (like Vercel, AWS Lambda) to minimize server expenses.

​​Online Education & Knowledge Products​​

  • ​​Core Appeal:​​ Create content once, sell it endlessly; platforms handle distribution.

​​Examples:​​

  • Recorded Courses: Publish courses like “Advanced React in Practice” on Udemy or MOOC platforms, or sell via your own website.

  • E-books/Guides: Write “TypeScript Best Practices” and sell the PDF via Gumroad.

  • Paid Newsletters: Publish technical newsletters like “Architecture Design Weekly” via Substack.

​​Tool Recommendations:​​ OBS for recording, Canva for slide design, Thinkific for building your membership site.

​​Monetizing Open Source Projects​​

  • ​​Core Appeal:​​ Free version drives adoption, paid “Pro/Enterprise” version generates revenue; low maintenance costs.

​​Examples:​​

  • Open Core Model: Offer a free library/core with premium features in a paid tier (e.g., AG Grid).

  • GitHub Sponsors: Get donations/sponsorships based on technical influence (e.g., core Vue.js contributors).

  • Plugin Marketplace: Develop premium plugins for popular open-source platforms like WordPress.

​​Key Point:​​ Target high-frequency use cases (e.g., data grids, chart libraries).

​​API Services​​

  • ​​Core Appeal:​​ Package technical capabilities into an API; charge per call/usage.

​​Examples:​​

  • OCR API: Wrap the Tesseract engine into a REST API (Pricing: $0.001 USD per call).

  • AI Model Services: Offer APIs for text sentiment analysis or image style transfer.

  • Data Aggregation API: Scrape public data (e.g., stock prices) and provide a structured API.

​​Platform Recommendation:​​ Deploy quickly to marketplaces like APILayer or RapidAPI.

​​Digital Products & Templates​​

  • ​​Core Appeal:​​ Zero cost of delivery; automate sales.

​​Examples:​​

  • Code Templates/Snippets: Sell Next.js e-commerce boilerplates or Flutter app starters.

  • Design Resources: Create Figma UI kits (e.g., “Dashboard UI Kit”).

  • Chrome Extensions: Solve niche problems (e.g., a “GitHub Lines-of-Code Counter”).

​​Sales Channels:​​ Gumroad, Creative Market, Envato Market.

​​Content Creation & Ad Revenue/Affiliate Marketing​​

  • ​​Core Appeal:​​ After building traffic, ads/affiliate income has near-zero marginal cost.

​​Examples:​​

  • Tech Blogging: Earn ad revenue via Google AdSense or Carbon Ads.

  • YouTube/Bilibili Tutorials: Platform revenue share + sponsored content (e.g., promoting coding tools).

  • Tech Book Affiliates: Earn commissions by recommending tech books on Amazon.

​​Tools:​​ Build a static blog with Hugo/Jekyll for automatic SEO optimization.

​​Automation Tools & Bots​​

  • ​​Core Appeal:​​ Automate manual tasks with code; scale the solution for profit.

​​Examples:​​

  • Discord Management Bot: Auto-moderation, welcome messages, stats (custom or public bots).

  • Twitter Growth Tools: Automate follows/unfollows, content scheduling (Must follow platform rules!).

  • RPA Scripts: Provide businesses with scripts automating Excel data cleaning.

​​Tech Stack:​​ Python + Selenium/AutoHotkey.

​​Paid Membership Communities​​

  • ​​Core Appeal:​​ Knowledge base + user engagement; low marginal service cost per member.

​​Examples:​​

  • Paid Tech Community: Offer Q&A and resource sharing via Discord or Zhishixingqiu (Knowledge Planet).

  • LeetCode Study Group: Provide curated weekly problems + solutions.

  • Open Source Collaboration Community: Paid access to participate in internal project development.

​​Tools:​​ Use Discord bots for automatic membership tier management.

​​Template & Theme Sales​​

  • ​​Core Appeal:​​ Develop once, sell across multiple marketplaces.

​​Examples:​​

  • Notion Templates: Sell templates for project management, book notes, etc.

  • WordPress Themes: Develop lightweight blog themes and sell on ThemeForest.

  • Resume Builders: Provide LaTeX/HTML resume templates with one-click PDF export.

​​Affiliate Marketing​​

  • ​​Core Appeal:​​ Zero inventory; earn commissions by promoting tech products.

​​Examples:​​

  • Cloud Services: Get referral bonuses for signing users up to AWS/Azure.

  • Developer Tools: Promote tools like GitHub Copilot or JetBrains IDEs.

  • Online Courses: Earn affiliate commissions for Udemy courses (if the instructor enables it).

​​Key Advice for Choosing​​

  • ​​Leverage Existing Skills:​​ Prioritize reusing your current tech stack (e.g., Frontend Dev -> Chrome Extensions).

  • ​​Validate Demand:​​ Test ideas in communities like Reddit, Indie Hackers, or relevant niche forums.

  • ​​Automate Processes:​​ Use GitHub Actions for deployment, Zapier for connecting payments/email notifications.

  • ​​Compliance:​​ Pay attention to data privacy laws (e.g., GDPR), tax obligations (register as self-employed to simplify).

​​Core Formula:​​Passive Revenue Stream = Low Marginal Cost + Scalability + Automated Delivery

​​Getting Started:​​Pick 1-2 directions from the list above to launch quickly (e.g., start by building a Chrome extension or publishing an e-book). Iterate and optimize gradually. This approach helps effectively balance your main job and your side hustle.

programmers side hustle considerations, what programmers should look for in a side hustle, best side hustles for software developers, how to choose a side hustle as a programmer, side hustle tips for developers, programmer side gig ideas, factors to consider when starting a side hustle, top side hustle options for coders, how to pick a side business for programmers, programmer side hustle checklist

YARA: A Tool for Identifying and Classifying Malware Samples​

​​YARA: A Tool for Identifying and Classifying Malware Samples​​Posted on August 2, 2025 by Maiyaba Dad

YARA is a pattern-matching tool widely used to identify and classify malware samples. It’s extensively applied in malware analysis, threat intelligence, and intrusion detection by creating custom rules that match specific strings, hex patterns, regular expressions, or other file characteristics.

friend link:(2 封私信) YARA-规则匹配神器-简单使用篇 - 知乎

github:https://github.com/VirusTotal/yara

I. Basic YARA Usage

​​Installing YARA​​Linux (Ubuntu/Debian): sudo apt-get install yara macOS: brew install yara Python Integration (Recommended): pip install yara-python Note: yara-python provides Python bindings for integrating YARA into scripts.

​​Writing YARA Rules (.yar files)​​Example rule (example.yar): rule HelloWorld { meta: author = “YourName” description = “Detects the string ‘Hello, World!’” strings: $hello = “Hello, World!” ascii condition: $hello }

​​Command-Line Execution​​ yara example.yar target_file.txt Output if matched: HelloWorld target_file.txt

II. Python Integration Examples

​​Scan a file using yara-python:​​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import yara

# Compile rule
rules = yara.compile(filepath='example.yar')

# Scan target
matches = rules.match('target_file.txt')

# Output results
if matches:
print("Matched rules:")
for match in matches:
print(match)
else:
print("No matches found")

​​Load rules from a string:​​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import yara

# Define rule directly
rule_source = '''
rule HelloWorld {
strings:
$hello = "Hello, World!" ascii
condition:
$hello
}
'''

# Compile and scan
rules = yara.compile(source=rule_source)
matches = rules.match('target_file.txt')
print(matches)

​​Scan all files in a directory:​​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import yara
import os

def scan_directory(directory, rules):
for root, _, files in os.walk(directory):
for file in files:
filepath = os.path.join(root, file)
try:
matches = rules.match(filepath)
if matches:
print(f"&#91;+] Match: {filepath} -> {matches}")
except Exception as e:
print(f"&#91;-] Error scanning {filepath}: {e}")

# Execute scan
rules = yara.compile(filepath='example.yar')
scan_directory('/path/to/scan', rules)

III. Advanced YARA Rules

​​Detect suspicious imports in PE files:​​

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import "pe"

rule SuspiciousPE
{
meta:
description = "Detects PE files with suspicious API calls"

condition:
pe.is_pe and
any of ($suspicious_funcs) in (pe.imported_functions)

strings:
$suspicious_funcs = "VirtualAllocEx"
$suspicious_funcs = "WriteProcessMemory"
$suspicious_funcs = "CreateRemoteThread"
}

Note: Requires valid PE files for pe module.

IV. SIEM/SOC Integration Strategies

  • ​​Scheduled Filesystem Scans:​​ Run Python scripts periodically to scan upload/temp directories.

  • ​​File Upload Integration:​​ Auto-trigger YARA scans in web apps after file uploads.

  • ​​ELK/Splunk Integration:​​ Send scan results to SIEM for alerting.

  • ​​Sandbox Coordination:​​ Extract IOC characteristics after dynamic analysis.

V. Practical Tips

​​Functionality​​​​Command/Implementation​​View compiled rulesyara -r example.yar /path/to/filesCase-insensitive matching$a = “virus” nocaseRegular expressions$re = /https?://[a-zA-Z0-9./]*/File header detection$mz = { 4D 5A } condition: $mz at 0

VI. Troubleshooting

  • ​​Compilation Errors:​​ Verify syntax (YARA is sensitive to indentation/punctuation).

  • ​​Performance Issues:​​ Avoid overly broad rules; optimize with ascii/wide/nocase.

  • ​​Permissions:​​ System file scanning may require elevated privileges.

  • Official Documentation

Rule Repositories:

  • Yara-Rules/rules

  • Neo23x0/signature-base

Online Rule Tester

Key Applications

YARA excels in:🛡️ Malware detection & classification🔍 Threat hunting🤖 Automated analysis pipelines🔌 Security product integration (EDR/AV/sandboxes)

The yara-python library enables seamless integration into security platforms. For advanced implementations (multi-threaded scanning, hot-reloading, REST APIs), consider building a microservice using ​​Flask​​ or ​​FastAPI​​.

​​Note:​​ All CLI commands and code blocks retain original functionality while using American English terminology (e.g., “malware samples” instead of “malicious specimens”, “elevated privileges” instead of “administrator rights”). Platform names (Udemy, Splunk) and technical terms (PE files, SIEM) remain unchanged per localization best practices.

文本编码设计思路详解

文本编码核心设计思路

相关文章:多编码的测试文本文件 Python二进制文件编码探测工具

文本编码的设计是一个复杂且历史悠久的过程,旨在用有限的数字(通常是 8 位字节)来表示世界上各种语言的字符。文本编码设计思路解析,涵盖多编码测试文本文件与核心原理,助你掌握高效文本处理方法。

文本编码的设计主要围绕以下几个目标:

表示能力:能够表示目标语言或字符集中的所有字符。

兼容性:尽可能与已有的标准(尤其是 ASCII)兼容。

效率:存储和传输的效率,以及处理速度。

标准化:需要被广泛接受和实现的标准。

主要编码类型及其字节设计

1. 单字节编码 (Single-Byte Character Sets - SBCS)

  • 设计:每个字符用一个字节(8 位)表示。

  • 容量:一个字节有 256 (2^8) 个可能的值。通常 0x00-0x7F 被保留给 ASCII 字符(0-127),剩下 128 个值(0x80-0xFF)用于表示其他字符。

示例:

  • ASCII:最基础的编码,只使用 0x00-0x7F 表示英文字符、数字、标点和控制字符。

  • ISO/IEC 8859 系列(Latin-1, Latin-2, …):扩展 ASCII,用 0x80-0xFF 表示西欧、中欧等地区的字符。

  • Windows-1252:Windows 对 Latin-1 的扩展,填充了 0x80-0xFF 中一些在 Latin-1 中未定义或定义为控制字符的位置。

优点:

  • 简单高效:字符与字节一一对应,处理速度快,存储空间固定。

  • 向后兼容 ASCII:所有 ASCII 文本也是有效的 Latin-1/Windows-1252 文本。

缺点:

  • 表示能力有限:只能表示最多 256 个字符,远远不足以表示像中文、日文、阿拉伯文等拥有成千上万个字符的语言。

2. 多字节编码 (Multi-Byte Character Sets - MBCS)

为了解决单字节编码表示能力不足的问题,多字节编码应运而生。其核心思想是使用变长的字节序列来表示不同的字符。文本编码设计思路解析,涵盖多编码测试文本文件与核心原理,助你掌握高效文本处理方法。

A. 双字节编码 (Double-Byte Character Sets - DBCS)

  • 设计:主要使用两个字节来表示一个字符,有时也用一个字节表示 ASCII 字符。

示例:

  • Shift JIS (SJIS):用于日文。第一个字节(前导字节)在特定范围(如 0x81-0x9F, 0xE0-0xFC),第二个字节(尾字节)在 0x40-0x7E 或 0x80-0xFC。

  • GBK / GB2312:用于简体中文。第一个字节在 0x81-0xFE,第二个字节在 0x40-0x7E 或 0x80-0xFE。

  • Big5:用于繁体中文。第一个字节在 0x81-0xFE,第二个字节在 0x40-0x7E 或 0xA1-0xFE。

优点:

  • 表示能力大增:可以表示几万甚至更多的字符。

  • 向后兼容 ASCII:单个 ASCII 字节(0x00-0x7F)仍然表示 ASCII 字符。

缺点:

  • 状态依赖:解析时需要记住前一个字节是 ASCII 还是多字节序列的开始,这使得解析变得复杂且容易出错。

  • 同步问题:如果在传输过程中丢失或插入一个字节,会导致后续所有字符解析错误,直到遇到下一个 ASCII 字符才能重新同步。

B. 真正的变长多字节编码

  • 设计:一个字符可以用 1 个、2 个、3 个甚至更多字节来表示。关键在于编码规则能自同步 (Self-Synchronizing),即解析器可以从任何一个字节开始,根据该字节的值判断它是一个新字符的开始,还是前一个字符的后续部分。

示例:

UTF-8(最典型和成功):

  • 1 字节字符:0xxxxxxx (0x00-0x7F) - 完全兼容 ASCII。

  • 2 字节字符:110xxxxx 10xxxxxx - 用于表示拉丁文补充、希腊文、西里尔文等。

  • 3 字节字符:1110xxxx 10xxxxxx 10xxxxxx - 用于表示大部分中文、日文、韩文常用字符。

  • 4 字节字符:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - 用于表示 Unicode 较后面平面上的字符(如一些生僻汉字、表情符号等)。

优点:

  • 自同步:通过检查字节的前几位(bit pattern),解析器总能知道当前字节是新字符的开始还是延续。

  • 完美兼容 ASCII:所有 ASCII 文本都是有效的 UTF-8 文本。

  • 表示能力极强:可以表示 Unicode 标准中的所有字符(超过 100 万个码位)。

  • 效率高:对于以 ASCII 为主的文本(如英文、编程代码),存储效率与 ASCII 相同。

缺点:

  • 对于非 ASCII 字符,存储效率可能低于固定宽度编码:例如,一个中文字符在 UTF-8 中需要 3 个字节,而在 UTF-16 中只需要 2 个字节(基本平面内)或 4 个字节(辅助平面)。

3. 固定宽度多字节编码

  • 设计:每个字符都使用固定数量的字节表示,例如每个字符都用 2 个字节或 4 个字节。

示例:

UTF-16:

  • 基本平面字符 (BMP):使用 2 个字节(16 位)表示,覆盖了大部分常用字符。

  • 辅助平面字符:使用 4 个字节(通过代理对 Surrogate Pair 实现)。

  • 特点:对于 BMP 内的字符(包括大部分中日韩字符),它是固定宽度的。但它不是完全固定宽度的,因为需要代理对来表示辅助平面字符。

UTF-32:

  • 所有字符:都使用 4 个字节(32 位)表示。

  • 优点:真正的固定宽度,一个字符就是一个整数,处理极其简单。

  • 缺点:存储效率低,即使是 ASCII 字符也要占用 4 个字节。对于以 ASCII 为主的文本,存储空间是 UTF-8 的 4 倍。

总结

编码类型字节数设计特点优点缺点单字节 (ASCII)1固定简单高效,兼容性好表示能力极低单字节扩展 (Latin-1)1固定简单高效,兼容 ASCII表示能力低,不同语言不兼容双字节 (SJIS, GBK)1 或 2变长 (但主要是2)表示能力大,兼容 ASCII解析复杂,易失同步变长多字节 (UTF-8)1, 2, 3, 4变长,自同步自同步,兼容 ASCII,表示能力极强,英文效率高非ASCII字符存储效率可能低固定多字节 (UTF-16)2 或 4变长 (主要是2)BMP内字符效率高不是完全固定,ASCII效率低固定多字节 (UTF-32)4固定处理最简单存储效率低

现代文本处理(尤其是国际化软件和 Web)普遍采用 UTF-8,因为它在兼容性、表示能力和效率之间取得了最佳平衡。而 UTF-16 在一些系统(如 Windows 内部、Java、.NET)中也很常见。UTF-32 由于其存储效率问题,使用较少。传统的 SBCS 和 DBCS 仍然在一些遗留系统中使用。

https://www.calcguide.tech/2025/08/06/文本编码的设计思路/

https://www.calcguide.tech/2025/08/26/linux开源软件路线图/

adjtimex 函数系统调用及示例

adjtimex 函数详解

  1. 函数介绍

adjtimex 是Linux系统调用,用于查询和调整系统时钟的状态。它是NTP(Network Time Protocol)守护进程和其他时间同步工具的核心接口,提供了精确的时钟调整和状态查询功能。通过 adjtimex,可以实现高精度的时间同步和时钟管理。

  1. 函数原型
1
2
3
#include <sys/timex.h>
int adjtimex(struct timex *buf);

  1. 功能

adjtimex 允许用户查询系统时钟的状态信息,包括时钟偏移、频率调整、最大误差等,同时支持调整时钟参数以实现精确的时间同步。它是Linux时间子系统的重要组成部分,为高精度时间管理提供了底层支持。

  1. 参数
  • *struct timex buf: 指向timex结构体的指针,用于传递时钟参数和接收状态信息
  1. 返回值
  • 成功: 返回时钟状态(TIME_OK, TIME_INS, TIME_DEL, TIME_OOP, TIME_WAIT, TIME_ERROR)

  • 失败: 返回-1,并设置errno

  1. 相似函数,或关联函数
  • settimeofday: 设置系统时间

  • gettimeofday: 获取系统时间

  • clock_adjtime: 更现代的时钟调整接口

  • ntp_adjtime: NTP时间调整函数

  • clock_gettime/clock_settime: 高精度时钟操作

  1. 示例代码

示例1:基础adjtimex使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>

/**
* 显示时钟状态信息
*/
void show_clock_status(const struct timex *tx) {
printf("=== 时钟状态信息 ===\n");

// 时钟偏移
printf("时钟偏移: %ld.%06ld 秒\n",
tx->offset / 1000000, labs(tx->offset) % 1000000);

// 时钟频率
printf("时钟频率: %ld ppm\n", tx->freq / 65536); // 转换为ppm

// 最大误差
printf("最大误差: %ld 毫秒\n", tx->maxerror);

// 估算误差
printf("估算误差: %ld 毫秒\n", tx->esterror);

// 状态标志
printf("状态标志: 0x%04x\n", tx->status);
printf(" ");
if (tx->status & STA_PLL) printf("STA_PLL ");
if (tx->status & STA_PPSFREQ) printf("STA_PPSFREQ ");
if (tx->status & STA_PPSTIME) printf("STA_PPSTIME ");
if (tx->status & STA_FLL) printf("STA_FLL ");
if (tx->status & STA_INS) printf("STA_INS ");
if (tx->status & STA_DEL) printf("STA_DEL ");
if (tx->status & STA_UNSYNC) printf("STA_UNSYNC ");
if (tx->status & STA_FREQHOLD) printf("STA_FREQHOLD ");
if (tx->status & STA_PPSSIGNAL) printf("STA_PPSSIGNAL ");
if (tx->status & STA_PPSJITTER) printf("STA_PPSJITTER ");
if (tx->status & STA_PPSWANDER) printf("STA_PPSWANDER ");
if (tx->status & STA_PPSERROR) printf("STA_PPSERROR ");
if (tx->status & STA_CLOCKERR) printf("STA_CLOCKERR ");
printf("\n");

// 时钟精度
printf("时钟精度: %ld 毫秒\n", tx->precision);

// PLL时间常数
printf("PLL时间常数: %ld\n", tx->constant);

// PPM容忍度
printf("PPM容忍度: %ld\n", tx->tolerance);

// 时钟状态
printf("时钟状态: ");
switch (tx->state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_INS: printf("TIME_INS (即将插入闰秒)\n"); break;
case TIME_DEL: printf("TIME_DEL (即将删除闰秒)\n"); break;
case TIME_OOP: printf("TIME_OOP (闰秒处理中)\n"); break;
case TIME_WAIT: printf("TIME_WAIT (等待同步)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (时钟错误)\n"); break;
default: printf("未知状态 (%d)\n", tx->state); break;
}

printf("\n");
}

/**
* 演示基础adjtimex使用方法
*/
int demo_adjtimex_basic() {
struct timex tx;
int result;

printf("=== 基础adjtimex使用示例 ===\n");

// 初始化timex结构体
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式,不修改任何参数

// 调用adjtimex查询时钟状态
printf("1. 查询时钟状态:\n");
result = adjtimex(&tx);

if (result == -1) {
printf(" 查询时钟状态失败: %s\n", strerror(errno));
if (errno == EPERM) {
printf(" 原因:需要CAP_SYS_TIME权限\n");
} else if (errno == EINVAL) {
printf(" 原因:参数无效\n");
}
return -1;
}

printf(" 查询成功\n");
show_clock_status(&tx);

// 显示时钟信息
printf("2. 时钟详细信息:\n");
printf(" 系统时钟: %ld.%06ld 秒\n", tx.time.tv_sec, tx.time.tv_usec);

// 显示频率调整信息
printf("3. 频率调整信息:\n");
printf(" 频率偏移: %ld (系统单位)\n", tx.freq);
printf(" 频率偏移: %.3f ppm\n", (double)tx.freq / 65536.0);

// 显示PLL参数
printf("4. PLL参数:\n");
printf(" PLL偏移: %ld\n", tx.offset);
printf(" PLL最大误差: %ld 毫秒\n", tx.maxerror);
printf(" PLL估算误差: %ld 毫秒\n", tx.esterror);
printf(" PLL时间常数: %ld\n", tx.constant);

// 演示权限检查
printf("\n5. 权限检查:\n");
uid_t uid = getuid();
printf(" 当前用户ID: %d\n", uid);
if (uid == 0) {
printf(" ✓ 具有root权限,可以调整时钟参数\n");
} else {
printf(" ✗ 没有root权限,只能查询时钟状态\n");
printf(" 提示:调整时钟参数需要root权限或CAP_SYS_TIME能力\n");
}

// 演示时钟状态解释
printf("\n=== 时钟状态解释 ===\n");
printf("TIME_OK: 时钟同步正常\n");
printf("TIME_INS: 即将插入闰秒\n");
printf("TIME_DEL: 即将删除闰秒\n");
printf("TIME_OOP: 闰秒处理中\n");
printf("TIME_WAIT: 等待同步\n");
printf("TIME_ERROR: 时钟错误\n");

// 演示状态标志解释
printf("\n=== 状态标志解释 ===\n");
printf("STA_PLL: PLL模式启用\n");
printf("STA_PPSFREQ: PPS频率调整\n");
printf("STA_PPSTIME: PPS时间调整\n");
printf("STA_FLL: 频率锁定环模式\n");
printf("STA_INS: 即将插入闰秒\n");
printf("STA_DEL: 即将删除闰秒\n");
printf("STA_UNSYNC: 时钟未同步\n");
printf("STA_FREQHOLD: 频率保持\n");

return 0;
}

int main() {
return demo_adjtimex_basic();
}

示例2:时钟调整和同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 模拟NTP时间同步
*/
int simulate_ntp_synchronization() {
struct timex tx;
int result;
double network_delay = 0.05; // 50ms网络延迟
double frequency_drift = 50.0; // 50ppm频率漂移

printf("=== NTP时间同步模拟 ===\n");

// 获取当前时钟状态
printf("1. 获取当前时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

printf(" 当前时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 当前最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 当前估算误差: %ld 毫秒\n", tx.esterror);

// 模拟网络时间查询
printf("\n2. 模拟网络时间查询:\n");
struct timeval network_time;
gettimeofday(&network_time, NULL);

// 模拟时钟偏移(100ms)
long offset_us = 100000; // 100ms偏移
printf(" 检测到时钟偏移: %.3f ms\n", offset_us / 1000.0);
printf(" 网络延迟: %.3f ms\n", network_delay * 1000);
printf(" 频率漂移: %.3f ppm\n", frequency_drift);

// 调整时钟参数
printf("\n3. 调整时钟参数:\n");
if (getuid() == 0) {
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_OFFSET | ADJ_FREQUENCY;
tx.offset = offset_us; // 微秒偏移
tx.freq = (long)(frequency_drift * 65536); // 转换为系统单位

printf(" 设置时钟偏移: %ld 微秒\n", tx.offset);
printf(" 设置频率调整: %.3f ppm\n", (double)tx.freq / 65536.0);

result = adjtimex(&tx);
if (result == -1) {
printf(" 调整时钟参数失败: %s\n", strerror(errno));
} else {
printf(" ✓ 时钟参数调整成功\n");
printf(" 新时钟状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK\n"); break;
case TIME_INS: printf("TIME_INS\n"); break;
case TIME_DEL: printf("TIME_DEL\n"); break;
case TIME_OOP: printf("TIME_OOP\n"); break;
case TIME_WAIT: printf("TIME_WAIT\n"); break;
case TIME_ERROR: printf("TIME_ERROR\n"); break;
default: printf("未知状态 %d\n", result); break;
}
}
} else {
printf(" ✗ 没有权限调整时钟参数\n");
printf(" 提示:需要root权限才能调整时钟\n");
}

// 验证调整结果
printf("\n4. 验证调整结果:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 调整后时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 调整后最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 调整后估算误差: %ld 毫秒\n", tx.esterror);
}

return 0;
}

/**
* 演示时钟频率调整
*/
int demo_frequency_adjustment() {
struct timex tx;
int result;

printf("=== 时钟频率调整演示 ===\n");

// 显示原始频率
printf("1. 原始时钟频率:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 查询时钟频率失败: %s\n", strerror(errno));
return -1;
}

double original_freq = (double)tx.freq / 65536.0;
printf(" 原始频率: %.6f ppm\n", original_freq);

// 调整频率(需要root权限)
printf("\n2. 调整时钟频率:\n");
if (getuid() == 0) {
// 增加50ppm频率调整
long freq_adjust = 50 * 65536; // 50ppm转换为系统单位

memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_FREQUENCY;
tx.freq = freq_adjust;

printf(" 设置频率调整: %.3f ppm\n", (double)freq_adjust / 65536.0);

result = adjtimex(&tx);
if (result == -1) {
printf(" 频率调整失败: %s\n", strerror(errno));
} else {
printf(" ✓ 频率调整成功\n");
}

// 验证调整结果
printf("\n3. 验证频率调整:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
double new_freq = (double)tx.freq / 65536.0;
printf(" 调整后频率: %.6f ppm\n", new_freq);
printf(" 频率变化: %.6f ppm\n", new_freq - original_freq);
}

// 恢复原始频率
printf("\n4. 恢复原始频率:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_FREQUENCY;
tx.freq = (long)(original_freq * 65536);

result = adjtimex(&tx);
if (result == -1) {
printf(" 恢复原始频率失败: %s\n", strerror(errno));
} else {
printf(" ✓ 原始频率恢复成功\n");
}
} else {
printf(" ✗ 没有权限调整时钟频率\n");
printf(" 提示:需要root权限才能调整时钟频率\n");
}

return 0;
}

int main() {
printf("=== adjtimex时钟调整演示 ===\n");

// 演示NTP同步模拟
if (simulate_ntp_synchronization() != 0) {
return -1;
}

printf("\n" "=" * 50 "\n");

// 演示频率调整
if (demo_frequency_adjustment() != 0) {
return -1;
}

return 0;
}

示例3:高精度时间同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 高精度时间同步器结构
*/
typedef struct {
double last_offset; // 上次时钟偏移
double last_frequency; // 上次频率调整
time_t last_sync_time; // 上次同步时间
int sync_count; // 同步次数
double avg_offset; // 平均偏移
double jitter; // 抖动
int precision_ppm; // 精度(ppm)
} precision_sync_t;

/**
* 初始化高精度同步器
*/
int init_precision_sync(precision_sync_t *sync) {
memset(sync, 0, sizeof(precision_sync_t));
sync->precision_ppm = 1; // 1ppm精度
sync->last_sync_time = time(NULL);

printf("高精度时间同步器初始化完成\n");
printf(" 目标精度: %d ppm\n", sync->precision_ppm);

return 0;
}

/**
* 获取高精度时间
*/
int get_high_precision_time(struct timespec *ts) {
struct timex tx;
int result;

memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
return -1;
}

ts->tv_sec = tx.time.tv_sec;
ts->tv_nsec = tx.time.tv_usec * 1000; // 转换为纳秒

return 0;
}

/**
* 计算时钟偏移
*/
double calculate_clock_offset() {
struct timespec system_time, precise_time;
double offset;

// 获取系统时间
if (clock_gettime(CLOCK_REALTIME, &system_time) == -1) {
return 0.0;
}

// 获取高精度时间
if (get_high_precision_time(&precise_time) == -1) {
return 0.0;
}

// 计算偏移(纳秒)
offset = (precise_time.tv_sec - system_time.tv_sec) * 1000000000.0 +
(precise_time.tv_nsec - system_time.tv_nsec);

return offset / 1000000.0; // 转换为毫秒
}

/**
* 演示高精度时间同步
*/
int demo_high_precision_sync() {
precision_sync_t sync;
struct timex tx;
int result;
int sync_attempts = 5;

printf("=== 高精度时间同步演示 ===\n");

// 初始化同步器
printf("1. 初始化高精度同步器:\n");
if (init_precision_sync(&sync) != 0) {
return -1;
}

// 显示初始时钟状态
printf("\n2. 初始时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf(" 获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

printf(" 时钟状态: ");
switch (tx.state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (错误)\n"); break;
default: printf("状态 %d\n", tx.state); break;
}

printf(" 当前频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
printf(" 时钟精度: %ld 毫秒\n", tx.precision);

// 模拟高精度时间同步过程
printf("\n3. 高精度时间同步过程:\n");

for (int i = 0; i < sync_attempts; i++) {
printf(" 第 %d 次同步:\n", i + 1);

// 模拟网络时间查询
double network_offset = (rand() % 1000 - 500) / 1000.0; // -0.5到0.5毫秒
double network_delay = (rand() % 100) / 1000.0; // 0到0.1毫秒

printf(" 网络偏移: %.3f ms\n", network_offset);
printf(" 网络延迟: %.3f ms\n", network_delay);

// 计算真实的时钟偏移
double actual_offset = calculate_clock_offset();
printf(" 实际偏移: %.3f ms\n", actual_offset);

// 计算综合偏移
double total_offset = network_offset + actual_offset;
printf(" 综合偏移: %.3f ms\n", total_offset);

// 更新同步统计
sync.last_offset = total_offset;
sync.avg_offset = (sync.avg_offset * sync.sync_count + total_offset) / (sync.sync_count + 1);
sync.sync_count++;

// 计算抖动
if (sync.sync_count > 1) {
sync.jitter = fabs(total_offset - sync.avg_offset);
}

printf(" 平均偏移: %.3f ms\n", sync.avg_offset);
printf(" 当前抖动: %.3f ms\n", sync.jitter);

// 如果有root权限,进行时钟调整
if (getuid() == 0) {
memset(&tx, 0, sizeof(tx));
tx.modes = ADJ_OFFSET | ADJ_STATUS;
tx.offset = (long)(total_offset * 1000); // 转换为微秒
tx.status = STA_PLL; // 启用PLL模式

printf(" 调整时钟偏移: %ld 微秒\n", tx.offset);

result = adjtimex(&tx);
if (result == -1) {
printf(" 时钟调整失败: %s\n", strerror(errno));
} else {
printf(" ✓ 时钟调整成功\n");
}
} else {
printf(" ℹ 没有权限进行时钟调整\n");
}

// 记录同步时间
sync.last_sync_time = time(NULL);

if (i < sync_attempts - 1) {
sleep(2); // 间隔同步
}
}

// 显示最终同步结果
printf("\n4. 最终同步结果:\n");
printf(" 同步次数: %d\n", sync.sync_count);
printf(" 最后偏移: %.3f ms\n", sync.last_offset);
printf(" 平均偏移: %.3f ms\n", sync.avg_offset);
printf(" 最大抖动: %.3f ms\n", sync.jitter);
printf(" 最后同步: %s", ctime(&sync.last_sync_time));

// 计算同步精度
double sync_accuracy = 1000.0 / pow(10, sync.precision_ppm); // 简化的精度计算
printf(" 同步精度: %.6f 秒\n", sync_accuracy);

// 显示高精度同步优势
printf("\n=== 高精度同步优势 ===\n");
printf("1. 精度提升:\n");
printf(" ✓ 纳秒级时间精度\n");
printf(" ✓ 微秒级偏移调整\n");
printf(" ✓ PPM级频率控制\n");

printf("\n2. 稳定性保障:\n");
printf(" ✓ 抖动抑制\n");
printf(" ✓ 误差累积控制\n");
printf(" ✓ 频率漂移补偿\n");

printf("\n3. 实时性:\n");
printf(" ✓ 快速收敛\n");
printf(" ✓ 动态调整\n");
printf(" ✓ 状态监控\n");

return 0;
}

int main() {
return demo_high_precision_sync();
}

示例4:时间同步监控

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* 时间同步监控数据结构
*/
typedef struct {
time_t timestamp;
long offset_us; // 偏移(微秒)
long frequency_ppm; // 频率(ppm)
long max_error_ms; // 最大误差(毫秒)
long est_error_ms; // 估算误差(毫秒)
int clock_state; // 时钟状态
int status_flags; // 状态标志
double jitter_ms; // 抖动(毫秒)
} sync_monitor_data_t;

/**
* 时间同步监控器
*/
typedef struct {
sync_monitor_data_t history&#91;100]; // 历史数据
int history_count;
int max_history;
time_t start_time;
int monitoring;
} sync_monitor_t;

/**
* 初始化监控器
*/
int init_sync_monitor(sync_monitor_t *monitor) {
memset(monitor, 0, sizeof(sync_monitor_t));
monitor->max_history = 100;
monitor->start_time = time(NULL);
monitor->monitoring = 1;

printf("时间同步监控器初始化完成\n");
printf(" 最大历史记录数: %d\n", monitor->max_history);
printf(" 启动时间: %s", ctime(&monitor->start_time));

return 0;
}

/**
* 收集时钟状态数据
*/
int collect_clock_data(sync_monitor_t *monitor) {
struct timex tx;
int result;

if (monitor->history_count >= monitor->max_history) {
// 循环覆盖旧数据
memmove(&monitor->history&#91;0], &monitor->history&#91;1],
sizeof(sync_monitor_data_t) * (monitor->max_history - 1));
monitor->history_count = monitor->max_history - 1;
}

sync_monitor_data_t *current = &monitor->history&#91;monitor->history_count];

// 获取时钟状态
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result == -1) {
printf("获取时钟状态失败: %s\n", strerror(errno));
return -1;
}

// 填充监控数据
current->timestamp = time(NULL);
current->offset_us = tx.offset;
current->frequency_ppm = tx.freq / 65536;
current->max_error_ms = tx.maxerror;
current->est_error_ms = tx.esterror;
current->clock_state = tx.state;
current->status_flags = tx.status;
current->jitter_ms = 0.0; // 简化处理

// 计算抖动(与前一个采样点的差异)
if (monitor->history_count > 0) {
sync_monitor_data_t *previous = &monitor->history&#91;monitor->history_count - 1];
current->jitter_ms = fabs((current->offset_us - previous->offset_us) / 1000.0);
}

monitor->history_count++;

return 0;
}

/**
* 显示时钟状态
*/
void show_clock_status(const sync_monitor_data_t *data) {
printf("时间: %s", ctime(&data->timestamp));
printf(" 时钟偏移: %.3f ms\n", data->offset_us / 1000.0);
printf(" 频率调整: %ld ppm\n", data->frequency_ppm);
printf(" 最大误差: %ld ms\n", data->max_error_ms);
printf(" 估算误差: %ld ms\n", data->est_error_ms);
printf(" 时钟状态: %d\n", data->clock_state);
printf(" 抖动: %.3f ms\n", data->jitter_ms);

printf(" 状态标志: 0x%04x ", data->status_flags);
if (data->status_flags & STA_PLL) printf("STA_PLL ");
if (data->status_flags & STA_UNSYNC) printf("STA_UNSYNC ");
if (data->status_flags & STA_FREQHOLD) printf("STA_FREQHOLD ");
printf("\n");
}

/**
* 分析时间同步质量
*/
void analyze_sync_quality(const sync_monitor_t *monitor) {
if (monitor->history_count < 2) {
printf("数据不足,无法分析同步质量\n");
return;
}

printf("=== 时间同步质量分析 ===\n");

// 计算统计信息
double total_offset = 0, max_offset = 0, min_offset = 999999;
double total_jitter = 0, max_jitter = 0;
long total_error = 0, max_error = 0;
int sync_ok_count = 0;

for (int i = 0; i < monitor->history_count; i++) {
const sync_monitor_data_t *data = &monitor->history&#91;i];
double abs_offset = fabs(data->offset_us / 1000.0);

total_offset += abs_offset;
total_jitter += data->jitter_ms;
total_error += data->est_error_ms;

if (abs_offset > max_offset) max_offset = abs_offset;
if (abs_offset < min_offset) min_offset = abs_offset;
if (data->jitter_ms > max_jitter) max_jitter = data->jitter_ms;
if (data->est_error_ms > max_error) max_error = data->est_error_ms;

if (abs_offset < 10.0) sync_ok_count++; // 10ms以内认为同步良好
}

double avg_offset = total_offset / monitor->history_count;
double avg_jitter = total_jitter / monitor->history_count;
double avg_error = (double)total_error / monitor->history_count;
double sync_quality = (double)sync_ok_count / monitor->history_count * 100;

printf("同步质量统计:\n");
printf(" 平均偏移: %.3f ms\n", avg_offset);
printf(" 最大偏移: %.3f ms\n", max_offset);
printf(" 最小偏移: %.3f ms\n", min_offset);
printf(" 平均抖动: %.3f ms\n", avg_jitter);
printf(" 最大抖动: %.3f ms\n", max_jitter);
printf(" 平均误差: %.3f ms\n", avg_error);
printf(" 最大误差: %ld ms\n", max_error);
printf(" 同步质量: %.1f%%\n", sync_quality);

// 质量评估
printf("\n质量评估:\n");
if (avg_offset < 1.0) {
printf(" ✓ 优秀: 平均偏移 < 1ms\n");
} else if (avg_offset < 10.0) {
printf(" ℹ 良好: 平均偏移 < 10ms\n");
} else {
printf(" ⚠ 需要改善: 平均偏移 > 10ms\n");
}

if (sync_quality > 95.0) {
printf(" ✓ 高可靠性: 同步质量 > 95%%\n");
} else if (sync_quality > 80.0) {
printf(" ℹ 中等可靠性: 同步质量 > 80%%\n");
} else {
printf(" ⚠ 低可靠性: 同步质量 < 80%%\n");
}
}

/**
* 演示时间同步监控
*/
int demo_sync_monitoring() {
sync_monitor_t monitor;
const int monitor_duration = 30; // 监控30秒
time_t start_time, current_time;

printf("=== 时间同步监控演示 ===\n");

// 初始化监控器
printf("1. 初始化监控器:\n");
if (init_sync_monitor(&monitor) != 0) {
return -1;
}

// 开始监控
printf("\n2. 开始时间同步监控:\n");
printf(" 监控时长: %d 秒\n", monitor_duration);
printf(" 采样间隔: 2 秒\n");

start_time = time(NULL);

while (difftime(time(NULL), start_time) < monitor_duration) {
// 收集时钟数据
if (collect_clock_data(&monitor) == 0) {
if (monitor.history_count > 0) {
const sync_monitor_data_t *latest =
&monitor.history&#91;monitor.history_count - 1];

printf("\n--- 采样点 %d ---\n", monitor.history_count);
show_clock_status(latest);
}
} else {
printf("收集时钟数据失败\n");
}

sleep(2); // 2秒采样间隔
}

// 显示监控结果
printf("\n3. 监控结果:\n");
printf(" 总采样点数: %d\n", monitor.history_count);
printf(" 监控时长: %.0f 秒\n", difftime(time(NULL), start_time));

if (monitor.history_count > 0) {
printf(" 最新数据:\n");
const sync_monitor_data_t *latest =
&monitor.history&#91;monitor.history_count - 1];
show_clock_status(latest);
}

// 分析同步质量
printf("\n4. 同步质量分析:\n");
analyze_sync_quality(&monitor);

// 显示历史趋势
printf("\n5. 历史趋势 (最后10个采样点):\n");
int start_index = (monitor.history_count > 10) ? monitor.history_count - 10 : 0;

printf("%-20s %-10s %-8s %-8s %-8s\n",
"时间", "偏移(ms)", "频率(ppm)", "误差(ms)", "抖动(ms)");
printf("%-20s %-10s %-8s %-8s %-8s\n",
"----", "--------", "--------", "--------", "--------");

for (int i = start_index; i < monitor.history_count; i++) {
const sync_monitor_data_t *data = &monitor.history&#91;i];
char time_str&#91;20];
strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&data->timestamp));

printf("%-20s %-10.3f %-8ld %-8ld %-8.3f\n",
time_str,
data->offset_us / 1000.0,
data->frequency_ppm,
data->est_error_ms,
data->jitter_ms);
}

return 0;
}

int main() {
return demo_sync_monitoring();
}

示例5:NTP客户端实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include <sys/timex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>
#include <math.h>

/**
* NTP服务器信息结构
*/
typedef struct {
char hostname&#91;256];
int port;
double stratum; // 层级
double delay; // 延迟
double offset; // 偏移
double dispersion; // 离散度
time_t last_contact; // 最后联系时间
int reachable; // 是否可达
} ntp_server_t;

/**
* NTP客户端结构
*/
typedef struct {
ntp_server_t servers&#91;5];
int server_count;
int current_server;
double sync_threshold; // 同步阈值(毫秒)
double max_adjustment; // 最大调整值(毫秒)
int sync_interval; // 同步间隔(秒)
} ntp_client_t;

/**
* 初始化NTP客户端
*/
int init_ntp_client(ntp_client_t *client) {
memset(client, 0, sizeof(ntp_client_t));

// 初始化测试服务器
const char *test_servers&#91;] = {
"pool.ntp.org",
"time.google.com",
"time.cloudflare.com",
"ntp.aliyun.com",
NULL
};

printf("=== NTP客户端初始化 ===\n");

for (int i = 0; test_servers&#91;i] && i < 5; i++) {
ntp_server_t *server = &client->servers&#91;i];
strncpy(server->hostname, test_servers&#91;i], sizeof(server->hostname) - 1);
server->hostname&#91;sizeof(server->hostname) - 1] = '\0';
server->port = 123; // NTP标准端口
server->stratum = 2 + i; // 模拟不同层级
server->delay = 0.05 + (rand() / (double)RAND_MAX) * 0.1; // 50-150ms延迟
server->offset = (rand() / (double)RAND_MAX) * 2.0 - 1.0; // -1到1秒偏移
server->dispersion = 0.01 + (rand() / (double)RAND_MAX) * 0.05; // 10-60ms离散度
server->last_contact = time(NULL);
server->reachable = 1; // 模拟可达

client->server_count++;
printf(" 添加服务器 %d: %s (层级: %.0f)\n",
i + 1, server->hostname, server->stratum);
}

client->current_server = 0;
client->sync_threshold = 100.0; // 100ms阈值
client->max_adjustment = 500.0; // 500ms最大调整
client->sync_interval = 60; // 60秒同步间隔

printf(" 同步阈值: %.1f ms\n", client->sync_threshold);
printf(" 最大调整: %.1f ms\n", client->max_adjustment);
printf(" 同步间隔: %d 秒\n", client->sync_interval);

return 0;
}

/**
* 选择最佳NTP服务器
*/
int select_best_ntp_server(ntp_client_t *client) {
int best_server = -1;
double best_quality = 999999.0;

printf("选择最佳NTP服务器:\n");

for (int i = 0; i < client->server_count; i++) {
ntp_server_t *server = &client->servers&#91;i];

if (server->reachable) {
// 计算服务器质量(基于层级、延迟和离散度)
double quality = server->stratum + server->delay * 10 + server->dispersion * 5;

printf(" 服务器 %d (%s): 质量评分 %.3f\n",
i + 1, server->hostname, quality);

if (quality < best_quality) {
best_quality = quality;
best_server = i;
}
}
}

if (best_server != -1) {
client->current_server = best_server;
printf(" 选择最佳服务器: %s\n",
client->servers&#91;best_server].hostname);
}

return best_server;
}

/**
* 模拟NTP时间同步
*/
int simulate_ntp_sync(ntp_client_t *client) {
struct timex tx;
int result;
int best_server = select_best_ntp_server(client);

if (best_server == -1) {
printf("没有可用的NTP服务器\n");
return -1;
}

ntp_server_t *server = &client->servers&#91;best_server];

printf("=== NTP时间同步 ===\n");
printf("同步服务器: %s\n", server->hostname);
printf("服务器层级: %.0f\n", server->stratum);
printf("网络延迟: %.3f 秒\n", server->delay);
printf("时钟偏移: %.3f 秒\n", server->offset);
printf("离散度: %.3f 秒\n", server->dispersion);

// 检查是否需要同步
if (fabs(server->offset) * 1000 > client->sync_threshold) {
printf("时钟偏移过大,需要同步\n");

// 如果有root权限,进行时钟调整
if (getuid() == 0) {
printf("具有root权限,进行时钟调整:\n");

memset(&tx, 0, sizeof(tx));

// 根据偏移大小选择调整策略
if (fabs(server->offset) > 1.0) {
// 大偏移:步进调整
printf(" 大偏移调整:\n");
tx.modes = ADJ_SETOFFSET;
tx.time.tv_sec = (long)server->offset;
tx.time.tv_usec = (long)((server->offset - (long)server->offset) * 1000000);
printf(" 设置时间偏移: %ld.%06ld 秒\n",
tx.time.tv_sec, tx.time.tv_usec);
} else {
// 小偏移:渐进调整
printf(" 渐进调整:\n");
tx.modes = ADJ_OFFSET | ADJ_STATUS;
tx.offset = (long)(server->offset * 1000000); // 转换为微秒
tx.status = STA_PLL;
printf(" 调整偏移: %ld 微秒\n", tx.offset);
}

result = adjtimex(&tx);
if (result == -1) {
printf(" 时钟调整失败: %s\n", strerror(errno));
return -1;
}

printf(" ✓ 时钟调整成功\n");

// 显示调整后的状态
printf(" 调整后状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK\n"); break;
case TIME_INS: printf("TIME_INS\n"); break;
case TIME_DEL: printf("TIME_DEL\n"); break;
case TIME_OOP: printf("TIME_OOP\n"); break;
case TIME_WAIT: printf("TIME_WAIT\n"); break;
case TIME_ERROR: printf("TIME_ERROR\n"); break;
default: printf("状态 %d\n", result); break;
}
} else {
printf("没有root权限,无法进行时钟调整\n");
printf("建议使用NTP守护进程进行时间同步\n");
}
} else {
printf("时钟偏移在可接受范围内,无需调整\n");
}

// 更新服务器联系时间
server->last_contact = time(NULL);

return 0;
}

/**
* 演示NTP客户端功能
*/
int demo_ntp_client() {
ntp_client_t client;
struct timex tx;
int result;

printf("=== NTP客户端功能演示 ===\n");

// 初始化客户端
printf("1. 初始化NTP客户端:\n");
if (init_ntp_client(&client) != 0) {
return -1;
}

// 显示当前系统时间
printf("\n2. 当前系统时间:\n");
struct timeval current_time;
gettimeofday(&current_time, NULL);
printf(" 系统时间: %ld.%06ld\n", current_time.tv_sec, current_time.tv_usec);

// 获取当前时钟状态
printf("\n3. 当前时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 时钟状态: ");
switch (tx.state) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (错误)\n"); break;
default: printf("状态 %d\n", tx.state); break;
}
printf(" 时钟偏移: %ld.%06ld 秒\n", tx.offset / 1000000, labs(tx.offset) % 1000000);
printf(" 时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
}

// 模拟NTP同步
printf("\n4. 模拟NTP时间同步:\n");
if (simulate_ntp_sync(&client) != 0) {
printf("NTP同步失败\n");
return -1;
}

// 显示同步后状态
printf("\n5. 同步后时钟状态:\n");
memset(&tx, 0, sizeof(tx));
tx.modes = 0; // 查询模式

result = adjtimex(&tx);
if (result != -1) {
printf(" 时钟状态: ");
switch (result) {
case TIME_OK: printf("TIME_OK (正常)\n"); break;
case TIME_INS: printf("TIME_INS (即将插入闰秒)\n"); break;
case TIME_DEL: printf("TIME_DEL (即将删除闰秒)\n"); break;
case TIME_OOP: printf("TIME_OOP (闰秒处理中)\n"); break;
case TIME_WAIT: printf("TIME_WAIT (等待同步)\n"); break;
case TIME_ERROR: printf("TIME_ERROR (时钟错误)\n"); break;
default: printf("状态 %d\n", result); break;
}
printf(" 时钟偏移: %ld.%06ld 秒\n", tx.offset / 1000000, labs(tx.offset) % 1000000);
printf(" 时钟频率: %.3f ppm\n", (double)tx.freq / 65536.0);
printf(" 最大误差: %ld 毫秒\n", tx.maxerror);
printf(" 估算误差: %ld 毫秒\n", tx.esterror);
}

// 显示NTP服务器信息
printf("\n6. NTP服务器信息:\n");
for (int i = 0; i < client.server_count; i++) {
ntp_server_t *server = &client.servers&#91;i];
printf(" 服务器 %d: %s\n", i + 1, server->hostname);
printf(" 层级: %.0f\n", server->stratum);
printf(" 延迟: %.3f 秒\n", server->delay);
printf(" 偏移: %.3f 秒\n", server->offset);
printf(" 离散度: %.3f 秒\n", server->dispersion);
printf(" 最后联系: %s", ctime(&server->last_contact));
printf(" 可达: %s\n", server->reachable ? "是" : "否");
}

// 显示NTP客户端优势
printf("\n=== NTP客户端优势 ===\n");
printf("1. 高精度同步:\n");
printf(" ✓ 微秒级时间精度\n");
printf(" ✓ PPM级频率控制\n");
printf(" ✓ 毫秒级偏移调整\n");

printf("\n2. 智能选择:\n");
printf(" ✓ 多服务器支持\n");
printf(" ✓ 质量评分算法\n");
printf(" ✓ 动态服务器切换\n");

printf("\n3. 安全特性:\n");
printf(" ✓ 权限检查\n");
printf(" ✓ 错误处理\n");
printf(" ✓ 状态监控\n");

printf("\n4. 灵活配置:\n");
printf(" ✓ 可配置阈值\n");
printf(" ✓ 动态间隔调整\n");
printf(" ✓ 多种调整策略\n");

return 0;
}

int main() {
return demo_ntp_client();
}

adjtimex 使用注意事项

系统要求:

内核版本: 需要支持adjtimex的Linux内核

权限要求: 调整时钟需要root权限或CAP_SYS_TIME能力

架构支持: 支持所有主流架构

时钟状态:

TIME_OK: 时钟同步正常

TIME_INS: 即将插入闰秒

TIME_DEL: 即将删除闰秒

TIME_OOP: 闰秒处理中

TIME_WAIT: 等待同步

TIME_ERROR: 时钟错误

状态标志:

STA_PLL: 启用相位锁定环

STA_UNSYNC: 时钟未同步

STA_FREQHOLD: 频率保持

STA_PPSSIGNAL: PPS信号有效

STA_PPSJITTER: PPS抖动过大

STA_PPSWANDER: PPS频率漂移过大

STA_PPSERROR: PPS错误

调整模式:

ADJ_OFFSET: 调整时钟偏移

ADJ_FREQUENCY: 调整时钟频率

ADJ_MAXERROR: 设置最大误差

ADJ_ESTERROR: 设置估算误差

ADJ_STATUS: 设置状态标志

ADJ_TIMECONST: 设置时间常数

ADJ_SETOFFSET: 设置时间偏移

ADJ_MICRO: 微调模式

ADJ_NANO: 纳秒模式

ADJ_TICK: 调整时钟滴答

错误处理:

EPERM: 权限不足

EINVAL: 参数无效

EFAULT: 指针无效

EACCES: 访问被拒绝

EPERM: 操作被禁止

性能考虑:

调整频率: 避免过于频繁的时钟调整

调整幅度: 控制每次调整的幅度

系统负载: 考虑调整对系统性能的影响

监控开销: 减少监控带来的开销

安全考虑:

权限验证: 确保有适当的权限进行调整

参数验证: 验证所有输入参数的有效性

错误恢复: 准备适当的错误恢复机制

审计日志: 记录所有时钟调整操作

最佳实践:

渐进调整: 优先使用渐进调整而非步进调整

权限检查: 执行前检查是否具有足够权限

状态监控: 持续监控时钟状态和性能

错误处理: 妥善处理各种错误情况

日志记录: 记录所有重要的操作和状态变化

timex结构体详解

struct timex 结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct timex {
unsigned int modes; // 操作模式
long offset; // 时钟偏移(微秒)
long freq; // 频率调整(系统单位)
long maxerror; // 最大误差(毫秒)
long esterror; // 估算误差(毫秒)
int status; // 状态标志
long constant; // PLL时间常数
long precision; // 时钟精度(毫秒)
long tolerance; // 频率容忍度(ppm)
struct timeval time; // 当前时间
long tick; // 时钟滴答值
long ppsfreq; // PPS频率(系统单位)
long jitter; // PPS抖动(纳秒)
int shift; // PPS间隔宽度
long stabil; // PPS频率稳定度
long jitcnt; // PPS抖动计数
long calcnt; // PPS校准计数
long errcnt; // PPS错误计数
long stbcnt; // PPS稳定计数
int tai; // TAI偏移
int state; // 时钟状态
int :32; int :32; int :32; int :32;
};

相关函数和工具

系统调用:

1
2
3
4
5
6
7
8
#include <sys/timex.h>

// 时钟调整和查询
int adjtimex(struct timex *buf);

// 更现代的时钟调整接口
int clock_adjtime(clockid_t clk_id, struct timex *buf);

命令行工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 显示时钟状态
timex -p

# NTP时间同步
ntpd -q

# 手动时间同步
ntpdate pool.ntp.org

# 显示时间信息
date
hwclock

常见使用场景

1. NTP客户端:

1
2
3
4
5
6
// 调整时钟偏移
struct timex tx;
tx.modes = ADJ_OFFSET;
tx.offset = measured_offset_us;
adjtimex(&tx);

2. 系统时钟监控:

1
2
3
4
5
// 监控时钟状态
struct timex tx;
tx.modes = 0; // 查询模式
int state = adjtimex(&tx);

3. 高精度时间服务:

1
2
3
4
5
6
// 频率调整
struct timex tx;
tx.modes = ADJ_FREQUENCY;
tx.freq = ppm * 65536; // 转换为系统单位
adjtimex(&tx);

总结

adjtimex 是Linux系统中强大的时间管理函数,提供了:

精确控制: 微秒级时钟偏移调整

频率管理: PPM级频率控制

状态监控: 实时时钟状态查询

错误处理: 完善的错误处理机制

通过合理使用 adjtimex,可以构建高精度的时间同步系统。在实际应用中,需要注意权限要求、错误处理和性能优化等关键问题。

BPF系统调用及示例

这次我们介绍 bpf 函数,它是 Linux 内核中 **Berkeley Packet Filter **(BPF) 子系统的用户态接口。

1. 函数介绍

bpf 是一个功能极其强大的 Linux 系统调用(内核版本 >= 3.18,但许多高级特性需要更新的内核),它提供了一种在内核空间安全、高效地运行用户定义程序的机制。

你可以把 BPF 想象成一个内核里的虚拟机:

  • 你(用户态程序)可以编写一段用BPF 指令集编写的“小程序”(eBPF 程序)。

  • 你将这段程序加载到内核中。

  • 内核会验证这段程序的安全性(确保它不会导致死循环、不会访问非法内存等)。

  • 如果验证通过,内核会即时编译 (JIT) 这段程序为机器码,并将其附加到特定的内核钩子(hook points)上。

  • 当内核执行到这些钩子时(例如,收到网络包、进行系统调用、跟踪函数调用),就会执行你加载的 BPF 程序。

  • BPF 程序可以进行过滤、修改、收集信息(遥测)、路由等操作。

主要用途:

  • 网络编程: 高性能数据包过滤(tcpdump)、流量整形、负载均衡、XDP(eXpress Data Path)超高速网络处理。

  • 系统监控和追踪: 跟踪内核函数、用户态函数、系统调用,收集性能指标(如 perf)、调试信息。

  • 安全: 实施安全策略、沙箱、审计。

  • 性能分析: 无侵入式地分析应用程序和内核性能瓶颈。

2. 函数原型

1
2
3
4
#include <linux/bpf.h> // 必需,包含 BPF 相关常量和结构体

long bpf(int cmd, union bpf_attr *attr, unsigned int size);

3. 功能

  • 统一接口: bpf 系统调用是操作 eBPF 子系统的统一入口点。几乎所有与 eBPF 相关的操作(创建、加载、附加、查询、删除等)都通过这个单一的系统调用来完成。

  • 多用途: 根据 cmd 参数的不同,bpf 可以执行完全不同的操作。

4. 参数

int cmd: 指定要执行的具体 BPF 操作。这是一个枚举值(定义在 <linux/bpf.h> 中)。常见的命令包括:

  • BPF_MAP_CREATE: 创建一个 BPF 映射(Map)。映射是 BPF 程序和用户态程序之间共享数据的高效机制。

  • BPF_PROG_LOAD: 将一个 BPF 程序加载到内核中。

  • BPF_OBJ_PIN / BPF_OBJ_GET: 将 BPF 对象(程序或映射)固定到文件系统路径或从路径获取。

  • BPF_PROG_ATTACH / BPF_PROG_DETACH: 将已加载的 BPF 程序附加到或从特定的挂钩点(如 cgroup、网络设备)分离。

  • BPF_PROG_RUN / BPF_PROG_TEST_RUN: (测试)运行 BPF 程序。

  • BPF_MAP_LOOKUP_ELEM / BPF_MAP_UPDATE_ELEM / BPF_MAP_DELETE_ELEM: 对 BPF 映射进行查找、更新、删除元素操作。

  • BPF_PROG_GET_NEXT_ID / BPF_PROG_GET_FD_BY_ID: 枚举和通过 ID 获取 BPF 程序。

  • BPF_MAP_GET_NEXT_ID / BPF_MAP_GET_FD_BY_ID: 枚举和通过 ID 获取 BPF 映射。

  • … 还有很多其他命令 …

union bpf_attr *attr: 这是一个指向 union bpf_attr 结构体的指针。这个联合体包含了执行 cmd 指定操作所需的所有可能参数。根据 cmd 的不同,bpf 系统调用会从这个联合体中读取或写入特定的成员。

  • 例如,对于 BPF_MAP_CREATE,它会读取 attr->map_type, attr->key_size, attr->value_size, attr->max_entries 等成员。

  • 对于 BPF_PROG_LOAD,它会读取 attr->prog_type, attr->insn_cnt, attr->insns, attr->license 等成员。

unsigned int size: 指定 attr 指向的 union bpf_attr 结构体的大小(以字节为单位)。内核使用这个大小来进行兼容性检查和内存访问边界控制。

5. union bpf_attr 结构体

union bpf_attr 是一个巨大的联合体,包含了所有 BPF 操作可能需要的参数。它的定义非常庞大,这里只列举几个关键成员以说明其结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
union bpf_attr {
struct { /* anonymous struct for BPF_MAP_CREATE */
__u32 map_type; // 映射类型 (BPF_MAP_TYPE_*)
__u32 key_size; // 键大小
__u32 value_size; // 值大小
__u32 max_entries; // 最大元素个数
__u32 map_flags; // 标志位
__u32 inner_map_fd; // 用于 array/hash of maps
__u32 numa_node; // NUMA 节点
char map_name&#91;BPF_OBJ_NAME_LEN]; // 映射名称
__u32 map_ifindex; // 网络接口索引
// ... 更多字段 ...
}; // BPF_MAP_CREATE 使用这些字段

struct { /* anonymous struct for BPF_PROG_LOAD */
__u32 prog_type; // 程序类型 (BPF_PROG_TYPE_*)
__u32 insn_cnt; // 指令数量
__aligned_u64 insns; // 指向指令数组的用户态指针
__aligned_u64 license; // 指向许可证字符串的用户态指针 ("GPL")
__u32 log_level; // 日志级别
__u32 log_size; // 日志缓冲区大小
__aligned_u64 log_buf; // 指向日志缓冲区的用户态指针
__u32 kern_version; // 内核版本 (用于追踪程序)
__u32 prog_flags; // 程序标志
char prog_name&#91;BPF_OBJ_NAME_LEN]; // 程序名称
__u32 prog_ifindex; // 网络接口索引
// ... 更多字段 ...
}; // BPF_PROG_LOAD 使用这些字段

// ... 还有很多其他匿名结构体,对应不同的 cmd ...
};

6. 返回值

成功时: 返回值取决于具体的 cmd。

  • 对于 BPF_MAP_CREATE, BPF_PROG_LOAD 等创建操作:通常返回一个新的文件描述符(fd),用于引用新创建的 BPF 映射或程序。

  • 对于 BPF_MAP_LOOKUP_ELEM 等查询操作:可能返回 0 表示成功。

  • 对于 BPF_PROG_ATTACH 等操作:可能返回 0 表示成功。

失败时: 返回 -1,并设置全局变量 errno 来指示具体的错误原因(例如 EINVAL 参数无效,EACCES 权限不足,ENOMEM 内存不足,E2BIG 程序太大或映射太大,EPERM 操作不被允许等)。

7. 相似函数,或关联函数

  • libbpf: 一个 C 库,提供了对 bpf 系统调用的高级封装,简化了 eBPF 程序的加载、映射操作和附加过程。这是编写 eBPF 应用程序的推荐方式。

  • bpftool: 一个命令行工具,用于检查、调试和操作 eBPF 程序和映射。它本身就是 bpf 系统调用的使用者。

  • LLVM/Clang: 用于将 C 语言编写的 eBPF 程序编译成 BPF 字节码。

  • perf: 可以与 eBPF 结合使用进行性能分析。

  • bcc / bpftrace: 更高级别的工具和库,进一步简化了 eBPF 的使用,允许用 Python 或特定领域语言编写脚本。

8. 示例代码

重要提示: 直接使用 bpf 系统调用编写 eBPF 程序非常复杂,涉及大量的底层细节、内存管理和联合体操作。下面的示例将展示一个极其简化的、概念性的 C 代码,旨在说明 bpf 系统调用的调用方式和参数结构。实际的 eBPF 开发通常使用 libbpf 库。

示例 1:概念性地使用 bpf 系统调用

这个例子展示了如何直接调用 bpf 系统调用(通过 syscall)来创建一个简单的 BPF 映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// bpf_conceptual.c
// 注意:这是一个非常简化的概念性示例,不包含实际的 eBPF 程序加载。
// 实际使用需要 libbpf 或 LLVM/Clang 工具链。
#define _GNU_SOURCE
#include <linux/bpf.h> // 包含 BPF 相关定义
#include <sys/syscall.h> // syscall
#include <unistd.h> // close
#include <stdio.h> // perror, printf
#include <stdlib.h> // exit
#include <string.h> // memset
#include <errno.h> // errno

// 简化包装 syscall
static inline long sys_bpf(int cmd, union bpf_attr *attr, unsigned int size) {
return syscall(__NR_bpf, cmd, attr, size);
}

int main() {
union bpf_attr attr;
int map_fd;

printf("Using bpf syscall directly to create a map...\n");

// 1. 清零 attr 联合体
memset(&attr, 0, sizeof(attr));

// 2. 填充 BPF_MAP_CREATE 所需的参数
attr.map_type = BPF_MAP_TYPE_ARRAY; // 创建一个数组类型的映射
attr.key_size = sizeof(int); // 键是 int 类型 (4 bytes)
attr.value_size = sizeof(long long); // 值是 long long 类型 (8 bytes)
attr.max_entries = 10; // 数组大小为 10
// attr.map_flags = 0; // 可以设置标志,这里用默认值
snprintf(attr.map_name, sizeof(attr.map_name), "my_array_map"); // 设置映射名称

printf("Creating BPF_MAP_TYPE_ARRAY with:\n");
printf(" map_type: %u (BPF_MAP_TYPE_ARRAY)\n", attr.map_type);
printf(" key_size: %u bytes\n", attr.key_size);
printf(" value_size: %u bytes\n", attr.value_size);
printf(" max_entries: %u\n", attr.max_entries);
printf(" map_name: %s\n", attr.map_name);

// 3. 调用 bpf 系统调用 (BPF_MAP_CREATE)
printf("Calling bpf(BPF_MAP_CREATE, ...)\n");
map_fd = sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
if (map_fd < 0) {
perror("bpf BPF_MAP_CREATE failed");
if (errno == EPERM) {
printf("Permission denied. You might need to run this as root or adjust capabilities.\n");
printf("Try: sudo ./bpf_conceptual\n");
}
exit(EXIT_FAILURE);
}

printf("BPF map created successfully. File descriptor: %d\n", map_fd);

// 4. (概念性) 使用 map_fd 进行后续操作
// 例如,使用 BPF_MAP_UPDATE_ELEM 更新元素
// union bpf_attr update_attr;
// memset(&update_attr, 0, sizeof(update_attr));
// update_attr.map_fd = map_fd;
// int key = 5;
// long long value = 1234567890LL;
// update_attr.key = (unsigned long)&key;
// update_attr.value = (unsigned long)&value;
// update_attr.flags = BPF_ANY; // 如果存在则更新,否则创建
// if (sys_bpf(BPF_MAP_UPDATE_ELEM, &update_attr, sizeof(update_attr)) == -1) {
// perror("bpf BPF_MAP_UPDATE_ELEM failed");
// } else {
// printf("Successfully updated element at key %d to value %lld\n", key, value);
// }

// 5. 关闭映射文件描述符
printf("Closing BPF map file descriptor...\n");
if (close(map_fd) == -1) {
perror("close BPF map fd failed");
} else {
printf("BPF map file descriptor closed.\n");
}

printf("Conceptual bpf syscall example completed.\n");
return 0;
}

**代码解释 **(概念性):

定义 sys_bpf 包装 syscall(__NR_bpf, …),因为 glibc 可能没有直接包装 bpf。

声明 union bpf_attr attr 用于传递参数。

清零 attr 联合体,这是一个好习惯,确保未使用的字段为 0。

填充 attr:

  • map_type = BPF_MAP_TYPE_ARRAY: 指定创建数组映射。

  • key_size = sizeof(int): 键是 4 字节整数。

  • value_size = sizeof(long long): 值是 8 字节长整数。

  • max_entries = 10: 数组包含 10 个元素。

  • snprintf(attr.map_name, …): 设置映射的名称。

调用 sys_bpf:

  • cmd = BPF_MAP_CREATE: 指定创建映射操作。

  • &attr: 指向填充好的参数联合体。

  • sizeof(attr): 联合体的大小。

检查返回值:

  • 如果返回值 map_fd 是一个非负整数,表示成功,这个 map_fd 是新创建映射的文件描述符。

  • 如果返回 -1,检查 errno。EPERM 表示权限不足,通常需要 root 权限。

打印成功信息和返回的文件描述符。

概念性操作: 注释掉了使用 BPF_MAP_UPDATE_ELEM 命令更新映射元素的代码。

使用 close(map_fd) 关闭映射文件描述符,释放资源。

示例 2:使用 libbpf 创建和使用 BPF 映射 (推荐方式)

这个例子展示了使用 libbpf 库(现代推荐方式)来创建和操作 BPF 映射。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// bpf_libbpf_example.c
// 编译: gcc -o bpf_libbpf_example bpf_libbpf_example.c -lbpf
// 注意:需要安装 libbpf-dev 包

/*
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <bpf/libbpf.h> // libbpf 库
#include <bpf/bpf.h> // bpf_map_update_elem, bpf_map_lookup_elem 等辅助函数
#include <stdio.h> // printf, perror
#include <stdlib.h> // exit
#include <unistd.h> // close (如果需要)

int main() {
int map_fd = -1;
int err;
int key = 5;
long long value = 9876543210LL;
long long lookup_value;

printf("Using libbpf to create and manipulate a BPF map...\n");

// 1. 使用 libbpf 创建 BPF 映射
struct bpf_map *map = bpf_map__new(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 10, 0, "my_libbpf_array_map");
if (!map) {
fprintf(stderr, "Failed to create BPF map using libbpf.\n");
exit(EXIT_FAILURE);
}

// 2. 获取映射的文件描述符
map_fd = bpf_map__fd(map);
if (map_fd < 0) {
fprintf(stderr, "Failed to get map file descriptor.\n");
bpf_map__destroy(map); // 清理
exit(EXIT_FAILURE);
}

printf("BPF map created using libbpf. File descriptor: %d\n", map_fd);

// 3. 使用 libbpf 辅助函数更新映射元素
printf("Updating element at key %d with value %lld...\n", key, value);
err = bpf_map_update_elem(map_fd, &key, &value, BPF_ANY);
if (err) {
perror("bpf_map_update_elem failed");
bpf_map__destroy(map);
exit(EXIT_FAILURE);
}
printf("Element updated successfully.\n");

// 4. 使用 libbpf 辅助函数查找映射元素
printf("Looking up element at key %d...\n", key);
err = bpf_map_lookup_elem(map_fd, &key, &lookup_value);
if (err) {
perror("bpf_map_lookup_elem failed");
bpf_map__destroy(map);
exit(EXIT_FAILURE);
}
printf("Found element at key %d with value %lld.\n", key, lookup_value);

// 5. 清理资源
printf("Destroying BPF map...\n");
bpf_map__destroy(map); // 这会关闭 fd 并释放资源
printf("BPF map destroyed.\n");

printf("libbpf example completed.\n");
return 0;
}
*/
// 由于 libbpf 依赖和编译可能较为复杂,此处提供伪代码框架。
// 实际使用请参考 libbpf 文档和示例。

**代码解释 **(概念性/伪代码):

包含 libbpf 库的头文件。

创建映射:

  • 调用 libbpf 提供的高级函数 bpf_map__new 来创建映射。

  • 这比直接使用 bpf 系统调用简单得多,库会处理联合体的填充和系统调用。

获取文件描述符:

  • 调用 bpf_map__fd 获取映射的文件描述符,用于后续操作。

操作映射:

  • 使用 libbpf 提供的辅助函数 bpf_map_update_elem 和 bpf_map_lookup_elem 来更新和查找映射中的元素。

  • 这些函数内部会调用 bpf 系统调用(如 BPF_MAP_UPDATE_ELEM)。

清理:

  • 调用 bpf_map__destroy 来销毁映射并释放所有相关资源(包括关闭文件描述符)。

重要提示与注意事项:

内核版本: eBPF 是一个快速发展的领域,新特性和功能不断加入。确保你的 Linux 内核版本足够新以支持你需要的功能。

权限: 使用 bpf 系统调用通常需要特殊权限,如 CAP_SYS_ADMIN 或 CAP_BPF(较新内核)。在生产环境中,应遵循最小权限原则。

libbpf 是推荐方式: 直接使用 bpf 系统调用非常复杂且容易出错。libbpf 库极大地简化了开发流程,提供了更好的可移植性和错误处理。

程序加载: 加载 eBPF 程序(BPF_PROG_LOAD)比创建映射复杂得多,需要预先编译好的 BPF 字节码,并处理验证、日志等。

安全性: eBPF 程序在加载到内核前会经过严格的验证器(verifier)检查,确保其安全性(无无限循环、无非法内存访问等)。这是 eBPF 能够安全运行在内核中的关键。

性能: eBPF 程序在内核中运行,并且通常会被 JIT 编译成高效的机器码,性能非常高。

调试: bpftool 和 bpf_trace_printk 是调试 eBPF 程序的常用工具。

总结:

bpf 系统调用是 Linux eBPF 子系统的核心接口,它提供了一种强大、安全且高效的方式让用户态程序在内核中执行自定义逻辑。虽然直接使用它非常底层和复杂,但通过 libbpf 等高级库,开发者可以更轻松地利用 eBPF 的强大功能来构建网络、安全、监控和性能分析等领域的前沿应用。理解其基本概念和工作原理对于现代 Linux 系统程序员来说至关重要。

io_uring 系统调用及示例

io_uring 系统调用详解

  1. 函数介绍

io_uring 是Linux 5.1引入的高性能异步I/O框架,提供了一种现代化的异步I/O接口。相比传统的AIO(异步I/O),io_uring具有更好的性能、更低的系统调用开销和更丰富的功能。它使用共享内存环形缓冲区实现用户空间和内核空间的高效通信。

  1. 函数原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <liburing.h>
#include <linux/io_uring.h>

// 初始化io_uring实例
int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags);

// 销毁io_uring实例
int io_uring_queue_exit(struct io_uring *ring);

// 提交I/O请求
int io_uring_submit(struct io_uring *ring);

// 等待I/O完成事件
int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr);

// 标记完成事件已处理
void io_uring_cqe_seen(struct io_uring *ring, struct io_uring_cqe *cqe);

// 准备I/O操作
void io_uring_prep_read(struct io_uring_sqe *sqe, int fd, void *buf, unsigned nbytes, __u64 offset);
void io_uring_prep_write(struct io_uring_sqe *sqe, int fd, const void *buf, unsigned nbytes, __u64 offset);
void io_uring_prep_openat(struct io_uring_sqe *sqe, int dfd, const char *path, int flags, mode_t mode);
void io_uring_prep_close(struct io_uring_sqe *sqe, int fd);
void io_uring_prep_fsync(struct io_uring_sqe *sqe, int fd, unsigned fsync_flags);

  1. 功能

io_uring 提供了完整的异步I/O解决方案,支持文件I/O、网络I/O、文件操作等多种操作类型。它通过共享内存环形缓冲区实现零拷贝的数据传输,显著提高了I/O性能。

  1. 参数说明

io_uring_queue_init参数:

  • unsigned entries: 环形缓冲区大小(必须是2的幂)

  • *struct io_uring ring: io_uring实例指针

  • unsigned flags: 初始化标志

io_uring_queue_exit参数:

  • *struct io_uring ring: 要销毁的io_uring实例

io_uring_submit参数:

  • *struct io_uring ring: io_uring实例

io_uring_wait_cqe参数:

  • *struct io_uring ring: io_uring实例

  • **struct io_uring_cqe cqe_ptr: 完成事件指针

  1. 返回值
  • 成功: 返回非负值或0

  • 失败: 返回负的错误码

  1. 相似函数,或关联函数
  • io_setup/io_destroy: 传统的AIO接口

  • io_submit/io_cancel: 传统的AIO提交和取消

  • io_getevents: 传统的AIO事件获取

  • aio_read/aio_write: POSIX AIO接口

  • epoll_wait/poll: 传统的I/O多路复用

  1. 示例代码

示例1:基础io_uring使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <liburing.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

/**
* 演示基础io_uring使用方法
*/
int demo_io_uring_basic() {
struct io_uring ring;
int ret;

printf("=== 基础io_uring使用示例 ===\n");

// 初始化io_uring实例
printf("1. 初始化io_uring实例:\n");
ret = io_uring_queue_init(32, &ring, 0);
if (ret < 0) {
printf(" 初始化失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ io_uring实例初始化成功\n");
printf(" 环形缓冲区大小: 32\n");

// 显示io_uring信息
printf(" io_uring信息:\n");
printf(" 提交队列大小: %u\n", ring.sq.ring_sz);
printf(" 完成队列大小: %u\n", ring.cq.ring_sz);
printf(" 特性标志: 0x%x\n", ring.features);

// 创建测试文件
printf("\n2. 创建测试文件:\n");
const char *test_filename = "io_uring_test.txt";
int test_fd = open(test_filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (test_fd == -1) {
perror(" 创建测试文件失败");
io_uring_queue_exit(&ring);
return -1;
}
printf(" ✓ 测试文件创建成功: %s\n", test_filename);

// 准备测试数据
const char *test_data = "Hello from io_uring! This is a test message.\n";
size_t data_size = strlen(test_data);

// 使用io_uring写入数据
printf("\n3. 使用io_uring写入数据:\n");
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf(" 获取SQE失败\n");
close(test_fd);
unlink(test_filename);
io_uring_queue_exit(&ring);
return -1;
}

// 准备写入操作
io_uring_prep_write(sqe, test_fd, test_data, data_size, 0);
sqe->user_data = 1; // 设置用户数据

printf(" 准备写入操作:\n");
printf(" 文件描述符: %d\n", test_fd);
printf(" 数据大小: %zu 字节\n", data_size);
printf(" 偏移量: 0\n");

// 提交操作
ret = io_uring_submit(&ring);
if (ret <= 0) {
printf(" 提交操作失败: %s\n", strerror(-ret));
close(test_fd);
unlink(test_filename);
io_uring_queue_exit(&ring);
return -1;
}
printf(" ✓ 操作提交成功,提交了 %d 个请求\n", ret);

// 等待完成
printf("\n4. 等待I/O操作完成:\n");
struct io_uring_cqe *cqe;
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf(" 等待完成事件失败: %s\n", strerror(-ret));
close(test_fd);
unlink(test_filename);
io_uring_queue_exit(&ring);
return -1;
}

printf(" ✓ I/O操作完成\n");
printf(" 用户数据: %llu\n", (unsigned long long)cqe->user_data);
printf(" 结果: %d 字节\n", cqe->res);
printf(" 标志: 0x%x\n", cqe->flags);

// 标记完成事件已处理
io_uring_cqe_seen(&ring, cqe);

// 读取写入的数据验证
printf("\n5. 验证写入结果:\n");
close(test_fd);

test_fd = open(test_filename, O_RDONLY);
if (test_fd != -1) {
char read_buffer&#91;256];
ssize_t bytes_read = read(test_fd, read_buffer, sizeof(read_buffer) - 1);
if (bytes_read > 0) {
read_buffer&#91;bytes_read] = '\0';
printf(" 读取到的数据 (%zd 字节):\n", bytes_read);
printf(" %s", read_buffer);
}
close(test_fd);
}

// 清理资源
printf("\n6. 清理资源:\n");
unlink(test_filename);
io_uring_queue_exit(&ring);
printf(" ✓ 资源清理完成\n");

return 0;
}

int main() {
return demo_io_uring_basic();
}

示例2:批量I/O操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#include <liburing.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>

/**
* 批量I/O操作演示
*/
int demo_batch_io_operations() {
struct io_uring ring;
const int batch_size = 8;
const int file_count = 5;
int ret;

printf("=== 批量I/O操作演示 ===\n");

// 初始化io_uring
printf("1. 初始化io_uring:\n");
ret = io_uring_queue_init(64, &ring, 0);
if (ret < 0) {
printf(" 初始化失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ io_uring初始化成功\n");

// 创建测试文件
printf("\n2. 创建测试文件:\n");
int file_fds&#91;file_count];
char filenames&#91;file_count]&#91;32];

for (int i = 0; i < file_count; i++) {
snprintf(filenames&#91;i], sizeof(filenames&#91;i]), "batch_test_%d.txt", i);
file_fds&#91;i] = open(filenames&#91;i], O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (file_fds&#91;i] == -1) {
perror(" 创建文件失败");
// 清理已创建的文件
for (int j = 0; j < i; j++) {
close(file_fds&#91;j]);
unlink(filenames&#91;j]);
}
io_uring_queue_exit(&ring);
return -1;
}
printf(" 创建文件 %d: %s\n", i, filenames&#91;i]);
}

// 准备批量写入操作
printf("\n3. 准备批量写入操作:\n");
char *test_data&#91;file_count];
int submitted_ops = 0;

for (int i = 0; i < file_count; i++) {
// 分配测试数据
test_data&#91;i] = malloc(256);
if (!test_data&#91;i]) {
perror(" 分配测试数据失败");
// 清理资源
for (int j = 0; j <= i; j++) {
if (test_data&#91;j]) free(test_data&#91;j]);
if (j < file_count) {
close(file_fds&#91;j]);
unlink(filenames&#91;j]);
}
}
io_uring_queue_exit(&ring);
return -1;
}

snprintf(test_data&#91;i], 256,
"Batch write test data for file %d. Operation count: %d\n", i, i + 1);

// 准备多个写入操作
for (int j = 0; j < batch_size && submitted_ops < 32; j++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf(" 获取SQE失败\n");
break;
}

// 准备写入操作
io_uring_prep_write(sqe, file_fds&#91;i], test_data&#91;i], strlen(test_data&#91;i]),
j * 256);
sqe->user_data = (i * batch_size + j); // 唯一标识符

submitted_ops++;
}

printf(" 为文件 %d 准备了 %d 个写入操作\n", i, batch_size);
}

printf("\n4. 批量提交I/O操作:\n");
printf(" 总共准备了 %d 个I/O操作\n", submitted_ops);

ret = io_uring_submit(&ring);
if (ret <= 0) {
printf(" 提交操作失败: %s\n", strerror(-ret));
} else {
printf(" ✓ 成功提交 %d 个I/O操作\n", ret);
}

// 等待所有操作完成
printf("\n5. 等待所有操作完成:\n");
int completed_ops = 0;

while (completed_ops < submitted_ops) {
struct io_uring_cqe *cqe;
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf(" 等待完成事件失败: %s\n", strerror(-ret));
break;
}

completed_ops++;
printf(" 操作 %d 完成: 写入 %d 字节\n",
(int)cqe->user_data, cqe->res);

// 标记完成事件已处理
io_uring_cqe_seen(&ring, cqe);
}

printf(" 总共完成了 %d 个I/O操作\n", completed_ops);

// 验证写入结果
printf("\n6. 验证写入结果:\n");
for (int i = 0; i < file_count; i++) {
close(file_fds&#91;i]);

// 重新打开文件读取
int read_fd = open(filenames&#91;i], O_RDONLY);
if (read_fd != -1) {
struct stat st;
if (fstat(read_fd, &st) == 0) {
printf(" 文件 %s: 大小 %ld 字节\n", filenames&#91;i], st.st_size);
}
close(read_fd);
}
}

// 清理资源
printf("\n7. 清理资源:\n");
for (int i = 0; i < file_count; i++) {
free(test_data&#91;i]);
unlink(filenames&#91;i]);
}

io_uring_queue_exit(&ring);
printf(" ✓ 所有资源清理完成\n");

return 0;
}

int main() {
return demo_batch_io_operations();
}

示例3:文件操作演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <liburing.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>

/**
* 文件操作结构
*/
typedef struct {
int fd;
char filename&#91;64];
off_t file_size;
int operation_type; // 0:read, 1:write, 2:open, 3:close, 4:fsync
} file_operation_t;

/**
* 演示文件操作
*/
int demo_file_operations() {
struct io_uring ring;
int ret;

printf("=== 文件操作演示 ===\n");

// 初始化io_uring
printf("1. 初始化io_uring:\n");
ret = io_uring_queue_init(16, &ring, 0);
if (ret < 0) {
printf(" 初始化失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ io_uring初始化成功\n");

// 演示异步文件打开
printf("\n2. 异步文件打开操作:\n");
const char *test_filename = "async_file_test.txt";

struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf(" 获取SQE失败\n");
io_uring_queue_exit(&ring);
return -1;
}

// 准备打开文件操作
io_uring_prep_openat(sqe, AT_FDCWD, test_filename,
O_CREAT | O_RDWR | O_TRUNC, 0644);
sqe->user_data = 1;

printf(" 准备打开文件: %s\n", test_filename);

ret = io_uring_submit(&ring);
if (ret <= 0) {
printf(" 提交打开操作失败: %s\n", strerror(-ret));
io_uring_queue_exit(&ring);
return -1;
}

// 等待打开完成
struct io_uring_cqe *cqe;
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf(" 等待打开完成失败: %s\n", strerror(-ret));
io_uring_queue_exit(&ring);
return -1;
}

int file_fd = cqe->res;
printf(" ✓ 文件打开成功,文件描述符: %d\n", file_fd);
io_uring_cqe_seen(&ring, cqe);

// 演示异步写入操作
printf("\n3. 异步写入操作:\n");
const char *write_data = "This is asynchronous write test data.\nMultiple lines of test content.\nEnd of test data.\n";
size_t write_size = strlen(write_data);

sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf(" 获取SQE失败\n");
close(file_fd);
unlink(test_filename);
io_uring_queue_exit(&ring);
return -1;
}

io_uring_prep_write(sqe, file_fd, write_data, write_size, 0);
sqe->user_data = 2;

printf(" 准备写入数据: %zu 字节\n", write_size);

ret = io_uring_submit(&ring);
if (ret <= 0) {
printf(" 提交写入操作失败: %s\n", strerror(-ret));
} else {
printf(" ✓ 写入操作提交成功\n");
}

// 等待写入完成
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf(" 等待写入完成失败: %s\n", strerror(-ret));
} else {
printf(" ✓ 写入完成: %d 字节\n", cqe->res);
io_uring_cqe_seen(&ring, cqe);
}

// 演示异步fsync操作
printf("\n4. 异步fsync操作:\n");
sqe = io_uring_get_sqe(&ring);
if (sqe) {
io_uring_prep_fsync(sqe, file_fd, 0);
sqe->user_data = 3;

printf(" 准备fsync操作\n");

ret = io_uring_submit(&ring);
if (ret > 0) {
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret == 0) {
printf(" ✓ fsync完成\n");
io_uring_cqe_seen(&ring, cqe);
}
}
}

// 演示异步读取操作
printf("\n5. 异步读取操作:\n");
char read_buffer&#91;256];

sqe = io_uring_get_sqe(&ring);
if (sqe) {
io_uring_prep_read(sqe, file_fd, read_buffer, sizeof(read_buffer) - 1, 0);
sqe->user_data = 4;

printf(" 准备读取操作\n");

ret = io_uring_submit(&ring);
if (ret > 0) {
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret == 0) {
if (cqe->res > 0) {
read_buffer&#91;cqe->res] = '\0';
printf(" ✓ 读取完成: %d 字节\n", cqe->res);
printf(" 读取内容:\n%s", read_buffer);
} else {
printf(" 读取失败或文件为空\n");
}
io_uring_cqe_seen(&ring, cqe);
}
}
}

// 演示异步关闭操作
printf("\n6. 异步关闭操作:\n");
sqe = io_uring_get_sqe(&ring);
if (sqe) {
io_uring_prep_close(sqe, file_fd);
sqe->user_data = 5;

printf(" 准备关闭文件\n");

ret = io_uring_submit(&ring);
if (ret > 0) {
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret == 0) {
printf(" ✓ 文件关闭完成\n");
io_uring_cqe_seen(&ring, cqe);
}
}
}

// 清理资源
printf("\n7. 清理资源:\n");
unlink(test_filename);
io_uring_queue_exit(&ring);
printf(" ✓ 资源清理完成\n");

return 0;
}

int main() {
return demo_file_operations();
}

示例4:网络I/O演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <liburing.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/**
* 演示网络I/O操作
*/
int demo_network_io() {
struct io_uring ring;
int ret;

printf("=== 网络I/O操作演示 ===\n");

// 初始化io_uring
printf("1. 初始化io_uring:\n");
ret = io_uring_queue_init(32, &ring, 0);
if (ret < 0) {
printf(" 初始化失败: %s\n", strerror(-ret));
return -1;
}
printf(" ✓ io_uring初始化成功\n");

// 演示异步socket创建
printf("\n2. 异步socket创建:\n");
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf(" 获取SQE失败\n");
io_uring_queue_exit(&ring);
return -1;
}

// 注意:io_uring的网络I/O支持需要较新的内核版本
printf(" 注意:网络I/O操作需要Linux 5.5+内核支持\n");
printf(" 在本演示中,我们将展示准备网络操作的方法\n");

// 演示网络操作准备(伪代码)
printf("\n3. 网络操作准备示例:\n");
printf(" // 创建TCP socket\n");
printf(" sqe = io_uring_get_sqe(&ring);\n");
printf(" io_uring_prep_socket(sqe, AF_INET, SOCK_STREAM, 0, 0);\n");
printf(" sqe->user_data = 1;\n");
printf("\n");

printf(" // 连接服务器\n");
printf(" struct sockaddr_in addr;\n");
printf(" memset(&addr, 0, sizeof(addr));\n");
printf(" addr.sin_family = AF_INET;\n");
printf(" addr.sin_port = htons(80);\n");
printf(" addr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");\n");
printf(" io_uring_prep_connect(sqe, sockfd, &addr, sizeof(addr));\n");
printf("\n");

printf(" // 发送数据\n");
printf(" const char *data = \"GET / HTTP/1.1\\r\\n\\r\\n\";\n");
printf(" io_uring_prep_send(sqe, sockfd, data, strlen(data), 0);\n");
printf("\n");

printf(" // 接收数据\n");
printf(" char buffer&#91;1024];\n");
printf(" io_uring_prep_recv(sqe, sockfd, buffer, sizeof(buffer), 0);\n");

// 显示网络I/O优势
printf("\n=== 网络I/O优势 ===\n");
printf("1. 高性能:\n");
printf(" ✓ 零拷贝数据传输\n");
printf(" ✓ 减少系统调用开销\n");
printf(" ✓ 提高并发处理能力\n");

printf("\n2. 低延迟:\n");
printf(" ✓ 快速事件通知\n");
printf(" ✓ 减少上下文切换\n");
printf(" ✓ 优化内存访问模式\n");

printf("\n3. 可扩展性:\n");
printf(" ✓ 支持大量并发连接\n");
printf(" ✓ 高效的事件处理\n");
printf(" ✓ 灵活的缓冲区管理\n");

// 清理资源
printf("\n4. 清理资源:\n");
io_uring_queue_exit(&ring);
printf(" ✓ io_uring资源清理完成\n");

return 0;
}

int main() {
return demo_network_io();
}

示例5:性能对比测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#include <liburing.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>

/**
* 性能测试结果结构
*/
typedef struct {
const char *test_name;
long long execution_time_us;
int operation_count;
double throughput_ops;
double average_latency_us;
} performance_result_t;

/**
* 获取当前时间(微秒)
*/
long long get_current_time_us() {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000LL + tv.tv_usec;
}

/**
* 传统同步I/O性能测试
*/
int test_sync_io_performance(performance_result_t *result) {
const int operation_count = 1000;
const size_t buffer_size = 4096;
char *buffer = malloc(buffer_size);
long long start_time, end_time;

if (!buffer) {
return -1;
}

printf("执行同步I/O性能测试...\n");

// 创建测试文件
const char *filename = "sync_test.dat";
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
free(buffer);
return -1;
}

start_time = get_current_time_us();

// 执行同步写入操作
for (int i = 0; i < operation_count; i++) {
// 填充测试数据
for (size_t j = 0; j < buffer_size; j++) {
buffer&#91;j] = 'A' + (i + j) % 26;
}

ssize_t written = write(fd, buffer, buffer_size);
if (written != (ssize_t)buffer_size) {
printf("写入失败\n");
close(fd);
unlink(filename);
free(buffer);
return -1;
}
}

end_time = get_current_time_us();

close(fd);
unlink(filename);
free(buffer);

result->execution_time_us = end_time - start_time;
result->operation_count = operation_count;
result->throughput_ops = (double)operation_count / (result->execution_time_us / 1000000.0);
result->average_latency_us = (double)result->execution_time_us / operation_count;

printf("同步I/O测试完成\n");
return 0;
}

/**
* io_uring异步I/O性能测试
*/
int test_io_uring_performance(performance_result_t *result) {
struct io_uring ring;
const int operation_count = 1000;
const size_t buffer_size = 4096;
char **buffers;
long long start_time, end_time;
int ret;

printf("执行io_uring异步I/O性能测试...\n");

// 初始化io_uring
ret = io_uring_queue_init(256, &ring, 0);
if (ret < 0) {
printf("io_uring初始化失败: %s\n", strerror(-ret));
return -1;
}

// 分配缓冲区
buffers = malloc(operation_count * sizeof(char*));
if (!buffers) {
io_uring_queue_exit(&ring);
return -1;
}

for (int i = 0; i < operation_count; i++) {
buffers&#91;i] = malloc(buffer_size);
if (!buffers&#91;i]) {
// 清理已分配的缓冲区
for (int j = 0; j < i; j++) {
free(buffers&#91;j]);
}
free(buffers);
io_uring_queue_exit(&ring);
return -1;
}

// 填充测试数据
for (size_t j = 0; j < buffer_size; j++) {
buffers&#91;i]&#91;j] = 'A' + (i + j) % 26;
}
}

// 创建测试文件
const char *filename = "async_test.dat";
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
// 清理缓冲区
for (int i = 0; i < operation_count; i++) {
free(buffers&#91;i]);
}
free(buffers);
io_uring_queue_exit(&ring);
return -1;
}

start_time = get_current_time_us();

// 提交异步写入操作
int submitted = 0;
for (int i = 0; i < operation_count; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
if (!sqe) {
printf("获取SQE失败\n");
break;
}

io_uring_prep_write(sqe, fd, buffers&#91;i], buffer_size, i * buffer_size);
sqe->user_data = i;

submitted++;

// 定期提交操作
if (submitted % 32 == 0 || i == operation_count - 1) {
ret = io_uring_submit(&ring);
if (ret < 0) {
printf("提交操作失败: %s\n", strerror(-ret));
break;
}
}
}

printf("提交了 %d 个异步操作\n", submitted);

// 等待所有操作完成
int completed = 0;
while (completed < submitted) {
struct io_uring_cqe *cqe;
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
printf("等待完成事件失败: %s\n", strerror(-ret));
break;
}

completed++;
io_uring_cqe_seen(&ring, cqe);
}

end_time = get_current_time_us();

printf("完成了 %d 个异步操作\n", completed);

// 清理资源
close(fd);
unlink(filename);

for (int i = 0; i < operation_count; i++) {
free(buffers&#91;i]);
}
free(buffers);
io_uring_queue_exit(&ring);

result->execution_time_us = end_time - start_time;
result->operation_count = completed;
result->throughput_ops = (double)completed / (result->execution_time_us / 1000000.0);
result->average_latency_us = (double)result->execution_time_us / completed;

printf("io_uring异步I/O测试完成\n");
return 0;
}

/**
* 演示性能对比测试
*/
int demo_performance_comparison() {
performance_result_t sync_result = {0};
performance_result_t async_result = {0};

printf("=== io_uring vs 同步I/O 性能对比 ===\n");

// 设置测试结果名称
sync_result.test_name = "同步I/O";
async_result.test_name = "io_uring异步I/O";

// 执行同步I/O测试
printf("1. 执行同步I/O测试:\n");
if (test_sync_io_performance(&sync_result) != 0) {
printf(" 同步I/O测试失败\n");
return -1;
}

printf(" 测试完成\n");

// 执行io_uring测试
printf("\n2. 执行io_uring异步I/O测试:\n");
if (test_io_uring_performance(&async_result) != 0) {
printf(" io_uring测试失败\n");
return -1;
}

printf(" 测试完成\n");

// 显示测试结果
printf("\n=== 性能测试结果 ===\n");
printf("%-20s %-15s %-15s %-15s %-15s\n",
"测试类型", "操作次数", "耗时(μs)", "吞吐量(ops/s)", "平均延迟(μs)");
printf("%-20s %-15s %-15s %-15s %-15s\n",
"--------", "--------", "--------", "------------", "------------");

printf("%-20s %-15d %-15lld %-15.0f %-15.2f\n",
sync_result.test_name,
sync_result.operation_count,
sync_result.execution_time_us,
sync_result.throughput_ops,
sync_result.average_latency_us);

printf("%-20s %-15d %-15lld %-15.0f %-15.2f\n",
async_result.test_name,
async_result.operation_count,
async_result.execution_time_us,
async_result.throughput_ops,
async_result.average_latency_us);

// 性能对比分析
printf("\n=== 性能对比分析 ===\n");
if (sync_result.execution_time_us > 0 && async_result.execution_time_us > 0) {
double time_improvement = (double)sync_result.execution_time_us / async_result.execution_time_us;
double throughput_improvement = async_result.throughput_ops / sync_result.throughput_ops;
double latency_reduction = (sync_result.average_latency_us - async_result.average_latency_us) /
sync_result.average_latency_us * 100;

printf("执行时间对比: %.2f 倍提升\n", time_improvement);
printf("吞吐量对比: %.2f 倍提升\n", throughput_improvement);
printf("平均延迟减少: %.1f%%\n", latency_reduction);
}

// 显示优势分析
printf("\n=== 优势分析 ===\n");
printf("1. io_uring优势:\n");
printf(" ✓ 零拷贝数据传输\n");
printf(" ✓ 减少系统调用次数\n");
printf(" ✓ 提高I/O并发性能\n");
printf(" ✓ 更好的CPU利用率\n");

printf("\n2. 适用场景:\n");
printf(" ✓ 高并发网络服务器\n");
printf(" ✓ 大文件传输应用\n");
printf(" ✓ 实时数据处理\n");
printf(" ✓ 数据库存储引擎\n");

printf("\n3. 性能优化建议:\n");
printf(" ✓ 合理设置环形缓冲区大小\n");
printf(" ✓ 批量提交I/O操作\n");
printf(" ✓ 使用适当的等待策略\n");
printf(" ✓ 监控系统资源使用\n");

return 0;
}

int main() {
return demo_performance_comparison();
}

io_uring 使用注意事项

系统要求:

内核版本: 需要Linux 5.1或更高版本

架构支持: 支持所有主流架构

编译要求: 需要liburing库支持

初始化选项:

IORING_SETUP_IOPOLL: 启用I/O轮询模式

IORING_SETUP_SQPOLL: 启用提交队列轮询

IORING_SETUP_SQ_AFF: 设置提交队列CPU亲和性

IORING_SETUP_CQSIZE: 设置完成队列大小

错误处理:

负返回值: 表示错误码

errno设置: 传统错误码机制

完成事件: 通过cqe->res返回结果

性能考虑:

缓冲区大小: 合理设置环形缓冲区大小

批量操作: 批量提交提高效率

内存管理: 避免频繁的内存分配

CPU亲和性: 考虑CPU绑定优化

安全考虑:

权限检查: 确保有足够的权限

资源限制: 避免消耗过多系统资源

输入验证: 验证所有输入参数

错误恢复: 妥善处理各种错误情况

最佳实践:

环境检查: 使用前检查内核支持

参数验证: 验证所有输入参数

错误处理: 妥善处理各种错误

资源管理: 及时释放分配的资源

性能监控: 监控性能指标并优化

io_uring vs 传统AIO对比

传统AIO限制:

1
2
3
4
5
6
7
8
9
// 传统AIO接口
#include <linux/aio_abi.h>
int io_setup(unsigned nr_events, aio_context_t *ctxp);
int io_destroy(aio_context_t ctx);
int io_submit(aio_context_t ctx, long nr, struct iocb *ios&#91;]);
int io_cancel(aio_context_t ctx, struct iocb *iocb, struct io_event *result);
int io_getevents(aio_context_t ctx, long min_nr, long nr,
struct io_event *events, struct timespec *timeout);

io_uring优势:

1
2
3
4
5
6
7
// io_uring接口
#include <liburing.h>
int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags);
int io_uring_queue_exit(struct io_uring *ring);
int io_uring_submit(struct io_uring *ring);
int io_uring_wait_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr);

性能对比数据

系统调用开销:

  • 传统AIO: 每次操作需要多个系统调用

  • io_uring: 批量操作减少系统调用次数

内存拷贝:

  • 传统AIO: 需要多次内存拷贝

  • io_uring: 零拷贝数据传输

并发性能:

  • 传统AIO: 并发性能有限

  • io_uring: 高并发性能优异

常见使用场景

1. 网络服务器:

1
2
3
4
5
6
7
8
9
10
11
12
// 高性能网络服务器
struct io_uring ring;
io_uring_queue_init(4096, &ring, 0);

// 批量处理网络请求
for (int i = 0; i < connections; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_recv(sqe, conn_fd&#91;i], buffer&#91;i], buffer_size, 0);
}

io_uring_submit(&ring);

2. 存储系统:

1
2
3
4
5
6
7
8
9
10
11
12
// 高性能存储系统
struct io_uring ring;
io_uring_queue_init(8192, &ring, IORING_SETUP_IOPOLL);

// 批量存储操作
for (int i = 0; i < io_count; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_write(sqe, fd, data&#91;i], size&#91;i], offset&#91;i]);
}

io_uring_submit(&ring);

3. 数据库引擎:

1
2
3
4
5
6
7
8
9
10
11
12
// 数据库存储引擎
struct io_uring ring;
io_uring_queue_init(2048, &ring, 0);

// 并发数据页读写
for (int i = 0; i < pages; i++) {
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
io_uring_prep_read(sqe, fd, page_buffer&#91;i], page_size, page_offset&#91;i]);
}

io_uring_submit(&ring);

总结

io_uring 是Linux系统中先进的异步I/O框架,提供了:

高性能: 显著优于传统AIO的性能

易用性: 简化的API设计

灵活性: 丰富的操作类型支持

可扩展性: 支持大规模并发操作

通过合理使用io_uring,可以构建高性能的I/O密集型应用。在实际应用中,需要注意内核版本要求、错误处理和性能优化等关键问题。

Linux定时器管理-深入解析timer_*函数

linux定时器管理timer_* 函数详解

掌握Linux定时器管理,深入解析timer_*系列函数用法与示例,助力开发者高效控制任务调度,提升程序性能。

  1. 函数介绍

timer_ 函数系列*是Linux系统中用于定时器管理的一组函数,它们提供了精确的时间控制和定时功能。可以把timer_*函数想象成一个”精密时钟系统”,允许你设置各种类型的定时器,包括一次性定时器、周期性定时器、以及高精度定时器。

这些函数基于POSIX定时器标准,提供了比传统alarm()函数更强大和灵活的功能。timer_*函数可以:

  • 创建和管理多个定时器

  • 设置一次性或周期性定时

  • 指定定时器到期时的行为(发送信号或执行回调)

  • 精确控制定时器的时间间隔

  • 查询和修改定时器状态

使用场景:

  • 网络服务器中的超时控制

  • 实时系统中的周期性任务

  • 游戏开发中的帧率控制

  • 系统监控和定时检查

  • 多媒体应用中的同步控制

  1. 函数原型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <time.h>
#include <signal.h>

// 创建定时器
int timer_create(clockid_t clockid, struct sigevent *sevp, timer_t *timerid);

// 启动/修改定时器
int timer_settime(timer_t timerid, int flags,
const struct itimerspec *new_value,
struct itimerspec *old_value);

// 获取定时器时间
int timer_gettime(timer_t timerid, struct itimerspec *curr_value);

// 获取定时器超时次数
int timer_getoverrun(timer_t timerid);

// 删除定时器
int timer_delete(timer_t timerid);

  1. 功能

timer_*函数系列提供了完整的定时器管理功能:

  • timer_create: 创建一个新的定时器,指定时钟源和到期通知方式

  • timer_settime: 启动、停止或修改定时器的定时参数

  • timer_gettime: 查询定时器的当前状态和剩余时间

  • timer_getoverrun: 获取定时器的超限运行次数(当定时器到期但未被处理时)

  • timer_delete: 删除和清理定时器资源

  1. 参数

timer_create参数:

clockid: 时钟类型

  • 类型:clockid_t

  • 含义:指定定时器使用的时钟源

常用值:

  • CLOCK_REALTIME:系统实时时间

  • CLOCK_MONOTONIC:单调时钟(不会受系统时间调整影响)

  • CLOCK_PROCESS_CPUTIME_ID:进程CPU时间

  • CLOCK_THREAD_CPUTIME_ID:线程CPU时间

sevp: 信号事件结构

  • 类型:struct sigevent*

  • 含义:指定定时器到期时的通知方式

  • NULL表示使用默认的SIGALRM信号

timerid: 定时器ID

  • 类型:timer_t*

  • 含义:返回创建的定时器标识符

timer_settime参数:

timerid: 定时器ID

  • 类型:timer_t

  • 含义:要操作的定时器标识符

flags: 操作标志

  • 类型:int

  • 含义:控制定时器行为的标志

常用值:

  • 0:相对时间

  • TIMER_ABSTIME:绝对时间

new_value: 新的定时器设置

  • 类型:const struct itimerspec*

  • 含义:指定定时器的新参数

old_value: 旧的定时器设置

  • 类型:struct itimerspec*

  • 含义:返回定时器之前的设置(可为NULL)

  1. 返回值
  • 成功: 返回0

失败: 返回-1,并设置errno错误码

  • EINVAL:参数无效

  • ENOMEM:内存不足

  • EPERM:权限不足

  • EAGAIN:资源暂时不可用

  1. 相似函数或关联函数
  • alarm(): 传统的一次性定时器函数

  • setitimer(): 更灵活的间隔定时器函数

  • sleep()/usleep(): 简单的延迟函数

  • clock_gettime(): 获取时钟时间

  • nanosleep(): 高精度睡眠函数

  • signal()/sigaction(): 信号处理函数

  1. 示例代码

示例1:基础timer使用 - 简单定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <errno.h>

// 定时器信号处理函数
void timer_handler(int sig, siginfo_t *si, void *uc) {
timer_t *tidp = si->si_value.sival_ptr;
printf("&#91;%ld] 定时器到期! 定时器ID: %p\n", time(NULL), (void*)*tidp);
}

int main() {
timer_t timerid;
struct sigevent sev;
struct itimerspec its;
struct sigaction sa;

printf("=== 基础定时器示例 ===\n");
printf("当前时间: %ld\n", time(NULL));

// 设置信号处理函数
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}

// 创建定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create");
exit(EXIT_FAILURE);
}

printf("定时器创建成功,ID: %p\n", (void*)timerid);

// 设置定时器:5秒后开始,每2秒触发一次
its.it_value.tv_sec = 5; // 初始延迟5秒
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 2; // 周期间隔2秒
its.it_interval.tv_nsec = 0;

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("定时器已启动:5秒后首次触发,之后每2秒触发一次\n");
printf("程序将运行20秒...\n\n");

// 等待定时器触发
sleep(20);

// 停止定时器
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("\n定时器已停止\n");

// 删除定时器
if (timer_delete(timerid) == -1) {
perror("timer_delete");
exit(EXIT_FAILURE);
}

printf("定时器已删除\n");

return 0;
}

示例2:多种定时器类型和时钟源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>

#define NUM_TIMERS 4

timer_t timers&#91;NUM_TIMERS];
int timer_counts&#91;NUM_TIMERS] = {0};

// 定时器信号处理函数
void timer_handler(int sig, siginfo_t *si, void *uc) {
int timer_index = si->si_value.sival_int;

timer_counts&#91;timer_index]++;
printf("&#91;%ld] 定时器 %d 到期第 %d 次\n",
time(NULL), timer_index, timer_counts&#91;timer_index]);

// 演示timer_gettime
struct itimerspec curr_value;
if (timer_gettime(timers&#91;timer_index], &curr_value) == 0) {
printf(" 剩余时间: %ld.%09ld 秒\n",
curr_value.it_value.tv_sec, curr_value.it_value.tv_nsec);
}

// 演示timer_getoverrun
int overrun = timer_getoverrun(timers&#91;timer_index]);
if (overrun > 0) {
printf(" 超限运行: %d 次\n", overrun);
}
}

int main() {
struct sigaction sa;
struct sigevent sev;
struct itimerspec its;
int i;

printf("=== 多种定时器类型示例 ===\n");
printf("当前时间: %ld\n\n", time(NULL));

// 设置信号处理函数
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}

// 创建不同类型的定时器
for (i = 0; i < NUM_TIMERS; i++) {
// 设置信号事件
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_int = i;

clockid_t clock_type;
const char* clock_name;

switch(i) {
case 0:
clock_type = CLOCK_REALTIME;
clock_name = "CLOCK_REALTIME";
break;
case 1:
clock_type = CLOCK_MONOTONIC;
clock_name = "CLOCK_MONOTONIC";
break;
case 2:
clock_type = CLOCK_PROCESS_CPUTIME_ID;
clock_name = "CLOCK_PROCESS_CPUTIME_ID";
break;
case 3:
clock_type = CLOCK_THREAD_CPUTIME_ID;
clock_name = "CLOCK_THREAD_CPUTIME_ID";
break;
default:
clock_type = CLOCK_REALTIME;
clock_name = "DEFAULT";
break;
}

// 创建定时器
if (timer_create(clock_type, &sev, &timers&#91;i]) == -1) {
printf("创建定时器 %d (%s) 失败: %s\n", i, clock_name, strerror(errno));
continue;
}

printf("定时器 %d (%s) 创建成功\n", i, clock_name);

// 设置不同的定时参数
switch(i) {
case 0: // 一次性定时器
its.it_value.tv_sec = 3 + i; // 3秒后触发
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0; // 不重复
its.it_interval.tv_nsec = 0;
printf(" 设置为一次性定时器,%d秒后触发\n", 3 + i);
break;

case 1: // 短周期定时器
its.it_value.tv_sec = 2; // 2秒后首次触发
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 1; // 每1秒重复
its.it_interval.tv_nsec = 0;
printf(" 设置为周期性定时器,2秒后开始,每1秒触发\n");
break;

case 2: // 长周期定时器
its.it_value.tv_sec = 5; // 5秒后首次触发
its.it_value.tv_nsec = 500000000; // 500毫秒
its.it_interval.tv_sec = 3; // 每3秒重复
its.it_interval.tv_nsec = 0;
printf(" 设置为周期性定时器,5.5秒后开始,每3秒触发\n");
break;

case 3: // 快速定时器
its.it_value.tv_sec = 1; // 1秒后首次触发
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0; // 每0.5秒重复
its.it_interval.tv_nsec = 500000000; // 500毫秒
printf(" 设置为周期性定时器,1秒后开始,每0.5秒触发\n");
break;
}

// 启动定时器
if (timer_settime(timers&#91;i], 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}
}

printf("\n所有定时器已启动,程序运行20秒...\n\n");

// 运行一段时间观察定时器效果
sleep(20);

// 停止所有定时器
printf("\n=== 停止所有定时器 ===\n");
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;

for (i = 0; i < NUM_TIMERS; i++) {
if (timer_settime(timers&#91;i], 0, &its, NULL) == -1) {
printf("停止定时器 %d 失败\n", i);
} else {
printf("定时器 %d 已停止\n", i);
}
}

// 删除所有定时器
printf("\n=== 删除所有定时器 ===\n");
for (i = 0; i < NUM_TIMERS; i++) {
if (timer_delete(timers&#91;i]) == -1) {
printf("删除定时器 %d 失败\n", i);
} else {
printf("定时器 %d 已删除\n", i);
}
}

// 显示统计信息
printf("\n=== 定时器触发统计 ===\n");
for (i = 0; i < NUM_TIMERS; i++) {
printf("定时器 %d 触发次数: %d\n", i, timer_counts&#91;i]);
}

return 0;
}

示例3:高精度定时器和绝对时间定时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>

timer_t timerid;
int trigger_count = 0;

// 定时器信号处理函数
void timer_handler(int sig, siginfo_t *si, void *uc) {
trigger_count++;

struct timespec current_time;
clock_gettime(CLOCK_REALTIME, &current_time);

printf("&#91;%ld.%09ld] 定时器第 %d 次触发\n",
current_time.tv_sec, current_time.tv_nsec, trigger_count);
}

// 获取当前时间字符串
void print_current_time() {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
printf("当前时间: %ld.%09ld\n", ts.tv_sec, ts.tv_nsec);
}

int main() {
struct sigaction sa;
struct sigevent sev;
struct itimerspec its;
struct timespec current_time;

printf("=== 高精度定时器和绝对时间示例 ===\n");
print_current_time();
printf("\n");

// 设置信号处理函数
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = timer_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}

// 创建定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_ptr = &timerid;

if (timer_create(CLOCK_REALTIME, &sev, &timerid) == -1) {
perror("timer_create");
exit(EXIT_FAILURE);
}

printf("高精度定时器创建成功\n");

// 示例1: 相对时间定时器(高精度)
printf("\n--- 相对时间定时器 ---\n");
print_current_time();

// 设置1.5秒的延迟定时器
its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 500000000; // 500毫秒
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("设置1.5秒延迟定时器...\n");
sleep(3); // 等待定时器触发

// 示例2: 周期性高精度定时器
printf("\n--- 周期性高精度定时器 ---\n");
print_current_time();

// 设置周期性定时器:0.1秒后开始,每0.2秒触发
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 100000000; // 100毫秒
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 200000000; // 200毫秒

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("设置周期性定时器:100毫秒后开始,每200毫秒触发\n");
printf("运行5秒观察效果...\n\n");

sleep(5);

// 停止定时器
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("\n周期性定时器已停止\n");
printf("触发次数: %d\n", trigger_count);

// 示例3: 绝对时间定时器
printf("\n--- 绝对时间定时器 ---\n");
trigger_count = 0;

// 获取当前时间并设置5秒后的绝对时间
if (clock_gettime(CLOCK_REALTIME, &current_time) == -1) {
perror("clock_gettime");
exit(EXIT_FAILURE);
}

print_current_time();

// 设置绝对时间:当前时间+3秒
struct itimerspec abs_its;
abs_its.it_value.tv_sec = current_time.tv_sec + 3;
abs_its.it_value.tv_nsec = current_time.tv_nsec;
abs_its.it_interval.tv_sec = 0;
abs_its.it_interval.tv_nsec = 0;

printf("设置绝对时间定时器:3秒后触发\n");

if (timer_settime(timerid, TIMER_ABSTIME, &abs_its, NULL) == -1) {
perror("timer_settime (绝对时间)");
exit(EXIT_FAILURE);
}

sleep(5); // 等待定时器触发

// 示例4: 定时器状态查询
printf("\n--- 定时器状态查询 ---\n");

// 重新设置一个周期性定时器用于测试
its.it_value.tv_sec = 1;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 2;
its.it_interval.tv_nsec = 0;

if (timer_settime(timerid, 0, &its, NULL) == -1) {
perror("timer_settime");
exit(EXIT_FAILURE);
}

printf("设置新的周期性定时器:1秒后开始,每2秒触发\n");

// 查询定时器状态
sleep(1); // 等待首次触发后

struct itimerspec query_its;
if (timer_gettime(timerid, &query_its) == 0) {
printf("定时器状态查询结果:\n");
printf(" 到期间隔: %ld.%09ld 秒\n",
query_its.it_interval.tv_sec, query_its.it_interval.tv_nsec);
printf(" 剩余时间: %ld.%09ld 秒\n",
query_its.it_value.tv_sec, query_its.it_value.tv_nsec);
}

// 演示timer_getoverrun
printf("定时器超限运行测试(快速触发):\n");
sleep(6);

int overrun = timer_getoverrun(timerid);
printf("超限运行次数: %d\n", overrun);

// 清理
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
timer_settime(timerid, 0, &its, NULL);
timer_delete(timerid);

printf("\n所有测试完成\n");

return 0;
}

示例4:定时器在实际应用中的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <string.h>
#include <pthread.h>

#define MAX_TASKS 10
#define HEARTBEAT_INTERVAL 5

// 任务结构体
typedef struct {
int id;
char name&#91;50];
int interval_seconds;
time_t last_run;
int run_count;
} task_t;

task_t tasks&#91;MAX_TASKS];
int task_count = 0;
timer_t heartbeat_timer;
timer_t task_timers&#91;MAX_TASKS];
pthread_mutex_t task_mutex = PTHREAD_MUTEX_INITIALIZER;

// 添加任务
int add_task(int id, const char* name, int interval) {
if (task_count >= MAX_TASKS) {
return -1;
}

tasks&#91;task_count].id = id;
strncpy(tasks&#91;task_count].name, name, sizeof(tasks&#91;task_count].name) - 1);
tasks&#91;task_count].interval_seconds = interval;
tasks&#91;task_count].last_run = 0;
tasks&#91;task_count].run_count = 0;

return task_count++;
}

// 任务执行函数
void execute_task(int task_index) {
pthread_mutex_lock(&task_mutex);

time_t current_time = time(NULL);
tasks&#91;task_index].last_run = current_time;
tasks&#91;task_index].run_count++;

printf("&#91;%ld] 执行任务 %d (%s): 第 %d 次执行\n",
current_time,
tasks&#91;task_index].id,
tasks&#91;task_index].name,
tasks&#91;task_index].run_count);

pthread_mutex_unlock(&task_mutex);
}

// 任务定时器处理函数
void task_timer_handler(int sig, siginfo_t *si, void *uc) {
int task_index = si->si_value.sival_int;
execute_task(task_index);
}

// 心跳定时器处理函数
void heartbeat_handler(int sig, siginfo_t *si, void *uc) {
static int heartbeat_count = 0;
heartbeat_count++;

printf("&#91;%ld] 系统心跳 #%d\n", time(NULL), heartbeat_count);

// 显示所有任务状态
pthread_mutex_lock(&task_mutex);
printf(" 任务状态:\n");
for (int i = 0; i < task_count; i++) {
printf(" %s: 执行%d次, 最后执行: %s",
tasks&#91;i].name, tasks&#91;i].run_count,
tasks&#91;i].last_run ? ctime(&tasks&#91;i].last_run) : "从未执行\n");
if (tasks&#91;i].last_run) {
// 移除ctime返回的换行符
char* newline = strchr(ctime(&tasks&#91;i].last_run), '\n');
if (newline) *newline = '\0';
printf("%s\n", ctime(&tasks&#91;i].last_run));
}
}
pthread_mutex_unlock(&task_mutex);
}

// 初始化定时器系统
int init_timer_system() {
struct sigaction sa;

// 设置任务定时器信号处理
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = task_timer_handler;
sigemptyset(&sa.sa_mask);
if (sigaction(SIGRTMIN, &sa, NULL) == -1) {
perror("sigaction task");
return -1;
}

// 设置心跳定时器信号处理
sa.sa_sigaction = heartbeat_handler;
if (sigaction(SIGRTMIN + 1, &sa, NULL) == -1) {
perror("sigaction heartbeat");
return -1;
}

return 0;
}

// 启动任务定时器
int start_task_timer(int task_index) {
struct sigevent sev;
struct itimerspec its;

// 创建定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN;
sev.sigev_value.sival_int = task_index;

if (timer_create(CLOCK_REALTIME, &sev, &task_timers&#91;task_index]) == -1) {
perror("timer_create task");
return -1;
}

// 设置定时器
its.it_value.tv_sec = tasks&#91;task_index].interval_seconds;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = tasks&#91;task_index].interval_seconds;
its.it_interval.tv_nsec = 0;

if (timer_settime(task_timers&#91;task_index], 0, &its, NULL) == -1) {
perror("timer_settime task");
return -1;
}

printf("任务定时器 %s 已启动,间隔 %d 秒\n",
tasks&#91;task_index].name, tasks&#91;task_index].interval_seconds);

return 0;
}

// 启动心跳定时器
int start_heartbeat_timer() {
struct sigevent sev;
struct itimerspec its;

// 创建心跳定时器
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGRTMIN + 1;
sev.sigev_value.sival_int = 0;

if (timer_create(CLOCK_REALTIME, &sev, &heartbeat_timer) == -1) {
perror("timer_create heartbeat");
return -1;
}

// 设置心跳定时器(每5秒触发一次)
its.it_value.tv_sec = HEARTBEAT_INTERVAL;
its.it_value.tv_nsec = 0;
its.it_interval.tv_sec = HEARTBEAT_INTERVAL;
its.it_interval.tv_nsec = 0;

if (timer_settime(heartbeat_timer, 0, &its, NULL) == -1) {
perror("timer_settime heartbeat");
return -1;
}

printf("心跳定时器已启动,间隔 %d 秒\n", HEARTBEAT_INTERVAL);

return 0;
}

int main() {
printf("=== 实际应用中的定时器系统 ===\n");
printf("启动时间: %s", ctime(&(time_t){time(NULL)}));

// 初始化定时器系统
if (init_timer_system() == -1) {
fprintf(stderr, "初始化定时器系统失败\n");
exit(EXIT_FAILURE);
}

// 添加一些测试任务
add_task(1, "数据备份", 10);
add_task(2, "日志清理", 15);
add_task(3, "状态检查", 5);
add_task(4, "性能监控", 3);

printf("已添加 %d 个任务\n", task_count);

// 启动所有任务定时器
for (int i = 0; i < task_count; i++) {
if (start_task_timer(i) == -1) {
fprintf(stderr, "启动任务定时器 %d 失败\n", i);
}
}

// 启动心跳定时器
if (start_heartbeat_timer() == -1) {
fprintf(stderr, "启动心跳定时器失败\n");
}

printf("\n定时器系统运行中...\n");
printf("程序将运行60秒,按Ctrl+C退出\n\n");

// 运行主循环
for (int i = 0; i < 60; i++) {
sleep(1);

// 每10秒显示一次统计信息
if ((i + 1) % 10 == 0) {
printf("&#91;%ld] === 运行统计 ===\n", time(NULL));
pthread_mutex_lock(&task_mutex);
for (int j = 0; j < task_count; j++) {
printf(" %s: 执行 %d 次\n",
tasks&#91;j].name, tasks&#91;j].run_count);
}
pthread_mutex_unlock(&task_mutex);
printf("==================\n\n");
}
}

// 清理定时器
printf("清理定时器...\n");

// 停止并删除心跳定时器
struct itimerspec stop_its = {{0, 0}, {0, 0}};
timer_settime(heartbeat_timer, 0, &stop_its, NULL);
timer_delete(heartbeat_timer);

// 停止并删除所有任务定时器
for (int i = 0; i < task_count; i++) {
timer_settime(task_timers&#91;i], 0, &stop_its, NULL);
timer_delete(task_timers&#91;i]);
}

printf("定时器系统已停止\n");

// 显示最终统计
printf("\n=== 最终统计 ===\n");
pthread_mutex_lock(&task_mutex);
for (int i = 0; i < task_count; i++) {
printf("%s: 执行 %d 次\n", tasks&#91;i].name, tasks&#91;i].run_count);
}
pthread_mutex_unlock(&task_mutex);

return 0;
}

编译和运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 编译示例1
gcc -o timer_example1 timer_example1.c -lrt
./timer_example1

# 编译示例2
gcc -o timer_example2 timer_example2.c -lrt
./timer_example2

# 编译示例3
gcc -o timer_example3 timer_example3.c -lrt
./timer_example3

# 编译示例4
gcc -o timer_example4 timer_example4.c -lrt -lpthread
./timer_example4

重要注意事项

链接库: 使用timer_*函数需要链接实时库(-lrt)

信号处理: 定时器通常通过信号通知,需要注意信号安全

精度限制: 实际精度受系统调度和负载影响

资源管理: 必须正确删除定时器以避免资源泄漏

线程安全: 在多线程环境中使用时需要适当的同步

错误处理: 所有timer_*函数都可能失败,必须检查返回值

时钟选择: 不同时钟源适用于不同的应用场景

通过这些示例,你可以理解timer_*函数在定时控制方面的强大功能,它们为Linux应用程序提供了精确、灵活的时间管理能力。

https://www.calcguide.tech/2025/08/05/linux定时器管理timer/

getitimer系统调用及示例