嵌入式编程题
1.
⽂件编程实例:打开⼀个⽂件,向⽂件写⼊三个字符串;然后重新定位⽂件流读写指针到⽂件起始位置,从⽂
件读取刚写⼊的三个字符串到另⼀个缓冲,并且打印读出来的字符串。
#include <stdio.h>
int main() {
// 声明一个指向文件的指针变量fp
FILE *fp = NULL;
// 声明一个字符串数组buf,用于存储文件的内容
const char *buf[3] = {
"This is the first line\n",
"Second line\n",
"OK, the last line\n"
};
// 声明一个二维字符数组tmp_buf,为题干中的“另一个缓冲”,用于存储从文件中读取的内容
char tmp_buf[3][64];
// 声明一个整型变量i,用于循环计数
int i;
// 打开名为text.dat的文件,并将文件指针赋值给fp
// 使用"w+b"模式打开文件,以二进制读写方式打开,文件不存在则创建它
fp = fopen("text.dat", "w+b");
// "w+b" 模式用于以读写方式打开一个文件,如果文件已存在,则其内容被截断,即清空文件内容。如果文件不存在,则创建新文件。
// 检查文件是否成功打开,如果打开失败,则打印错误消息并返回-1
if (fp == NULL) {
printf("Cannot open file\n");
return -1;
}
// 将buf数组中的字符串逐行写入文件
for (i = 0; i < 3; i++) {
// fputs函数用于将字符串写入文件,直到遇到字符串结尾的空字符'\0'
fputs(buf[i], fp);
}
// 清空缓冲区并刷新输出缓冲区,确保所有写入操作都已执行
fflush(fp);
// fseek函数用于在文件中进行定位,将文件指针fp重新定位到文件起始位置
// SEEK_SET表示从文件的开头开始计算偏移量
fseek(fp, 0, SEEK_SET);
// 从文件中逐行读取内容并存储在tmp_buf数组中
for (i = 0; i < 3; i++) {
// fgets函数用于从文件中读取字符串,最多读取64个字符
fgets(tmp_buf[i], sizeof(tmp_buf[i]), fp);
// 打印读取的内容
printf("%s", tmp_buf[i]);
}
// 关闭文件
fclose(fp);
return 0;
}
函数解析:
在提供的C语言程序中,使用了多个标准库函数,下面是对这些函数的解析:
-
#include <stdio.h>
- 预处理指令,用于引入标准输入输出库函数的声明。
-
FILE *fp = NULL;
- 声明一个指向
FILE
结构的指针fp
,用于后续操作文件。
- 声明一个指向
-
fopen(const char *filename, const char *mode);
- 函数用于打开文件,并返回指向该文件的
FILE
指针。如果失败,返回NULL
。 - 第一个参数是文件名,第二个参数是打开模式。
- 函数用于打开文件,并返回指向该文件的
-
fputs(const char *str, FILE *stream);
- 函数用于将字符串写入到指定的文件流中,直到遇到字符串结束符
'\0'
。
- 函数用于将字符串写入到指定的文件流中,直到遇到字符串结束符
-
fflush(FILE *stream);
- 函数用于刷新输出缓冲区,确保所有输出操作都已执行。
-
fseek(FILE *stream, long offset, int whence);
- 函数用于在文件中移动文件位置指针。
- 第二个参数是偏移量,第三个参数
whence
指定了偏移量的计算方式:SEEK_SET
:从文件开头开始计算。SEEK_CUR
:从当前位置开始计算。SEEK_END
:从文件末尾开始计算。
-
fgets(char *str, int num, FILE *stream);
- 函数用于从指定的文件流中读取字符串,最多读取
num - 1
个字符加上一个结束符'\0'
。
- 函数用于从指定的文件流中读取字符串,最多读取
-
printf(const char *format, ...);
- 函数用于格式化输出到标准输出(通常是屏幕)。
-
fclose(FILE *stream);
- 函数用于关闭指定的文件流,并释放所有与该文件相关的资源。
-
return -1;
- 用于从
main
函数返回一个整数值-1
,通常表示程序执行失败。
- 用于从
-
int main() { ... }
- 程序的入口点,
main
函数的返回类型是int
,表示程序的退出状态。
- 程序的入口点,
-
int i;
- 声明一个整型变量
i
,用于循环计数。
- 声明一个整型变量
请注意,程序中使用 fflush(fp);
可能不是必要的,因为 fputs
在写入字符串后会自动刷新缓冲区。然而,在某些情况下,如果需要确保数据立即写入磁盘,可以使用 fflush
。此外,fflush
调用在读取文件之前是不必要的,因为读取操作不会影响缓冲区的状态。
2.
程序创建两个进程,在⽗进程和⼦进程之间通过管道传递数据,⽗进程向⼦进程发送字符串“exit”表示让⼦进程退出,并且等待⼦进程返回;⼦进程查询管道,当从管道读出字符串“exit”时结束
#include <stdio.h> // 包含用于标准输入输出的函数,如printf和scanf
#include <stdlib.h> // 包含用于内存分配和进程控制的函数,如malloc、free、exit
#include <unistd.h> // 包含UNIX标准函数,如read、write、fork、pipe、sleep
#include <string.h> // 包含用于字符串操作的函数,如strcmp、strlen
#include <sys/types.h>// 包含一些基本的数据类型定义,如pid_t
#include <sys/wait.h> // 包含等待进程状态的函数,如waitpid
int main() {
int pipefds[2]; // 声明一个整型数组,用于存储管道的文件描述符 0代表读端,1代表写端
pid_t pid; // 声明一个pid_t类型的变量,用于存储fork()的返回值
char buf[5]; // 声明一个字符数组,用于从管道中读取数据
const char *cmd = "exit"; // 声明一个字符串常量,用于存储要发送的命令
// 使用pipe函数创建一个管道,并将文件描述符存储在pipefds数组中
// pipe(pipefds): 这是一个调用 pipe 函数的表达式。pipe 函数用于创建一个管道(pipe),这是一种特殊的文件描述符,允许两个进程通过一个通道进行单向通信。pipefds 是一个整型数组,它将被填充为两个文件描述符:pipefds[0] 用于读取,pipefds[1] 用于写入
if (pipe(pipefds) == -1) {
perror("pipe"); // 如果pipe函数失败,输出错误信息
exit(EXIT_FAILURE); // 退出程序并返回失败状态
}
// 使用fork函数创建一个新的进程
pid = fork();
if (pid == -1) {
perror("fork"); // 如果fork函数失败,输出错误信息
exit(EXIT_FAILURE); // 退出程序并返回失败状态
}
if (pid == 0) { // 如果pid为0,表示当前是子进程
close(pipefds[1]); // 子进程不需要写端,关闭它以避免错误
// 从管道的读端读取数据,最多读取4个字节加一个空字符 '\0'
read(pipefds[0], buf, sizeof(buf));
// 如果读取的数据与cmd相同,表示接收到退出命令
if (strcmp(buf, cmd) == 0) {
printf("Child process received 'exit' command and will exit.\n");
}
close(pipefds[0]); // 子进程操作完成后关闭读端
_exit(EXIT_SUCCESS); // 使用_exit立即退出子进程,不进行任何清理工作
} else { // 如果pid不为0,表示当前是父进程
close(pipefds[0]); // 父进程不需要读端,关闭它
sleep(1); // 让子进程先执行,这里使用1秒的睡眠时间
// 向管道的写端写入"exit"命令,包括字符串末尾的空字符 '\0'
write(pipefds[1], cmd, strlen(cmd) + 1);
close(pipefds[1]); // 写入完成后关闭写端
// 使用waitpid等待子进程结束,这里使用0作为选项,表示等待任意一个子进程
int status;
waitpid(pid, &status, 0);
// 检查子进程是否正常退出,并输出退出状态
if (WIFEXITED(status)) {
printf("Child process exited with status %d.\n", WEXITSTATUS(status));
}
}
return 0; // 父进程结束,返回0表示成功
}
函数解析
以下是代码中出现的每个函数及其解析:
-
pipe(pipefds)
:- 功能:创建一个管道,允许一个进程(通常是子进程)与另一个进程(通常是父进程)进行单向通信。
- 参数:
pipefds
是一个整型数组,用于存储管道的两个文件描述符,pipefds[0]
为读端,pipefds[1]
为写端。 - 返回值:成功时返回0,失败时返回-1。
-
fork()
:- 功能:创建一个新的进程,子进程是父进程的副本,从fork()调用处开始执行。
- 参数:无。
- 返回值:在子进程中返回0,在父进程中返回子进程的PID,在出错时返回-1。
-
close(fd)
:- 功能:关闭指定的文件描述符。
- 参数:
fd
是要关闭的文件描述符。 - 返回值:成功时返回0,失败时返回-1。
-
read(fd, buf, count)
:- 功能:从指定的文件描述符
fd
读取数据到缓冲区buf
,最多读取count
个字节。 - 参数:
fd
是文件描述符,buf
是存储读取数据的缓冲区,count
是要读取的字节数。 - 返回值:成功时返回实际读取的字节数,失败时返回-1,如果达到文件末尾返回0。
- 功能:从指定的文件描述符
-
strcmp(str1, str2)
:- 功能:比较两个字符串
str1
和str2
。 - 参数:
str1
和str2
是要比较的字符串。 - 返回值:如果
str1
小于str2
返回小于0的值,如果str1
等于str2
返回0,如果str1
大于str2
返回大于0的值。
- 功能:比较两个字符串
-
write(fd, buf, count)
:- 功能:向指定的文件描述符
fd
写入数据,数据从缓冲区buf
复制,写入count
个字节。 - 参数:
fd
是文件描述符,buf
是包含要写入数据的缓冲区,count
是要写入的字节数。 - 返回值:成功时返回实际写入的字节数,失败时返回-1。
- 功能:向指定的文件描述符
-
sleep(seconds)
:- 功能:使调用进程暂停执行指定的秒数。
- 参数:
seconds
是要暂停的秒数。 - 返回值:无。
-
_exit(status)
:- 功能:立即终止进程,并返回
status
状态值给父进程。 - 参数:
status
是退出状态值,通常是一个整数值。 - 返回值:无,因为进程将被终止。
- 功能:立即终止进程,并返回
-
waitpid(pid, status, options)
:- 功能:等待一个子进程的状态改变,可以是终止、停止或继续。
- 参数:
pid
是要等待的子进程的PID,status
是指向整数变量的指针,用于存储子进程的退出状态,options
是指定等待选项的整数。 - 返回值:成功时返回子进程的PID,失败时返回-1。
-
WIFEXITED(status)
和WEXITSTATUS(status)
:- 功能:
WIFEXITED
检查进程是否正常退出,WEXITSTATUS
获取进程的退出状态码。 - 参数:
status
是从waitpid
或其他等待函数获得的状态值。 - 返回值:
WIFEXITED
在进程正常退出时返回非零值,否则返回0;WEXITSTATUS
返回进程的退出状态码。
- 功能:
-
strlen(str)
:- 功能:计算字符串
str
的长度,不包括结尾的空字符\0
。 - 参数:
str
是要计算长度的字符串。 - 返回值:返回字符串的长度。
- 功能:计算字符串
-
perror(str)
:- 功能:将当前错误消息字符串打印到标准错误输出。
- 参数:
str
是要打印在错误消息前面的字符串。 - 返回值:无。
这些函数是UNIX和类UNIX系统编程中常用的库函数,用于进程控制、管道通信、文件描述符操作等。
3.
在主程序中创建两个线程mid_thread和term_thread,mid线程不断等待term线程终⽌它,并且每隔2秒打印⼀次等待的次数;term线程接受从主函数传进来的mid线程的ID。如果ID合法,就调⽤pthread_cancel 函数结束mid线程。
#include <stdio.h> // 包含标准输入输出函数
#include <stdlib.h> // 包含标准库函数,如exit
#include <pthread.h> // 包含POSIX线程函数
#include <unistd.h> // 包含UNIX标准函数,如sleep
// 全局变量,用于计数mid线程的等待次数
int count = 0;
// mid_thread 线程函数的声明
void* mid_thread_function(void* arg) {
// 无限循环,模拟持续运行的线程
while (1) {
count++; // 增加等待次数
printf("Mid thread is waiting... Count: %d\n", count); // 打印当前等待次数
sleep(2); // 睡眠2秒,然后继续循环
}
return NULL; // 线程函数返回NULL
}
// term_thread 线程函数的声明
void* term_thread_function(void* arg) {
pthread_t* midThreadIdPtr = (pthread_t*)arg; // 从参数中获取指向mid线程ID的指针
// 检查mid线程的ID是否非空且不是当前term线程自身
if (midThreadIdPtr != NULL && *midThreadIdPtr != pthread_self()) {
// 使用pthread_cancel终止mid线程
pthread_cancel(*midThreadIdPtr);
printf("Term thread has canceled the mid thread.\n");
}
return NULL; // 线程函数返回NULL
}
int main() {
pthread_t mid, term; // 声明两个线程的变量
pthread_t midThreadId; // 存储mid线程的ID
// 创建mid线程,参数依次为目标线程函数、线程属性(NULL表示默认属性)、线程函数参数(NULL)
if (pthread_create(&mid, NULL, mid_thread_function, NULL) != 0) {
perror("Failed to create mid thread"); // 如果创建失败,打印错误信息
exit(EXIT_FAILURE); // 退出程序
}
midThreadId = mid; // 存储mid线程的ID
// 创建term线程,传递mid线程的ID
if (pthread_create(&term, NULL, term_thread_function, &midThreadId) != 0) {
perror("Failed to create term thread"); // 如果创建失败,打印错误信息
exit(EXIT_FAILURE); // 退出程序
}
// 主线程等待term线程结束,确保term线程有机会取消mid线程
pthread_join(term, NULL);
printf("Main function finished.\n"); // 打印主函数结束信息
return EXIT_SUCCESS; // 返回成功状态码
}
函数解析
以下是代码中出现的每个函数及其解析:
-
pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
:- 功能:创建一个新线程。
- 参数:
thread
: 指向pthread_t
变量的指针,用于存储新创建线程的ID。attr
: 指向pthread_attr_t
结构的指针,指定线程的属性(传入NULL
表示使用默认属性)。start_routine
: 线程函数的入口,即新线程将执行的函数。arg
: 传递给线程函数的参数。
- 返回值:成功时返回0,失败时返回错误码。
-
pthread_self(void)
:- 功能:返回调用线程的ID。
- 参数:无。
- 返回值:调用线程的
pthread_t
类型的ID。
-
pthread_cancel(pthread_t thread)
:- 功能:请求取消指定的线程。
- 参数:
thread
: 需要被取消的线程的ID。
- 返回值:成功时返回0,失败时返回错误码。
-
pthread_join(pthread_t thread, void **retval)
:- 功能:等待指定线程终止。
- 参数:
thread
: 要等待的线程的ID。retval
: 可选参数,指向一个指针的指针,用于存储被取消线程的退出状态。
- 返回值:成功时返回0,失败时返回错误码。
-
sleep(unsigned int seconds)
:- 功能:使调用线程暂停执行指定的秒数。
- 参数:
seconds
: 要暂停的秒数。
- 返回值:无。
-
printf(const char *format, ...)
:- 功能:格式化输出到标准输出设备(通常是控制台)。
- 参数:
format
: 格式字符串,定义了后续参数的输出格式。...
: 可变参数列表,包含要输出的数据。
- 返回值:成功时返回打印的字符数,失败时返回负数。
-
perror(const char *str)
:- 功能:将当前错误消息字符串打印到标准错误输出,并在字符串前面添加
str
指定的错误描述。 - 参数:
str
: 错误描述字符串。
- 返回值:无。
- 功能:将当前错误消息字符串打印到标准错误输出,并在字符串前面添加
-
exit(int status)
:- 功能:立即终止程序,并返回给操作系统的状态码。
- 参数:
status
: 状态码,EXIT_SUCCESS
通常用于表示成功,EXIT_FAILURE
表示失败。
- 返回值:无(实际上,
exit
调用不会返回)。
-
pthread_exit(void *retval)
:- 功能:终止调用线程,并返回一个退出值给
pthread_join
。 - 参数:
retval
: 退出值,可以是NULL
或指向一个值的指针。
- 返回值:无(实际上,
pthread_exit
调用不会返回)。
- 功能:终止调用线程,并返回一个退出值给
这些函数是多线程编程中常用的,用于线程的创建、同步、取消和状态管理。