Linux系统编程学习笔记
Linux系统编程是指在Linux操作系统上进行程序开发的一种形式。它涵盖了多个方面,包括文件操作、进程管理、内存管理、网络编程等。 Linux系统编程涵盖了众多领域,从基础的文件操作到高级的网络通信和多线程编程,为开发者提供了广泛的功能和灵活性。在这个领域工作的开发者需要深入了解Linux内核和系统底层的工作原理。 Linux 系统编程是指在 Linux 操作系统上进行应用程序和工具开发的一系列活动。这种编程涉及到与 Linux 操作系统核心交互,以实现各种功能,如文件操作、进程管理、网络通信等。Linux 系统编程通常需要直接调用系统调用,使用 C 或 C++ 等低级编程语言进行开发。 ### Linux系统编程学习笔记 #### 一、IO **1.1 标准I/O (stdio)** - **fopen/fclose**: `fopen` 用于打开或创建一个文件,并返回一个指向该文件的 `FILE *` 类型的指针。`fclose` 用于关闭由 `FILE *` 指向的文件。这两个函数是标准 I/O 的一部分,它们通过 `stdio.h` 头文件定义。 ```c FILE *fopen(const char *path, const char *mode); int fclose(FILE *fp); ``` - **fgetc/fputc/fgets/fputs**: 这些函数分别用于从文件读取一个字符、向文件写入一个字符、从文件读取一行文本以及向文件写入一行文本。 - **fread/fwrite**: 用于从文件中读取或写入指定数量的数据块。这些函数可以更高效地处理大量数据。 ```c size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); ``` - **printf/scanf/atoi/sprintf**: `printf` 和 `scanf` 用于格式化输入和输出,`atoi` 用于将字符串转换为整数,而 `sprintf` 则用于格式化字符串输出到缓冲区。 - **fseek/ftell/rewind/fflush**: 这些函数用于控制文件的位置指示器。例如,`fseek` 用于设置文件位置指示器,`ftell` 返回当前文件位置指示器的位置,`rewind` 用于将文件位置指示器重置到文件开头,`fflush` 则用于刷新输出缓冲区。 - **getline/mygetline**: `getline` 用于读取一行文本,直到遇到换行符或文件结束符。`mygetline` 是自定义函数,可能根据具体需求进行了优化或修改。 - **临时文件tmpfile**: 用于创建一个只能被当前进程访问的临时文件。 **2.2 文件描述符 (sysio)** - **文件描述符实现原理**: 文件描述符是操作系统用来标识打开文件的整数。每个打开的文件都会有一个文件描述符,通常从0开始递增。0、1、2分别代表标准输入、标准输出和标准错误输出。 - **open/close/creat**: `open` 用于打开现有文件或创建新文件。`close` 用于关闭已打开的文件描述符。`creat` 用于创建新文件并打开它。 ```c int open(const char *pathname, int flags); int close(int fd); int creat(const char *pathname, mode_t mode); ``` - **read/write/lseek**: `read` 从文件描述符读取数据,`write` 向文件描述符写入数据,`lseek` 用于改变文件描述符的位置指示器。 ```c ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); off_t lseek(int fd, off_t offset, int whence); ``` - **IO区别fileno/fdopen**: `fileno` 用于获取 `FILE *` 对象对应的文件描述符,而 `fdopen` 则用于将文件描述符转换为 `FILE *` 对象,从而使得系统 I/O 的文件描述符可以用标准 I/O 的函数来操作。 ```c int fileno(FILE *stream); FILE *fdopen(int fd, const char *mode); ``` - **IO效率和文件共享**: 不同类型的 I/O 在性能和资源消耗上有所不同。标准 I/O 通常有额外的缓冲机制,而系统 I/O 则更加接近底层硬件,因此效率更高。同时,文件描述符可以被多个进程共享,从而实现文件级别的资源共享。 - **原子操作dup/dup2**: `dup` 和 `dup2` 用于复制文件描述符。`dup` 将指定的文件描述符复制给最小的可用文件描述符,而 `dup2` 则会覆盖目标文件描述符。 ```c int dup(int oldfd); int dup2(int oldfd, int newfd); ``` **3.3 高级I/O (advio)** - **有限状态机编程**: 使用有限状态机模型可以更清晰地管理和控制 I/O 操作的状态转换。 - **IO多路转接select/poll**: `select` 和 `poll` 用于监听多个文件描述符的 I/O 事件。它们允许程序同时等待多个文件描述符的 I/O 事件发生,而不是阻塞在一个单一的文件描述符上。 ```c int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); int poll(struct pollfd *fds, nfds_t nfds, int timeout); ``` - **readv/writev**: 这两个函数允许非连续的内存区域作为缓冲区进行读写操作,从而提高了 I/O 效率。 ```c ssize_t readv(int fd, const struct iovec *iov, int iovcnt); ssize_t writev(int fd, const struct iovec *iov, int iovcnt); ``` - **内存映射mmap**: `mmap` 允许文件或其他对象直接映射到进程的地址空间。这样可以直接对文件进行读写操作,而不必显式地调用 `read` 或 `write` 函数。 ```c void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); ``` - **文件锁**: 文件锁机制用于确保多个进程不会同时修改同一文件。常用的文件锁函数包括 `fcntl` 和 `flock`。 ```c int fcntl(int fd, int cmd, ...); int flock(int fd, int operation); ``` #### 二、文件系统 - **目录和文件**: 包括文件属性的查询 (`stat`)、文件类型和权限的设置 (`chmod`)、文件系统的类型 (`FAT`, `UFS`) 以及目录操作 (`mkdir`, `rmdir`, `opendir`, `readdir`, `closedir`)。 - **系统数据文件和信息**: 如口令文件 (`passwd`)、组文件 (`group`) 和加密文件 (`shadow`) 等。 - **进程环境**: 包括进程的终止方式、钩子函数 (`atexit`)、命令行参数的处理 (`getopt`)、环境变量 (`environ`) 以及程序空间和手动装载库的管理等。 #### 三、进程基础 - **进程标识符pid**: 每个进程都有一个唯一的进程标识符 (PID)。 - **父子进程的产生fork**: `fork` 创建一个新的进程,这个新进程称为子进程,而调用 `fork` 的进程称为父进程。 ```c pid_t fork(void); ``` - **释放资源waitpid/交叉分配**: `waitpid` 用于等待子进程结束,并回收资源。`waitpid` 支持等待特定的子进程或者等待任意子进程结束。 ```c pid_t waitpid(pid_t pid, int *wstatus, int options); ``` - **exec函数族/myshell**: `exec` 函数族用于替换当前进程的程序映像。`myshell` 可能是一个简单的 shell 实现,用于执行命令。 - **用户和组权限/解释器文件**: 用户和组权限控制着文件的访问权限,解释器文件则决定了脚本的解释方式。 - **system/进程会计和时间**: `system` 函数可以执行外部命令,进程会计则是跟踪进程资源使用情况的机制。 - **守护进程**: 守护进程是一种特殊类型的进程,它在后台运行并且与终端脱离关联。 - **系统日志**: 系统日志用于记录系统活动的信息,对于调试和监控非常重要。 #### 四、并发(信号和线程) - **信号**: 信号是在进程之间传递的软件中断通知。信号可以被发送给进程、线程或由硬件异常触发。 - **信号/signal**: `signal` 函数用于设置信号处理函数。 ```c void (*signal(int signum, void (*handler)(int)))(int); ``` - **可重入/信号的响应过程**: 可重入函数可以在多线程或多进程环境中安全使用,而信号处理函数必须是可重入的。 - **函数 kill/raise/alarm/pause**: `kill` 用于发送信号给进程或线程,`raise` 用于向调用进程发送信号,`alarm` 设置一个定时器,`pause` 则使进程暂停直到接收到信号。 - **漏桶/令牌桶和令牌桶封装**: 这些是流量控制算法,用于限制数据传输速率。 - **多任务计时/setitimer**: `setitimer` 设置不同类型的定时器。 - **信号集/屏蔽字/pending**: 信号集用于管理一组信号,而屏蔽字则用于阻止信号的传递。 - **sigsuspend/sigaction**: `sigsuspend` 用于挂起进程直到接收到信号,`sigaction` 用于设置信号处理行为。 - **实时信号和信号总结**: 实时信号提供了一种更精细的控制机制,可以用于处理高优先级的任务。 - **线程** - **线程的概念标识与创建**: 线程是比进程更小的执行单元,每个进程至少有一个线程。`pthread_create` 用于创建新的线程。 - **线程终止和栈清理**: 当一个线程完成工作或发生错误时,需要适当地终止并清理资源。 - **线程的取消与竞争故障**: 线程可以被取消,但需要注意取消的顺序以及可能的竞争条件。 - **互斥量/条件变量/信号量**: 互斥量用于保护共享资源,条件变量用于线程间的同步,而信号量则用于管理资源的访问。 - **线程/互斥量/条件变量属性/clone**: 这些是线程同步和属性设置的相关函数。 - **线程重入和openmp标准**: 线程重入函数可以在多线程环境中安全调用,OpenMP 是一个多线程编程的标准。 #### 五、进程间通信 - **管道**: 管道是一种进程间通信方式,允许一个进程的输出作为另一个进程的输入。 - **消息队列**: 消息队列是一种用于在进程间传递消息的机制。 - **信号量数组**: 信号量数组可以用于控制多个资源的访问。 - **共享内存**: 共享内存是一种高效的进程间通信方法,允许多个进程共享相同的内存区域。 - **网络编程socket**: Socket API 提供了在不同主机之间建立连接的方式,支持 TCP 和 UDP 协议。 #### 六、结尾 Linux系统编程是一个广泛的领域,涵盖了从文件操作到网络编程等多个方面。深入理解这些概念和技术,对于开发高质量的Linux应用程序至关重要。随着技术的发展,不断出现的新技术和工具也使得Linux系统编程变得更加灵活和强大。