文件编程
内容概览
包含文件系统原理及访问机制、文件在内核中的管理机制、文件信息节点 inode、文件的共享、文件权限(各种用户对其权限)等内容。
应用场景
- 实际应用:账单、游戏进度、配置文件等。
- 核心需求:通过代码操作文件,实现文件创建、打开、编辑等自动化执行。
文件操作对比
- Windows 手动修改文件:如编写 Word 文档的流程。
- Linux 操作流程:打开 / 创建文档→编辑文档→保存文档→关闭文档。
Linux 文件操作 API
操作系统提供一系列 API 实现自动化操作,包括:
- 打开:
open - 读写:
write/read - 光标定位:
lseek - 关闭:
close
打开 / 创建文件

-
函数原型:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode); int creat(const char *pathname, mode_t mode); -
描述:
open()通过路径名打开文件,返回文件描述符(非负整数),用于后续系统调用(如read、write等)。成功返回的文件描述符是进程中当前未使用的最小非负整数。 -
参数说明:
pathname:要打开的文件名(含路径,缺省为当前路径)。flags:- 必选权限(仅能指定一个):
O_RDONLY(只读)、O_WRONLY(只写)、O_RDWR(可读可写)。 - 可选参数:
O_CREAT:文件不存在则创建,需同时指定mode。O_EXCL:与O_CREAT同时使用时,若文件已存在则出错。O_APPEND:每次写操作追加到文件尾端。O_TRUNC:若文件有内容且以只读 / 只写成功打开,将长度截短为 0。
- 必选权限(仅能指定一个):
mode:仅当flags含O_CREAT时有效,指定新文件的访问权限。
-
创建文件
creat函数:运行
int creat (const char *filename, mode_t mode)filename:要创建的文件名(含路径,缺省为当前路径)。mode:创建模式(宏表示与对应数字):- 可读:
S_IRUSR(4) - 可写:
S_IWUSR(2) - 可执行:
S_IXUSR(1) - 可读、写、执行:
S_IRWXU(7)
- 可读:
写入文件
- 函数原型:
运行
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); - 描述:从缓冲区
buf向文件描述符fd对应的文件写入最多count字节。 - 返回值:成功返回写入的字节数(0 表示未写入);失败返回 - 1,
errno被设置。- 写入字节可能少于
count(如存储空间不足、被信号中断等)。 - 对可定位文件,写入从当前偏移量开始,偏移量随写入字节数增加;若以
O_APPEND打开,写入前偏移量被设为文件尾,偏移量调整和写入为原子操作。
- 写入字节可能少于
读取文件
- 函数原型:
#include <unistd.h> ssize_t read(int fd, void *buf, size_t count); - 描述:从文件描述符
fd对应的文件读取最多count字节到缓冲区buf。 - 返回值:成功返回读取的字节数(0 表示文件结束),文件位置随字节数推进;失败返回 - 1,
errno被设置。- 读取字节可能少于
count(如接近文件尾、从管道 / 终端读取、被信号中断等)。
- 读取字节可能少于
文件 “光标” 位置(lseek)
- 函数原型:
#include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t offset, int whence); - 描述:重新定位文件读写偏移量,根据
whence调整:SEEK_SET:偏移量设为offset字节。SEEK_CUR:偏移量设为当前位置加offset字节。SEEK_END:偏移量设为文件大小加offset字节。
关闭文件(close)
- 函数原型:
#include <unistd.h> int close(int fd); - 描述:关闭文件描述符
fd,使其不再指向任何文件并可重用。释放关联资源,若为最后一个引用且文件已被unlink,则删除文件。 - 返回值:成功返回 0;失败返回 - 1,
errno被设置。
文件描述符
- 内核通过文件描述符(非负整数)引用所有打开文件。打开 / 创建文件时,内核向进程返回文件描述符,用于标识文件并传递给
read、write等函数。 - 惯例:文件描述符 0(标准输入,
STDIN_FILENO)、1(标准输出,STDOUT_FILENO)、2(标准错误输出,STDERR_FILENO)。 - 进程中,文件描述符与内核维护的动态文件数据结构绑定,作用域仅限当前进程。
文件编程一般步骤
- 打开 / 创建文件:获取文件描述符。
- 读取 / 写入文件:操作内存中的动态文件。
- 关闭文件:同步动态文件到块设备的静态文件,释放资源。
- 原理:
- 静态文件:存于块设备的文件系统中。
- 动态文件:打开文件时,内核在内存中创建的数据结构,映射静态文件内容,读写操作基于动态文件。
- 设计原因:内存操作(字节单位、随机访问)比块设备操作(按块读写)更灵活。
Linux 文件管理简述
(对应图片内容未明确,暂空)
文件编程练手
- 实现 Linux 的
cp命令代码。 - 配置文件修改:如修改包含
SPEED=5、LENG=100、SCORE=90、LEVEL=95的配置文件。
其他问题
- 如何用文件记录一个结构体?(未提供具体内容)
- 函数对比:
fopen、fread、fwrite、fseek、fclose、fgetc、fputc、feof与前述 API 的区别(参考链接:总结open与fopen的区别 - NickyYe - 博客园)。
扩展概念
Linux 中 “一切皆文件”,包括文件系统(文件夹 / 文件)、硬件设备、管道、数据库、Socket 等。
进程
进程关键概念
- 什么是程序,什么是进程,有什么区别?
- 如何查看系统中有哪些进程?
- 什么是进程标识符?
- 什么叫父进程,什么叫子进程?
- C 程序的存储空间是如何分配?
问题与解答
-
什么是程序,什么是进程,有什么区别?
- 程序:静态的概念,如通过
gcc xxx.c –o pro在磁盘中生成的pro文件。 - 进程:程序的一次运行活动,即程序运行起来后,系统中新增的活动实体。
- 程序:静态的概念,如通过
-
如何查看系统中有哪些进程?
- 使用
ps指令,实际工作中常配合grep查找特定进程。 - 使用
top指令,类似 Windows 任务管理器。
- 使用
-
什么是进程标识符?
- 每个进程都有一个非负整数表示的唯一 ID,称为
pid(类似身份证)。 - 特殊进程 ID:
pid=0:交换进程(swapper),作用是进程调度。pid=1:init进程,作用是系统初始化。
- 编程中可通过
getpid函数获取自身进程标识符,getppid获取父进程标识符。
- 每个进程都有一个非负整数表示的唯一 ID,称为
-
什么叫父进程,什么叫子进程?
- 若进程 A 创建了进程 B,则 A 称为父进程,B 称为子进程。
- 父子进程是相对概念,类似人类的父子关系。
-
C 程序的存储空间是如何分配?
- 从高地址到低地址依次为:命令行参数和环境变量、栈、堆、未初始化的数据(bss 段)、初始化的数据、正文段。
- 各段说明:
- 正文段:CPU 执行的机器指令部分,可共享且通常只读。
- 初始化数据段:包含程序中需明确赋初值的变量,如
int maxcount =99;。 - 非初始化数据段(bss 段):程序开始执行前,内核将此段数据初始化为 0 或空指针,如
long sum[1000];。 - 栈:存放自动变量及函数调用时需保存的信息(如返回地址、寄存器值),支持函数递归调用。
- 堆:用于动态存储分配,位于非初始化数据段和栈之间。
进程创建实战
-
使用
fork函数创建进程- 函数原型:
pid_t fork(void); - 返回值:
- 调用成功:返回两次,子进程中返回 0,父进程中返回子进程 ID。
- 调用失败:返回 - 1。
- 函数原型:
-
fork创建子进程的一般目的- 父进程复制自己,使父子进程同时执行不同代码段(如网络服务中,父进程等待请求,子进程处理请求)。
- 进程需执行不同程序(如 shell 中,子进程
fork后立即调用exec)。
-
fork编程说明fork创建的子进程是父进程的副本,子进程获得父进程数据空间、堆和栈的副本(现代实现采用写时复制技术,避免完全复制)。- 父子进程共享正文段。
-
vfork函数与fork的区别- 关键区别一:
vfork直接使用父进程存储空间,不进行拷贝。 - 关键区别二:
vfork保证子进程先运行,子进程调用exit退出后,父进程才执行。
- 关键区别一:
进程退出
-
正常退出
main函数调用return。- 进程调用
exit()(标准 C 库函数)。 - 进程调用
_exit()或_Exit()(系统调用)。 - 补充:最后一个线程返回;最后一个线程调用
pthread_exit。
-
异常退出
- 调用
abort。 - 进程收到某些信号(如
ctrl+C)。 - 最后一个线程对取消请求做出响应。
- 调用
等待子进程
-
等待原因
- 父进程需等待子进程退出并收集其退出状态,否则子进程退出状态不被收集,会变成僵死进程(僵尸进程)。
-
相关函数
wait和waitpid:- 函数原型:
c
运行
#include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); pid_t waitpid(pid_t pid, int *status, int options); - 区别:
wait使调用者阻塞;waitpid可通过选项设置不阻塞。 status参数:非空时,子进程退出状态存于其指向的地址;为空时,不关心退出状态。waitpid的pid参数作用:pid==-1:等待任一子进程(与wait等效)。pid>0:等待进程 ID 与pid相等的子进程。pid==0:等待组 ID 等于调用进程组 ID 的任一子进程。pid<-1:等待组 ID 等于pid绝对值的任一子进程。
- 函数原型:
-
检查终止状态的宏(表 8-1)
宏 说明 WIFEXITED(status)若为正常终止子进程的状态,则为真;可通过 WEXITSTATUS(status)取子进程退出参数的低 8 位WIFSIGNALED(status)若为异常终止子进程的状态(接到不捕捉的信号),则为真;可通过 WTERMSIG(status)取终止信号编号WIFSTOPPED(status)若为当前暂停子进程的状态,则为真;可通过 WSTOPSIG(status)取暂停信号编号WIFCONTINUED(status)若为作业控制暂停后继续的子进程状态,则为真(仅用于 waitpid)
孤儿进程
- 父进程在子进程之前结束,子进程称为孤儿进程。
- Linux 中,
init进程会收留孤儿进程,成为其新的父进程。
exec族函数
-
函数特点
- 执行成功后不会返回;调用失败时,设置
errno并返回 - 1,从原程序调用点继续执行。 - 参数说明:
path:可执行文件的路径名。arg:可执行程序所带参数,第一个参数为可执行文件名(无路径时需以NULL结束)。file:若包含/则视为路径名,否则按PATH环境变量搜寻可执行文件。
- 函数名含义辅助记忆:
l:使用参数列表。p:使用文件名,从PATH环境变量寻找可执行文件。v:需构造指向各参数的指针数组,将数组地址作为参数。e:多envp数组,使用新环境变量代替调用进程的环境变量。
- 执行成功后不会返回;调用失败时,设置
-
作用
- 让进程执行不同的程序(如 shell 中,子进程
fork后立即调用exec)。
- 让进程执行不同的程序(如 shell 中,子进程
-
与
fork配合使用- 例如:父进程检测到输入为 1 时,创建子进程并通过
exec修改配置文件的字段值。
- 例如:父进程检测到输入为 1 时,创建子进程并通过
system函数
- 功能:执行 shell 命令。
- 函数原型:
#include <stdlib.h> int system(const char *command); - 返回值:
- 成功:返回进程的状态值。
- 当
sh不能执行时:返回 127。 - 失败:返回 - 1。
- 参考链接:linux system函数详解 - 南哥的天下 - 博客园
popen函数
- 功能:创建管道流,实现与进程的输入输出交互。
- 函数原型:
c
运行
#include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream); - 比
system的优势:可获取命令运行的输出结果。 - 参考链接:linux下popen的使用心得_linux c++ popen 命令执行结束-优快云博客
其他参考链接
网络编程
TCP/UDP 对比
- 连接性:TCP 面向连接(如打电话需先拨号);UDP 无连接,发送数据前无需建立连接。
- 可靠性:TCP 提供可靠服务(数据无差错、不丢失、不重复、按序到达);UDP 尽最大努力交付,不保证可靠交付。
- 数据处理:TCP 面向字节流(将数据视为无结构字节流);UDP 面向报文。UDP 无拥塞控制,网络拥塞时不降低发送速率(适用于实时应用如 IP 电话、视频会议)。
- 连接方式:每条 TCP 连接仅点到点;UDP 支持一对一、一对多、多对一、多对多交互通信。
- 首部开销:TCP 首部开销 20 字节;UDP 首部开销仅 8 字节。
- 通信信道:TCP 为全双工可靠信道;UDP 为不可靠信道。
端口号作用
- 一台主机可通过 IP 地址提供多种服务(如 Web、FTP、SMTP 等),仅靠 IP 地址无法区分,需通过 “IP 地址 + 端口号” 识别。
- 端口是访问通道,服务器通过知名端口号被识别,例如:
- FTP 服务器的 TCP 端口号为 21。
- Telnet 服务器的 TCP 端口号为 23。
- TFTP 服务器的 UDP 端口号为 69。
字节序
-
定义:
- 小端字节序(Little endian):数据低位字节存于内存低地址,高位字节存于高地址(如 0x01020304 在内存中存储为 04 03 02 01)。
- 大端字节序(Big endian):数据高位字节存于内存低地址,低位字节存于高地址(如 0x01020304 在内存中存储为 01 02 03 04)。
- 网络字节序:采用大端字节序。
- 注:x86 系列 CPU 使用小端字节序。
-
字节序转换 API:
c
运行
#include <netinet/in.h> uint16_t htons(uint16_t host16bitvalue); // 主机16位字节序转网络字节序 uint32_t htonl(uint32_t host32bitvalue); // 主机32位字节序转网络字节序 uint16_t ntohs(uint16_t net16bitvalue); // 网络16位字节序转主机字节序 uint32_t ntohl(uint32_t net32bitvalue); // 网络32位字节序转主机字节序- 缩写说明:
h(host,主机)、n(net,网络)、s(short,2 字节)、l(long,4 字节)。 - 可使用
INADDR_ANY让操作系统自动获取地址。
- 缩写说明:
服务器与客户端交互基础
- 服务器:指定协议(TCP/UDP)、IP 地址(楼号)、端口号(房间号),并监听连接请求。
- 客户端:获取服务器 IP 和端口号,发起连接。
Socket 服务器和客户端开发步骤
- 创建套接字。
- 为套接字绑定信息(IP 地址和端口号)。
- 服务器监听网络连接。
- 服务器接受客户端连接。
- 数据交互。
- 关闭套接字,断开连接。
关键函数与结构
-
创建套接字(
socket函数):c
运行
int socket(int domain, int type, int protocol);- 参数:
domain:协议族(如AF_INET为 IPv4,AF_INET6为 IPv6,AF_UNIX为 Unix 域等)。type:套接字类型(SOCK_STREAM为 TCP 流式套接字,SOCK_DGRAM为 UDP 数据报套接字,SOCK_RAW为原始套接字)。protocol:协议(通常为 0,选择type对应的默认协议,如IPPROTO_TCP为 TCP,IPPROTO_UDP为 UDP)。
- 参数:
-
地址结构:
- 通用结构(
struct sockaddr):c
运行
struct sockaddr { unsigned short sa_family; // 协议族 char sa_data[14]; // IP+端口 }; - IPv4 专用结构(
struct sockaddr_in):c
运行
struct sockaddr_in { sa_family_t sin_family; // 协议族 in_port_t sin_port; // 端口号(网络字节序) struct in_addr sin_addr; // IP地址结构体 unsigned char sin_zero[8]; // 填充,与`sockaddr`对齐 };
- 通用结构(
-
地址转换 API:
int inet_aton(const char* straddr, struct in_addr *addrp);:将字符串 IP(如 “192.168.1.123”)转为网络格式。char* inet_ntoa(struct in_addr inaddr);:将网络格式 IP 转为字符串形式。
-
监听连接(
listen函数):c
运行
#include <sys/types.h> #include <sys/socket.h> int listen(int sockfd, int backlog);- 功能:设置服务器套接字为监听模式,规定最大连接队列长度。
- 参数:
sockfd:服务器套接字描述符。backlog:请求队列允许的最大请求数(内核维护未完成连接队列和已完成连接队列)。
-
接受连接(
accept函数,服务器端):- 用于从已完成连接队列中接收客户端连接,返回新的套接字描述符用于数据交互。
-
发起连接(
connect函数,客户端):c
运行
#include <sys/types.h> #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);- 功能:客户端与服务器建立连接。
- 参数:
sockfd:客户端套接字描述符。addr:服务器地址结构指针。addrlen:地址长度(sizeof(struct sockaddr))。
- 返回值:成功返回 0,失败返回 - 1。
-
数据收发函数:
- 基础函数(
read/write):c
运行
ssize_t write(int fd, const void* buf, size_t nbytes); // 发送数据 ssize_t read(int fd, void *buf, size_t nbyte); // 接收数据- 返回实际读写的字节数,出错返回 - 1。
- TCP 专用(
send/recv):c
运行
ssize_t send(int s, const void *msg, size_t len, int flags); // 发送数据 ssize_t recv(int s, void *buf, size_t len, int flags); // 接收数据- 仅用于已连接的套接字,
flags一般为 0。
- 仅用于已连接的套接字,
- 其他函数:
readv/writev、recvmsg/sendmsg、recvfrom/sendto等。
- 基础函数(
总结
网络编程核心是通过 Socket 接口实现 TCP/UDP 通信,需关注字节序转换、地址绑定、连接管理及数据收发等环节,根据应用场景选择合适的协议(TCP 适用于可靠传输,UDP 适用于实时性要求高的场景)。
3万+

被折叠的 条评论
为什么被折叠?



