## Linux C 编程核心函数详解
### 1. `fork` – 进程管理
#### 函数介绍及归类
* **类别:** 系统调用 (System Call)
* **头文件:** `#include
* **作用:** 创建一个当前进程的副本(子进程)。这是 Linux 下创建新进程的主要方式。
#### 函数原型
“`c
#include
pid_t fork(void);
“`
#### 功能
`fork()` 调用一次,返回两次。它会创建一个与调用进程(父进程)几乎完全相同的子进程。子进程会从 `fork()` 返回后的下一条指令开始执行。
#### 参数
无参数。
#### 返回值
* **在父进程中:** 返回新创建的子进程的进程 ID (PID),这是一个正整数。
* **在子进程中:** 返回 0。
* **失败时:** 在父进程中返回 -1,并设置全局变量 `errno` 来指示错误原因(如系统资源不足)。
#### 相似函数或关联函数
* `vfork()`: 类似于 `fork()`,但在子进程调用 `exec` 或 `_exit` 之前,父进程会被阻塞。通常用于紧接着调用 `exec` 的场景,效率可能稍高。
* `wait()`, `waitpid()`: 父进程用来等待子进程结束。
* `getpid()`: 获取当前进程的 PID。
* `getppid()`: 获取当前进程的父进程 PID。
#### 示例代码 (C89)
“`c
#include
#include
#include
#include
int main() {
pid_t pid;
int status;
/* 调用 fork 创建子进程 */
pid = fork();
if (pid < 0) {
/* fork 失败 */
perror("fork failed");
return 1; /* 返回非零值表示错误 */
} else if (pid == 0) {
/* 这段代码在子进程中执行 */
printf("Child Process:\n");
printf(" My PID is %d\n", (int)getpid());
printf(" My Parent's PID is %d\n", (int)getppid());
/* 子进程做一些事情 */
printf(" Child is exiting now.\n");
/* 子进程结束 */
/* 注意:使用 _exit 而不是 exit 可以避免刷新缓冲区等问题,尤其是在 fork 后 */
_exit(0);
} else {
/* 这段代码在父进程中执行 */
printf("Parent Process:\n");
printf(" My PID is %d\n", (int)getpid());
printf(" I just created a child with PID %d\n", (int)pid);
/* 父进程等待子进程结束 */
if (wait(&status) == -1) {
perror("wait failed");
return 1;
}
/* 检查子进程是否正常退出 */
if (WIFEXITED(status)) {
printf(" Child exited with status %d\n", WEXITSTATUS(status));
} else {
printf(" Child did not exit normally.\n");
}
printf(" Parent is exiting now.\n");
}
return 0; /* 程序正常退出 */
}
```
---
### 2. `vfork` - 进程管理
#### 函数介绍及归类
* **类别:** 系统调用 (System Call)
* **头文件:** `#include
* **作用:** 创建一个新进程,但与 `fork` 不同,它在子进程调用 `exec` 或 `_exit` 之前,共享父进程的地址空间,并且父进程会被阻塞。
#### 函数原型
“`c
#include
pid_t vfork(void);
“`
#### 功能
`vfork()` 也调用一次,返回两次。它创建一个新进程,但这个新进程(子进程)在调用 `exec` 系列函数或 `_exit` 之前,与父进程共享内存(包括栈、数据段等)。在此期间,父进程是被挂起的。这使得 `vfork` 在紧接着调用 `exec` 时非常高效,因为它避免了复制父进程的页表。
#### 参数
无参数。
#### 返回值
* **在父进程中:** 返回新创建的子进程的 PID。
* **在子进程中:** 返回 0。
* **失败时:** 在父进程中返回 -1,并设置 `errno`。
#### 相似函数或关联函数
* `fork()`: 更通用,子进程独立于父进程。
* `exec()` 系列函数 (`execl`, `execv`, `execle`, `execve`, `execlp`, `execvp`): 用新程序替换当前进程的镜像。
#### 示例代码 (C89)
“`c
#include
#include
#include
int main() {
pid_t pid;
int shared_var = 10; /* 父子进程共享的变量 */
printf(“Before vfork, shared_var = %d\n”, shared_var);
/* 调用 vfork 创建子进程 */
pid = vfork();
if (pid < 0) {
/* vfork 失败 */
perror("vfork failed");
return 1;
} else if (pid == 0) {
/* 这段代码在子进程中执行 */
/* 注意:在 vfork 后,子进程只能调用 exec 或 _exit */
/* 修改共享变量 */
shared_var = 20;
printf("Child modified shared_var to %d\n", shared_var);
printf("Child is about to exec.\n");
/* 紧接着调用 exec,这是 vfork 最常见的用法 */
/* execl("/bin/echo", "echo", "Hello from exec'd process!", (char *)NULL); */
/* 如果 exec 失败,必须使用 _exit 退出 */
/* execl("nonexistent_program", "dummy", (char *)NULL); */
/* perror("exec failed"); */
/* _exit(1); */ /* 子进程结束,父进程恢复 */
/* 为了演示,我们直接 _exit */
_exit(0); /* 子进程结束,父进程恢复 */
/* 下面的代码在 vfork 后是危险的,不应该执行 */
/* printf("This line should not be reached in child after vfork without exec/_exit\n"); */
/* return 0; */ /* 不要在 vfork 子进程中使用 return */
} else {
/* 这段代码在父进程中执行 */
/* 父进程在这里被阻塞,直到子进程调用 exec 或 _exit */
printf("Parent resumed after child called _exit.\n");
printf("After child _exit, shared_var = %d (might be modified by child)\n", shared_var);
printf("Parent is exiting now.\n");
}
return 0;
}
```
**注意:** `vfork` 的使用场景非常特定,主要是为了紧接着调用 `exec`。由于其共享内存的特性,使用不当容易导致问题,因此现代编程中 `fork` + `exec` 或 `posix_spawn` 更常用。
---
### 3. `system` - 进程管理/命令执行
#### 函数介绍及归类
* **类别:** 标准库函数 (Standard Library Function)
* **头文件:** `#include
* **作用:** 执行一个 shell 命令字符串。它内部通常通过调用 `fork`、`exec` 和 `wait` 来实现。
#### 函数原型
“`c
#include
int system(const char *command);
“`
#### 功能
`system()` 函数将 `command` 字符串传递给系统的命令处理器(通常是 shell,如 `/bin/sh`)来执行。它是一个方便的函数,可以让你在 C 程序中执行外部命令。
#### 参数
* `command`: 指向以空字符结尾的字符串,该字符串包含了要执行的命令及其参数。
#### 返回值
* **成功:** 返回命令执行后的退出状态。这个状态可以通过 `WIFEXITED`, `WEXITSTATUS` 等宏来解析(类似 `wait`)。
* **`command` 为 NULL:** 如果系统有命令处理器可用,则返回非零值;否则返回 0。这可以用来检查 `system` 是否可用。
* **失败或错误:** 返回 -1,并设置 `errno`(例如,无法创建子进程)。
* **命令本身执行失败:** 返回由命令处理器报告的状态(通常是命令的退出码)。
#### 相似函数或关联函数
* `fork()`, `exec()` 系列, `wait()`: `system` 内部就是用这些函数实现的,提供了更底层和灵活的控制。
* `popen()`, `pclose()`: 可以执行命令并获取其输出(通过管道)。
#### 示例代码 (C89)
“`c
#include
#include
int main() {
int result;
/* 检查 system 是否可用 */
if (system(NULL) == 0) {
printf(“Command processor is not available.\n”);
return 1;
}
printf(“Command processor is available.\n”);
/* 执行一个简单的命令 */
printf(“\n— Executing ‘echo Hello from system call’ —\n”);
result = system(“echo Hello from system call”);
if (result == -1) {
perror(“system call failed”);
return 1;
} else {
printf(“Command ‘echo …’ finished with status %d\n”, result);
/* 注意:直接的 result 值可能包含更多信息,需要用 wait 宏解析 */
/* 为简化,这里只打印原始值 */
}
/* 执行一个列出文件的命令 */
printf(“\n— Executing ‘ls -l’ —\n”);
result = system(“ls -l”);
if (result == -1) {
perror(“system call failed”);
} else {
printf(“Command ‘ls -l’ finished with status %d\n”, result);
}
/* 执行一个会失败的命令 */
printf(“\n— Executing ‘nonexistentcommand’ —\n”);
result = system(“nonexistentcommand”);
if (result == -1) {
perror(“system call failed (this might be due to fork/exec failure)”);
} else {
printf(“Command ‘nonexistentcommand’ finished with status %d\n”, result);
/* 这里的 status 通常表示命令未找到 */
}
printf(“\nMain program ending.\n”);
return 0;
}
“`
—
### 4. `strlen` – 字符串处理
#### 函数介绍及归类
* **类别:** 标准库函数 (Standard Library Function)
* **头文件:** `#include
* **作用:** 计算 C 风格字符串(以 `\0` 结尾)的长度。
#### 函数原型
“`c
#include
size_t strlen(const char *str);
“`
#### 功能
`strlen()` 函数遍历字符串 `str`,从第一个字符开始计数,直到遇到终止符 `\0`(不包括 `\0`),然后返回计数的字符数。
#### 参数
* `str`: 指向以 `\0` 结尾的字符串。
#### 返回值
返回字符串 `str` 中字符的个数(不包括结尾的 `\0` 字符)。返回类型 `size_t` 是一个无符号整数类型。
#### 相似函数或关联函数
* `sizeof`: 这是一个运算符,不是函数。对于字符数组,`sizeof` 返回整个数组的大小(包括 `\0`),而 `strlen` 返回实际内容的长度。
* `strnlen()`: (C99/C11) 类似 `strlen`,但会限制搜索长度,防止读取超出指定范围的内存。
#### 示例代码 (C89)
“`c
#include
#include
int main() {
char str1[] = “Hello”; /* 编译器自动添加 \0 */
char str2[20] = “World”; /* 数组大小为 20,但内容只有 6 个字符(包括 \0) */
const char *str3 = “This is a longer string.”;
char str4[] = {‘N’, ‘o’, ‘ ‘, ‘N’, ‘u’, ‘l’, ‘l’}; /* 危险!没有 \0 */
size_t len1, len2, len3;
len1 = strlen(str1);
len2 = strlen(str2);
len3 = strlen(str3);
printf(“String 1: \”%s\”\n”, str1);
printf(“Length of str1 (strlen): %u\n”, (unsigned int)len1);
printf(“Size of str1 array (sizeof): %u\n”, (unsigned int)sizeof(str1)); /* 包括 \0 */
printf(“\nString 2: \”%s\”\n”, str2);
printf(“Length of str2 (strlen): %u\n”, (unsigned int)len2);
printf(“Size of str2 array (sizeof): %u\n”, (unsigned int)sizeof(str2)); /* 包括 \0 */
printf(“\nString 3: \”%s\”\n”, str3);
printf(“Length of str3 (strlen): %u\n”, (unsigned int)len3);
printf(“\n— Important Note —\n”);
printf(“String 4 is not null-terminated. Calling strlen on it leads to undefined behavior.\n”);
printf(“It might crash or run forever. Uncommenting the next lines is dangerous.\n”);
/* 危险!不要对没有 \0 的字符串调用 strlen */
/* printf(“Length of str4 (UNDEFINED BEHAVIOR!): %u\n”, (unsigned int)strlen(str4)); */
return 0;
}
“`
—
### 5. `strchr` – 字符串处理
#### 函数介绍及归类
* **类别:** 标准库函数 (Standard Library Function)
* **头文件:** `#include
* **作用:** 在字符串中查找第一次出现的指定字符。
#### 函数原型
“`c
#include
char *strchr(const char *str, int c);
“`
#### 功能
`strchr()` 函数在字符串 `str` 中从左到右搜索字符 `c`(转换为 `char` 类型进行比较)。搜索包括终止符 `\0`。
#### 参数
* `str`: 指向要搜索的以 `\0` 结尾的字符串。
* `c`: 要搜索的字符,以 `int` 类型传递,但在比较时会转换为 `unsigned char`。
#### 返回值
* **找到:** 返回指向字符串 `str` 中第一次出现字符 `c` 的指针。
* **未找到:** 返回 `NULL`。
#### 相似函数或关联函数
* `strrchr()`: 查找字符在字符串中最后一次出现的位置。
* `strstr()`: 查找一个子字符串在另一个字符串中第一次出现的位置。
* `strpbrk()`: 查找字符串中第一个匹配指定字符集中任意字符的位置。
#### 示例代码 (C89)
“`c
#include
#include
int main() {
const char *str = “This is a sample string.”;
char target_char = ‘s’;
char *result_ptr;
printf(“String to search: \”%s\”\n”, str);
printf(“Character to find: ‘%c’\n”, target_char);
/* 查找第一次出现 ‘s’ 的位置 */
result_ptr = strchr(str, (int)target_char);
if (result_ptr != NULL) {
printf(“First occurrence of ‘%c’ found at position: %ld\n”,
target_char, (long)(result_ptr – str)); /* 计算偏移量 */
printf(“Substring from first ‘%c’: \”%s\”\n”, target_char, result_ptr);
/* 查找下一个 ‘s’ */
printf(“\n— Searching for next occurrence —\n”);
result_ptr = strchr(result_ptr + 1, (int)target_char); /* 从下一个位置开始搜索 */
if (result_ptr != NULL) {
printf(“Second occurrence of ‘%c’ found at position: %ld\n”,
target_char, (long)(result_ptr – str));
printf(“Substring from second ‘%c’: \”%s\”\n”, target_char, result_ptr);
} else {
printf(“No more occurrences of ‘%c’ found.\n”, target_char);
}
} else {
printf(“Character ‘%c’ not found in the string.\n”, target_char);
}
/* 查找字符串结尾符 ‘\0’ */
printf(“\n— Searching for null terminator —\n”);
result_ptr = strchr(str, ‘\0’);
if (result_ptr != NULL) {
printf(“Null terminator ‘\\0’ found at position: %ld\n”, (long)(result_ptr – str));
printf(“Pointer points to: ‘%c’ (ASCII value %d)\n”, *result_ptr, (int)*result_ptr);
}
/* 查找一个不存在的字符 */
printf(“\n— Searching for a character that doesn’t exist —\n”);
result_ptr = strchr(str, ‘z’);
if (result_ptr != NULL) {
printf(“Character ‘z’ found? This is unexpected.\n”);
} else {
printf(“Character ‘z’ not found, as expected. strchr returned NULL.\n”);
}
return 0;
}
“`
—