linux app(3)
基础编程规范
命令行参数处理
下面这行代码通常在程序的“帮助信息”或“用法提示”中出现,用于告诉用户如何正确运行程序。
1 | printf("Usage: %s <file>\n", argv[0]); |
参数占位符规范:
< >
表示必填参数(如<file>
需替换为实际文件路径)[ ]
表示可选参数(如[options]
)
示例执行流程:
1
2./program /path/to/file # 正确用法
./program # 错误:缺少必要参数
系统手册(man)
手册章节结构
man(manual 的缩写)是 Linux/Unix 系统中用于查看命令、函数、配置文件等手册页的核心工具。
章节 | 内容类型 | 示例命令 | 典型内容 |
---|---|---|---|
1 | 用户命令 | man 1 ls |
文件列表操作 |
2 | 系统调用 | man 2 fork |
进程创建机制 |
3 | 库函数 | man 3 printf |
格式化输出函数 |
4 | 设备文件 | man 4 tty |
终端设备接口 |
5 | 配置文件格式 | man 5 passwd |
用户账户文件结构 |
8 | 系统管理命令 | man 8 ifconfig |
网络接口配置 |
实用技巧
1 | man -k keyword # 搜索关键字相关手册 |
文件描述符(File Descriptor)
分配机制
- 规则:返回当前进程未使用的最小整数
- 特殊描述符:
0
:标准输入(STDIN_FILENO
)1
:标准输出(STDOUT_FILENO
)2
:标准错误(STDERR_FILENO
)
成功调用 open() 时,返回的文件描述符是当前进程 未使用的最小编号。最小编号分配策略:若当前打开的 fd 是 0、1、2、5,新分配的会是 3(因为 3 是未使用的最小整数)。
示例代码
1 |
|
设备控制(ioctl)
核心功能
Linux/Unix 系统中的一个设备控制函数,用于与设备驱动程序进行交互,执行无法通过标准文件操作(如 read/write)实现的特定操作。
1 | ioctl(fd, FBIOGET_VSCREENINFO, &fb_info); |
- 作用:与设备驱动交互的特殊操作
- 典型应用:
- 获取帧缓冲设备信息(分辨率/色深)
- 配置串口参数(波特率/数据位)
- 控制摄像头参数(曝光/对焦)
参数解析
参数 | 作用 |
---|---|
fd |
设备文件描述符 |
cmd |
设备特定命令(如FBIOGET_* ) |
arg |
指向参数结构的指针 |
内存映射(mmap)
mmap(Memory Map,内存映射)是 Unix/Linux 系统提供的一种 将文件或设备直接映射到进程内存空间 的系统调用。它允许程序通过读写内存的方式操作文件或硬件资源,避免了频繁的 read/write 调用,从而提升性能。
系统调用原型
1 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
典型应用场景
帧缓冲设备映射:
1
unsigned char *fb = mmap(NULL, screen_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd_fb, 0);
大文件高效IO:
1
2int fd = open("large_file.bin", O_RDONLY);
void *data = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0);
返回值:
成功:返回映射区域的起始地址(void * 类型,此处强制转换为 unsigned char *)。
失败:返回 MAP_FAILED(即 (void *) -1)。
参数详解
参数 | 可选值 | 说明 |
---|---|---|
prot |
PROT_READ /PROT_WRITE |
内存页保护标志 |
flags |
MAP_SHARED /MAP_PRIVATE |
映射修改的可见性 |
进程管理
fork() 工作机制
用于创建一个与父进程几乎完全相同的子进程。
1 | pid_t pid = fork(); |
返回值:
成功时:
父进程:返回子进程的 PID(正整数)。
子进程:返回 0。
失败时:返回 -1(比如系统资源不足)。
fork() 的执行过程
父进程调用 fork() 后,内核会创建一个几乎完全相同的子进程:
代码段、数据段、堆栈、打开的文件描述符等都会被复制(但实际采用 “写时复制” (Copy-On-Write, COW) 优化)。
子进程从 fork() 返回处继续执行(而不是从头开始)。
父进程和子进程并行运行,谁先执行由调度器决定。
1 |
|
僵尸进程处理
signal(SIGCHLD, SIG_IGN);
是一个 信号处理设置,用于告诉操作系统内核:当前进程对子进程的终止状态不感兴趣,内核应自动清理子进程的残留资源(避免僵尸进程)。
1 | signal(SIGCHLD, SIG_IGN); // 自动回收子进程 |
网络编程(Socket)
套接字的核心作用
套接字(Socket)是 应用层与传输层之间的抽象接口,主要作用包括:
建立网络连接
作为通信的端点,支持不同主机(或同一主机)上的进程间数据传输。
例如:客户端与服务端的 TCP 连接、UDP 数据报发送。
协议封装
封装底层协议细节(如 TCP/IP 或 UDP/IP),开发者无需直接处理网络包的分组、校验、重传等。
多协议支持
通过参数指定协议族(如 IPv4/IPv6)、传输类型(如面向连接的 TCP 或无连接的 UDP)。
步骤 | 服务端(接电话的人) | 客户端(打电话的人) | 对应函数 |
---|---|---|---|
1 | 准备通信设备(买一部手机) | 准备通信设备(买一部手机) | socket() |
2 | 绑定身份(插卡固定号码) | 无需绑定(系统自动分配端口) | bind() (仅服务端) |
3 | 等待连接(开机并等待来电) | 主动发起连接(拨打对方号码) | listen() + accept() (服务端)connect() (客户端) |
4 | 数据传输(接听电话后说话/听对方) | 数据传输(接通后说话/听对方) | send() / recv() |
5 | 挂断(挂断电话) | 挂断(挂断电话) | close() |
socket() 的底层工作
(1) 资源分配
创建套接字描述符
操作系统内核分配一个 文件描述符(类似打开文件时的 fd),用于唯一标识这个套接字。
例如:调用 int sockfd = socket(AF_INET, SOCK_STREAM, 0); 返回的 sockfd 可能是 3(假设当前进程已占用 0,1,2)。
内核数据结构初始化
内核为该套接字创建关键数据结构(不同系统实现不同,例如 Linux 的 struct socket):
1 | struct socket { |
同时初始化关联的 发送缓冲区 和 接收缓冲区(用于临时存储待发送/接收的数据)。
(2) 协议栈绑定
确定通信协议
根据参数(如 SOCK_STREAM)绑定到 TCP/IP 协议栈,后续所有操作(如 connect()、send())均通过协议栈处理。
如果是 UDP(SOCK_DGRAM),则绑定到无连接的协议栈。
(3) 设置默认属性
套接字初始化为 主动模式(可调用 connect() 发起连接,适用于客户端)。
默认是 阻塞模式(调用 recv() 等函数时,若数据未就绪,进程会休眠等待)。
线程编程
1 | /tmp/ccQzThAV.o: In function `main': Pthread_Text3.c:(.text+0xa3): undefined reference to `pthread_create' |
在Linux/Unix系统下进行多线程编程时,编译时需要显式链接-lpthread
(即POSIX Threads库)
1 | gcc -o thread_07 ./Pthread_Text7.c -lpthread |
相关函数
pthread_join() :
POSIX 线程(Pthreads)库中的一个核心函数,用于阻塞当前线程,等待指定的目标线程终止,并回收其资源。
pthread_cancel():
POSIX 线程(Pthreads)库中的一个函数,用于请求取消(终止)指定的线程。它提供了一种协作式线程终止机制,允许一个线程向另一个线程发送取消请求,但实际终止的时机取决于目标线程的取消状态和类型。
核心作用
发送取消请求:向目标线程发送终止请求,但不会立即强制终止线程。
协作式终止:目标线程是否响应取消请求,取决于其取消状态(是否允许被取消)和取消类型(延迟或异步取消)。
资源清理:若线程响应取消请求,会执行清理函数(通过 pthread_cleanup_push 注册)后再终止。
pthread_mutex_t mutex
作用:声明一个互斥锁(mutex)变量,类型为 pthread_mutex_t。
pthread_mutex_lock(&mutex);
作用
尝试对互斥锁加锁:
如果锁未被其他线程持有,则当前线程成功获取锁,继续执行。
如果锁已被其他线程持有,则当前线程阻塞,直到锁被释放。
pthread_mutex_unlock(&mutex);
作用
释放已持有的互斥锁,允许其他线程获取该锁。
ret = pthread_mutex_init(&mutex,NULL);
作用
主函数中动态初始化互斥锁,第二个参数为 NULL 时使用默认属性。
pthread_mutex_destroy(&mutex);
作用
主函数中销毁互斥锁,释放相关资源。
信号量(Semaphore)是线程/进程同步的重要工具,用于控制对共享资源的访问或实现线程间的协作。
sem_init()
初始化信号量
1 | int sem_init(sem_t *sem, int pshared, unsigned int value); |
功能:初始化一个无名信号量。
sem_wait()
1 | int sem_wait(sem_t *sem); |
功能:
如果信号量值 > 0,则减 1 并立即返回。
如果信号量值 = 0,则线程阻塞,直到信号量值变为正数。
返回值:成功返回 0,失败返回 -1(如被信号中断)。
sem_post()
释放(V操作)
1 | int sem_post(sem_t *sem); |
功能:将信号量值加 1,并唤醒等待该信号量的线程。
返回值:成功返回 0,失败返回 -1。
sem_trywait()
非阻塞等待
sem_destroy()
销毁信号量
/dev/tty*和其他
在 Linux 系统中,/dev/tty* 是一系列与终端(Terminal)相关的设备文件,它们代表了不同的终端类型和功能。以下是详细的分类和解释:
设备文件 | 类型 | 主要用途 |
---|---|---|
/dev/ttyS0 |
物理串行终端 | 连接硬件设备(如调制解调器) |
/dev/tty1~tty6 |
虚拟终端 | 本地多用户文本界面(Ctrl+Alt+F1~F6切换) |
/dev/pts/0 |
伪终端 | 图形终端或远程SSH会话 |
/dev/tty |
当前终端 | 进程获取自己的控制终端 |
/dev/console |
系统控制台 | 内核输出和紧急消息 |
/dev/console 是 Linux 系统中的 系统控制台设备,用于内核和系统级消息的输出,同时也是系统启动过程中的主要交互终端。
行规程(Line Discipline)
Linux TTY(终端)子系统的核心组件之一,负责管理终端设备的数据流,包括:
输入/输出处理(缓冲、回显、转换)
特殊字符解释(如 Ctrl+C 终止进程)
协议封装(如串口通信的 SLIP 或 PPP)
行规程的作用
行规程位于 TTY 驱动层 和 用户进程 之间,对数据流进行中间处理:
用户进程 (e.g., Bash) ←→ 行规程 (Line Discipline) ←→ TTY 驱动 (UART/PTY) ←→ 硬件/网络
主要功能包括:
功能 | 说明 |
---|---|
行缓冲 | 将用户输入按行缓冲(直到按下 Enter )。 |
回显(Echo) | 将用户输入的字符显示到终端(可关闭,如密码输入)。 |
特殊字符处理 | 解释 Ctrl+C (中断)、Ctrl+Z (挂起)、Backspace (删除)等。 |
字符转换 | 如将 \r (回车)转换为 \n (换行),或处理 Unicode 编码。 |
流量控制 | 支持 XON/XOFF (软件流控)或硬件流控(RTS/CTS )。 |
协议封装 | 将原始字节流封装为 SLIP/PPP 等协议(用于串口拨号或嵌入式通信)。 |
SMBus(System Management Bus)详解
SMBus(System Management Bus)是由 Intel 在 1995 年定义的一种 两线制串行总线,主要用于 低速率系统管理通信,如:
电源管理(PMBus)
传感器监控(温度、电压、风扇)
智能电池管理
嵌入式设备配置
SMBus 基于 I²C(Inter-Integrated Circuit)协议,但增加了额外的规范和限制,使其更适合 系统管理 任务。
特性 | SMBus | I²C |
---|---|---|
时钟速度 | 10 kHz ~ 100 kHz(标准) | 标准模式:100 kHz,最高 5 MHz |
电压电平 | 3.3V(兼容 5V) | 1.8V ~ 5V(取决于设备) |
超时机制 | 强制超时(35ms 时钟超时) | 无强制超时 |
ACK/NACK 规则 | 更严格(某些情况必须 ACK) | 较灵活 |
协议扩展 | 支持 Packet Error Checking (PEC) | 无内置校验机制 |
用途 | 系统管理(电源、传感器、电池) | 通用外设通信(EEPROM、LCD) |
volatile 使用场景
C/C++ 中的一个关键字,用于告诉编译器不要优化对该变量的访问,确保每次读写都直接操作内存(而非寄存器缓存)。它在嵌入式系统和多线程编程中尤为重要。
1 | volatile int sensor_value; // 防止编译器优化 |
- 适用情况:
- 内存映射硬件寄存器
- 多线程共享变量
- 信号处理程序修改变量