沈阳企业网站优化排名方案,中国万网创始人张向东,折腾wordpress,长沙好的设计公司文章目录 第九周预习报告学习内容AI 对学习内容的总结章节概述内容总结 对知识总结的反思思维导图基于AI的学习知识点1#xff1a;系统调用知识点2#xff1a;进程控制 学习实践过程遇到的问题与解决方式问题1#xff1a;fork() 调用后#xff0c;子进程和父进程同时运行导… 文章目录 第九周预习报告学习内容AI 对学习内容的总结章节概述内容总结 对知识总结的反思思维导图基于AI的学习知识点1系统调用知识点2进程控制 学习实践过程遇到的问题与解决方式问题1fork() 调用后子进程和父进程同时运行导致输出混乱问题2exec() 调用失败程序无法执行新程序问题3open() 系统调用失败无法打开文件问题4wait() 调用后父进程无法正确捕获子进程的退出状态问题5环境变量传递失败子进程无法获取正确的环境变量 参考资料 第九周预习报告
学习内容
Head First C 第9章
AI 对学习内容的总结
章节概述
本章主要介绍进程和系统调用的概念学习如何在自己的代码内部使用其他程序学会如何使用系统服务来创建和控制进程。使程序能够访问电子邮件、Web以及其他任何安装的工具。
内容总结 系统调用 定义系统调用是用户程序与操作系统内核之间的接口允许用户程序请求操作系统执行特定的任务。常见系统调用 open()打开文件。read()从文件中读取数据。write()将数据写入文件。close()关闭文件。fork()创建一个新进程。exec()执行一个新程序。wait()等待子进程结束。exit()终止当前进程。 工作原理当用户程序调用一个系统调用时CPU会从用户模式切换到内核模式内核执行相应的操作完成后返回结果CPU再切换回用户模式。 进程控制 进程进程是操作系统进行资源分配和调度的基本单位每个进程都有自己的地址空间、文件描述符、环境变量等。创建进程 fork()创建一个新进程新进程是调用进程的副本两者几乎完全相同但有一个唯一的区别fork()返回值不同。在父进程中返回子进程的PID在子进程中返回0。 执行新程序 exec()有一系列函数如execl()、execlp()、execle()、execv()、execvp()、execvpe()用于替换当前进程的地址空间使其开始执行一个新程序。 等待子进程 wait()父进程调用wait()等待子进程结束wait()会阻塞父进程直到子进程结束。 终止进程 exit()终止当前进程可以传递一个退出状态码给父进程。 环境变量传递 定义环境变量是操作系统提供的一种机制用于在进程之间传递配置信息。传递方式子进程会继承父进程的环境变量可以通过exec()系列函数传递新的环境变量。 错误处理 返回值大多数系统调用都有一个返回值成功时返回一个有意义的值失败时返回一个错误码通常是负数。errno全局变量errno用于存储系统调用的错误代码可以通过strerror()函数将错误代码转换为人类可读的字符串。#include stdio.h
#include unistd.h
#include errno.h
#include string.hint main() {int fd open(nonexistent_file.txt, O_RDONLY);if (fd -1) {fprintf(stderr, Error opening file: %s\n, strerror(errno));return 1;}close(fd);return 0;
}RSS订阅 定义RSSReally Simple Syndication是一种用于发布经常更新的内容的标准格式如博客文章、新闻等。实现通过系统调用读取RSS源解析XML数据提取所需信息。#include stdio.h
#include curl/curl.hsize_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {size_t written fwrite(ptr, size, nmemb, stream);return written;
}int main() {CURL *curl;FILE *file;CURLcode res;curl_global_init(CURL_GLOBAL_DEFAULT);curl curl_easy_init();if(curl) {file fopen(rss_feed.xml, wb);curl_easy_setopt(curl, CURLOPT_URL, http://example.com/rss.xml);curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);res curl_easy_perform(curl);if(res ! CURLE_OK) {fprintf(stderr, curl_easy_perform() failed: %s\n, curl_easy_strerror(res));}fclose(file);curl_easy_cleanup(curl);}curl_global_cleanup();return 0;
}父子进程关系 父子关系通过fork()创建的子进程是父进程的子进程子进程可以调用getppid()获取父进程的PID。#include stdio.h
#include unistd.h
#include sys/wait.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程printf(Child process: PID %d, PPID %d\n, getpid(), getppid());execlp(/bin/ls, ls, -l, NULL);} else {// 父进程printf(Parent process: PID %d, Child PID %d\n, getpid(), pid);wait(NULL); // 等待子进程结束printf(Child process finished\n);}return 0;
}对知识总结的反思 系统调用 系统调用是用户程序与操作系统内核之间的桥梁通过系统调用可以执行各种底层操作如文件操作、进程控制等。在实际编程中需要仔细检查每个系统调用的返回值以确保调用成功并处理可能出现的错误。 进程控制 进程控制是操作系统管理进程生命周期的关键部分通过fork()和exec()可以创建和执行新的进程。在使用fork()和exec()时需要注意父子进程的关系确保程序逻辑的正确性。 环境变量传递 环境变量是进程之间传递配置信息的重要机制子进程会继承父进程的环境变量。在调用exec()系列函数时可以通过参数传递新的环境变量。 错误处理 错误处理是确保程序健壮性的关键通过检查系统调用的返回值和使用errno可以有效地处理错误。 RSS订阅 RSS订阅是一个实际应用的例子展示了如何使用系统调用和网络编程来获取和处理外部数据。通过CURL库可以方便地实现HTTP请求和数据下载。
思维导图
mindmap本周学习内容进程和系统调用章节概述系统调用定义常见系统调用openreadwritecloseforkexecwaitexit进程控制进程创建进程fork执行新程序exec等待子进程wait终止进程exit环境变量传递定义传递方式错误处理返回值errnoRSS订阅定义实现使用CURL库解析XML数据父子进程关系父子关系getppid重点内容系统调用定义常见系统调用openreadwritecloseforkexecwaitexit进程控制进程创建进程fork执行新程序exec等待子进程wait终止进程exit环境变量传递定义传递方式错误处理返回值errnoRSS订阅定义实现使用CURL库解析XML数据父子进程关系父子关系getppid基于AI的学习
知识点1系统调用
AI: 举一个常见的系统调用例子。
我: 打开文件是一个常见的系统调用。当程序想要读取或写入文件时它会调用 open() 系统调用。这个调用会请求操作系统打开指定的文件并返回一个文件描述符程序可以使用这个文件描述符进行后续的读写操作。
AI: 系统调用 fork() 和 exec() 有什么区别
我: fork() 和 exec() 是两个不同的系统调用它们分别用于不同的目的。
fork() 用于创建一个新的进程。新进程是调用进程的副本具有相同的代码段、数据段和堆栈段但有自己的独立内存空间。fork() 返回一个值如果返回值为0则表示当前进程是子进程如果返回值大于0则表示当前进程是父进程返回值是子进程的PID。exec() 用于在当前进程中加载并运行一个新的程序。exec() 有多个变种如 execl()、execlp()、execle()、execv()、execvp() 和 execvpe()它们的主要区别在于参数的传递方式。调用 exec() 之后当前进程的代码段、数据段和堆栈段会被新程序的代码段、数据段和堆栈段替换。
AI: wait() 系统调用的作用是什么
我: wait() 系统调用用于父进程等待子进程结束。父进程调用 wait() 会阻塞直到一个子进程结束。wait() 返回子进程的PID并且可以通过返回值和 wait() 的参数来获取子进程的退出状态。
AI: 如何处理系统调用的错误
我: 处理系统调用的错误通常涉及以下几个步骤
检查系统调用的返回值。大多数系统调用在成功时返回一个有意义的值而在失败时返回一个错误码通常是负数。如果系统调用失败检查全局变量 errno 以获取具体的错误代码。使用 strerror() 函数将错误代码转换为人类可读的错误信息并进行适当的错误处理如打印错误信息、记录日志或采取其他补救措施。
AI: 举一个处理 open() 系统调用错误的例子。
我: 当然以下是一个处理 open() 系统调用错误的示例代码
#include stdio.h
#include unistd.h
#include errno.h
#include string.hint main() {int fd open(nonexistent_file.txt, O_RDONLY);if (fd -1) {fprintf(stderr, Error opening file: %s\n, strerror(errno));return 1;}close(fd);return 0;
}在这个例子中如果 open() 调用失败fd 将被设置为 -1程序会检查 errno 并使用 strerror() 函数将错误代码转换为错误信息然后打印错误信息并返回1表示程序异常退出。
知识点2进程控制
AI: 什么是进程
我: 进程是操作系统进行资源分配和调度的基本单位。每个进程都有自己的地址空间、文件描述符、环境变量等。进程可以包含一个或多个线程每个线程共享进程的资源。
AI: fork() 系统调用的返回值有什么含义
我: fork() 系统调用的返回值有三种可能
如果 fork() 成功它会在父进程中返回子进程的PID进程标识符在子进程中返回0。如果 fork() 失败它会返回-1并设置全局变量 errno 以指示具体的错误原因。
AI: exec() 系统调用的变种有哪些
我: exec() 系统调用有多个变种它们的主要区别在于参数的传递方式
execl(): 以固定数量的参数调用新程序。execlp(): 类似于 execl()但会在环境变量 PATH 中查找可执行文件。execle(): 类似于 execl()但允许传递环境变量。execv(): 以数组形式传递参数。execvp(): 类似于 execv()但会在环境变量 PATH 中查找可执行文件。execvpe(): 类似于 execvp()但允许传递环境变量。
AI: wait() 系统调用的参数和返回值是什么
我: wait() 系统调用的原型如下
pid_t wait(int *status);参数 status 是一个指向整数的指针用于接收子进程的退出状态。如果不需要获取退出状态可以将 status 设为 NULL。返回值是子进程的PID。如果没有子进程结束wait() 会阻塞父进程。如果 wait() 失败它会返回-1并设置全局变量 errno 以指示具体的错误原因。
AI: 举一个使用 fork() 和 exec() 的示例。
我: 当然以下是一个使用 fork() 和 exec() 的示例代码
#include stdio.h
#include unistd.h
#include sys/wait.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程printf(Child process: PID %d, PPID %d\n, getpid(), getppid());execlp(/bin/ls, ls, -l, NULL);fprintf(stderr, Failed to execute ls\n);return 1;} else {// 父进程printf(Parent process: PID %d, Child PID %d\n, getpid(), pid);wait(NULL); // 等待子进程结束printf(Child process finished\n);}return 0;
}在这个例子中父进程调用 fork() 创建一个子进程。子进程调用 execlp() 执行 /bin/ls 命令列出当前目录的详细信息。父进程调用 wait() 等待子进程结束然后打印一条消息表示子进程已经结束。
学习实践过程遇到的问题与解决方式
问题1fork() 调用后子进程和父进程同时运行导致输出混乱
描述在使用 fork() 创建子进程后子进程和父进程同时运行导致输出混乱难以区分哪个输出来自哪个进程。 解决方式
使用 sleep() 临时解决#include stdio.h
#include unistd.h
#include sys/wait.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程sleep(1); // 让子进程稍微延迟一下printf(Child process: PID %d, PPID %d\n, getpid(), getppid());execlp(/bin/ls, ls, -l, NULL);} else {// 父进程printf(Parent process: PID %d, Child PID %d\n, getpid(), pid);wait(NULL); // 等待子进程结束printf(Child process finished\n);}return 0;
}使用 wait() 确保子进程先结束#include stdio.h
#include unistd.h
#include sys/wait.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程printf(Child process: PID %d, PPID %d\n, getpid(), getppid());execlp(/bin/ls, ls, -l, NULL);} else {// 父进程wait(NULL); // 等待子进程结束printf(Parent process: PID %d, Child PID %d\n, getpid(), pid);printf(Child process finished\n);}return 0;
}问题2exec() 调用失败程序无法执行新程序
描述在调用 exec() 系列函数时程序无法成功执行新程序导致程序挂起或报错。 解决方式
检查路径和文件名 确保提供的路径和文件名是正确的。如果使用 execlp() 或 execvp()确保环境变量 PATH 中包含了可执行文件的路径。检查错误信息 使用 perror() 或 strerror(errno) 打印详细的错误信息。#include stdio.h
#include unistd.h
#include errno.h
#include string.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程execlp(/bin/ls, ls, -l, NULL);perror(Failed to execute ls); // 打印错误信息return 1;} else {// 父进程wait(NULL); // 等待子进程结束printf(Child process finished\n);}return 0;
}问题3open() 系统调用失败无法打开文件
描述在调用 open() 系统调用时无法打开指定的文件导致程序出错。 解决方式
检查文件路径和权限 确保文件路径是正确的并且当前用户有权限访问该文件。检查错误信息 使用 perror() 或 strerror(errno) 打印详细的错误信息。#include stdio.h
#include unistd.h
#include errno.h
#include string.hint main() {int fd open(nonexistent_file.txt, O_RDONLY);if (fd -1) {fprintf(stderr, Error opening file: %s\n, strerror(errno));return 1;}close(fd);return 0;
}问题4wait() 调用后父进程无法正确捕获子进程的退出状态
描述在调用 wait() 系统调用后父进程无法正确捕获子进程的退出状态导致程序逻辑出错。 解决方式 使用 WIFEXITED 和 WEXITSTATUS 宏 使用 WIFEXITED 检查子进程是否正常退出使用 WEXITSTATUS 获取子进程的退出状态。
#include stdio.h
#include unistd.h
#include sys/wait.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程execlp(/bin/ls, ls, -l, NULL);perror(Failed to execute ls);return 1;} else {// 父进程int status;pid_t child_pid wait(status);if (WIFEXITED(status)) {printf(Child process %d exited with status %d\n, child_pid, WEXITSTATUS(status));} else {printf(Child process %d terminated abnormally\n, child_pid);}}return 0;
}问题5环境变量传递失败子进程无法获取正确的环境变量
描述在使用 exec() 系列函数时子进程无法获取正确的环境变量导致程序出错。 解决方式 检查环境变量的传递方式 确保在调用 exec() 系列函数时正确传递了环境变量。
#include stdio.h
#include unistd.h
#include stdlib.hint main() {pid_t pid fork();if (pid 0) {fprintf(stderr, Fork failed\n);return 1;} else if (pid 0) {// 子进程char *new_env[] {PATH/usr/bin:/bin, MY_VARvalue, NULL};execle(/bin/ls, ls, -l, NULL, new_env);perror(Failed to execute ls);return 1;} else {// 父进程wait(NULL); // 等待子进程结束printf(Child process finished\n);}return 0;
}参考资料
HeadFirstC嗨翻C语言通义千问Linux System CallsProcess Control in LinuxError Handling in C课程 mindmapMermaid Live Editor