Linux 程序设计 ・ 考点分类 ・ 真题解析 ・ 知识点总结
前言
之前搞错了,没上试卷原题,这次是的了
按 8 大考点组织。每个考点先给 知识解析 ,再附该考点下的历年真题(2024 & 2025 甲A),真题含 原题 + 翻译 + 答案 + 解析 。
考点目录:
- 考点一 Linux 基础命令与文件权限(ls / 文件类型 / chmod)
- 考点二 文件操作:系统调用 I/O 与标准库 I/O(open/read/write、fopen、dup 重定向、fseek)
- 考点三 进程控制:fork / exec / wait
- 考点四 管道 pipe 与进程间通信(IPC:管道、消息队列)
- 考点五 信号量与同步:System V / POSIX、生产者---消费者
- 考点六 多线程 pthread
- 考点七 Socket 网络编程(TCP/IP)
- 考点八 编译、Makefile 与信号(gcc / make / signal)
真题答案以官方《2024 标准答案》《2025 参考答案》为准;个别官方笔误或与试卷不符之处(如 2025 程序设计两题)已在解析中标注并给出修正。
考点一 Linux 基础命令与文件权限
【知识解析】 ls -l 长格式输出从左到右依次是:文件类型+权限位、硬链接数、属主、属组、大小(字节)、修改时间、文件名。行首第一个字符表示文件类型。权限位分三组:属主 u、属组 g、其他 o,每组 rwx。
要点速记
- 文件类型字符:- 普通文件,d 目录,l 符号链接,c 字符设备,b 块设备,p 命名管道(FIFO),s 套接字。
- chmod 数字法:r=4, w=2, x=1;每组相加后按 属主-属组-其他 拼成三位八进制。
- 常见权限:755=rwxr-xr-x,644=rw-r--r--,700=rwx------,764=rwxrw-r--。
- chmod 符号法:u/g/o/a 配 +/-/= 与 rwx,如 chmod u+x、chmod go-w、chmod a=r。
该考点真题
真题 ・ 2025 填空(1)
- 原题:In Linux, the character p represents a ____, while d represents a ____.
- 翻译:在 Linux 中,字符 p 表示____,d 表示____。
- 答案: 管道文件(命名管道 FIFO);目录文件。
- 解析:ls -l 行首字符决定文件类型:p=命名管道,d=目录。
真题 ・ 2025 填空(2)
- 原题:If "chmod 764 ff.txt" is executed successfully, the owner's permissions are ____, the group's are ____.
- 翻译:若成功执行 chmod 764 ff.txt,则属主权限是____,属组权限是____。
- 答案: 属主 rwx;属组 rw-。
- 解析:7=rwx,6=rw-,4=r--;按属主/属组/其他依次对应。
考点二 文件操作:系统调用 I/O 与标准库 I/O
【知识解析】 两套接口:低级 I/O 用 int 文件描述符(open/read/write/close、无缓冲),高级 I/O 用 FILE* 文件指针(fopen/fread/fwrite/fgets/fclose、带缓冲)。标准描述符 0=stdin、1=stdout、2=stderr。重定向靠 dup/dup2:close 掉某描述符后 dup 会复用最小可用号。fseek 以 SEEK_SET/CUR/END 移动读写位置。
要点速记
- open 返回 int 描述符;fopen 返回 FILE*。出错时 open 返回 -1。
- open 标志:O_RDONLY/O_WRONLY/O_RDWR、O_CREAT、O_APPEND、O_TRUNC;mode 用 S_IRUSR(400) 等组合。
- 权限 644 = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH。
- 重定向标准错误:close(2) 后 dup(fd)(等价 dup2(fd,2))。dup 参数须是 int 描述符(FILE* 用 fileno(fp))。
- fseek 三个基准:SEEK_SET 文件头、SEEK_CUR 当前、SEEK_END 文件尾。
- mmap 映射不会自动写回磁盘,需 msync()。
该考点真题
真题 ・ 2025 填空(3)
- 原题:When using a file stream (FILE *), the function used to open the file is ____.
- 翻译:使用文件流(FILE *)操作文件时,用来打开文件的函数是____。
- 答案: fopen()。(官方答案误写成 fopon)
- 解析:标准库用 fopen 返回 FILE*;低级 I/O 用 open 返回 int。
2025 程序阅读 1 --- 文件复制 kb1(file.in 含 4200 字符)
c
int main() {
char block[1024]; int in, out, nread;
in = open("file.in", O_RDONLY);
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
while ((nread = read(in, block, sizeof(block))) > 0) {
write(out, block, nread);
write(1, block, nread);
}
exit(0);
}
真题 ・ 2025 程序阅读1(1)
- 原题:What user can run kb1 successfully? What is the result of "kb1 > file.bak"?
- 翻译:哪类用户能成功运行 kb1?"kb1 > file.bak" 的结果是什么?
- 答案: 对 file.in 有读权限的用户;file.in 内容被同时复制到 file.out 和 file.bak。
- 解析:write(out) 写 file.out,write(1) 写标准输出,被 > 重定向到 file.bak,故同时进入两文件。
真题 ・ 2025 程序阅读1(2)
- 原题:Which line needs modification to set file.out permissions to 644? How?
- 翻译:要把 file.out 权限设为 644,需改哪一行?怎么改?
- 答案: 改 out=open(...) 行,权限参数改为 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH。
- 解析:原 600 加上属组读(040)与其他读(004)即 644。
真题 ・ 2025 程序阅读1(3)
- 原题:Total system calls made? (List all)
- 翻译:总共多少次系统调用?(列出全部)
- 答案: 18 次:open×2 + read×5 + write(out)×5 + write(1)×5 + exit×1。
- 解析:4200 字符按 1024/次读 5 次取数据,每次循环 write 两次;官方评分答案 18 次。
2024 程序阅读 2-2 --- 读标准输入并写文件
c
int main(int argc, char *argv[]) {
char buffer[1024]; int nread, out;
nread = read(0, buffer, 1024); // (1)
if (argc > 1) // (2)
out = open(argv[1], O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR); // (3)
else
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
write(out, buffer, nread);
exit(0);
}
真题 ・ 2024 程序阅读2-2(1)
- 原题:In line (1), what does the first argument 0 of read() refer to?
- 翻译:第①行 read() 的第一个参数 0 指代什么?
- 答案: 标准输入(stdin,描述符 0)。
- 解析:从键盘读取输入。
真题 ・ 2024 程序阅读2-2(2)
- 原题:What do argc and argv1 refer to?
- 翻译:argc 和 argv1 指代什么?
- 答案: argc 是命令行参数个数;argv1 是第 1 个参数(此处当作输出文件名)。
- 解析:带参数则用它做输出文件名,否则默认 file.out。
真题 ・ 2024 程序阅读2-2(3)/(4)
- 原题:Describe the workflow. Is the code style good? Any suggestions?
- 翻译:描述工作流程。代码风格好吗?有何建议?
- 答案: 流程:从标准输入读一段文本写入文件(带参用 argv1,否则 file.out)。风格不好:应检查 read/open/write 的返回值做错误处理。
- 解析:健壮代码必须判断系统调用是否失败。
真题 ・ 2024 程序设计(2)
- 原题:Redirect standard error to a file controlled by argv1, using dup().
- 翻译:用命令行参数 argv1 控制,把标准错误重定向到文件(用 dup())。
- 答案: 当 argv1=="redirect" 时打开 argv2,close(2) 后 dup(fd) 使 2 号指向该文件。
- 解析:close(2) 让 2 成为最小可用号,dup 复制后 stderr 即指向文件。dup 参数应为 int 描述符(或 fileno(fp))。
c
int main(int argc, char *argv[]) {
if (argc > 2 && strcmp("redirect", argv[1]) == 0) {
int fd = open(argv[2], O_WRONLY|O_CREAT|O_APPEND, 0644);
if (fd != -1) { close(2); dup(fd); close(fd); } // 等价 dup2(fd,2)
}
fprintf(stderr, "error goes to file when redirected\n");
return 0;
}
真题 ・ 2024 判断(6)(7)(8)(9)(10)
- 原题:(6) mmap auto-syncs memory and file. (7) fread optimizes with fixed size. (8) fd is an integer >=0. (9) 0,1,2 are stdin/stdout/stderr. (10) fseek can move by header/tail/current.
- 翻译:(6) mmap 自动同步内存与文件。(7) fread 以固定大小优化读取。(8) fd 是 >=0 的整数。(9) 0/1/2 是标准输入/输出/错误。(10) fseek 可基于文件头/尾/当前移动。
- 答案: (6) F;(7) T;(8) T;(9) T;(10) T。
- 解析:(6) mmap 需 msync 才写回,非自动;(7) 标准库带缓冲按固定大小读;(10) 对应 SEEK_SET/END/CUR。
考点三 进程控制:fork / exec / wait
【知识解析】 fork() 调用一次返回两次:父进程得子进程 PID(>0),子进程得 0,失败 -1。n 个连续 fork 产生 2^n 个进程。exec 系列(execlp 等)用新程序替换当前进程映像,替换成功后其后代码不再执行。wait()/waitpid() 让父进程阻塞等待子进程结束并回收,避免僵尸进程,也可控制父子输出顺序。
要点速记
- fork 返回值:父=子PID,子=0,失败=-1,用它区分父子分支。
- execlp 成功后"掉不下来":后面的语句被新映像覆盖;失败才继续往下。
- wait() 使父进程等子进程,常用于保证子进程输出在父进程(或 shell 提示符 $)之前。
- printf 行缓冲(\n)在终端会即时刷新;若 stdout 被重定向为全缓冲,fork 可能导致缓冲区内容被复制后重复输出。
2025 程序阅读 2 --- fork + execlp(kb2)
c
int main() {
pid_t pid; int n;
printf("fork example\n");
if ((pid = fork()) < 0) { perror("fork error"); exit(0); }
if (pid == 0) {
execlp("ls", "ls", "-l", 0);
n = 4; for (; n > 0; n--) printf("child process\n");
printf("Child has finished: PID = %d\n", getpid());
}
n = 3; for (; n > 0; n--) printf("parent process\n");
printf("Child has finished: PID = %d\n", pid);
printf("over\n");
exit(0);
}
真题 ・ 2025 程序阅读2(1)
- 原题:Outputs of parent and child during normal execution?
- 翻译:正常执行时父、子进程各自的输出?
- 答案: 父:parent process×3、Child has finished: PID=<子pid>、over;子:ls -l 的目录列表。
- 解析:子进程一进入就被 execlp 替换为 ls,只输出列表;父进程走 n=3 循环及之后语句。(fork 前 printf("fork example") 由父进程额外输出一次。)
真题 ・ 2025 程序阅读2(2)
- 原题:After removing execlp, child output? How to prevent duplication?
- 翻译:去掉 execlp 后子进程输出?如何避免重复?
- 答案: 子进程会输出 child process×4、Child has finished PID=<子pid>,并继续掉下去输出 parent process×3、PID=0、over。避免重复:子进程块末尾加 exit(0)。
- 解析:去掉 exec 后子进程顺序执行到公共代码,与父进程重复;exit(0) 让其提前结束。
真题 ・ 2025 程序阅读2(3)
- 原题:Without execlp, make parent's printf("over") run only after child finishes all output.
- 翻译:无 execlp 时,使父进程 printf("over") 仅在子进程输出全部完成后执行。
- 答案: 子进程末尾加 exit(0);父进程 printf("over") 之前加 wait()。
- 解析:wait() 阻塞父进程直到子进程结束。
2024 程序阅读 2-1 --- fork 输出计数
c
int main() {
int n;
printf("fork example\n");
if ((pid = fork()) < 0) { perror("fork error"); exit(0); }
if (pid == 0) { n = 4; /* execlp("ls","ls","-l",0); */ }
else n = 1;
for (; n > 0; n--) printf("fork example\n");
}
真题 ・ 2024 程序阅读2-1(1)
- 原题:How many lines of "fork example"? Why?
- 翻译:会输出多少行 "fork example"?为什么?
- 答案: 6 行:fork 前 1 行 + 子进程(n=4) 4 行 + 父进程(n=1) 1 行。
- 解析:官方总数 6(其"子3父2"分配为笔误,正确是子4父2)。fork 前 printf 行缓冲刷新只输出一次。
真题 ・ 2024 程序阅读2-1(2)
- 原题:If execlp is enabled, how many lines? Why?
- 翻译:若启用 execlp,输出多少行?为什么?
- 答案: 官方答案 3 行:子进程 execlp 后映像被替换,其后 printf 被覆盖。
- 解析:精确行数与 stdout 缓冲方式有关,按官方计分口径作答即可,重点是"execlp 覆盖子进程后续输出"。
真题 ・ 2024 程序阅读2-1(3)
- 原题:Some lines appear after prompt . Explain. How to make all appear before ?
- 翻译:部分输出出现在提示符 之后,解释原因;如何让全部出现在 之前?
- 答案: 父进程先结束返回 shell(打印 ),子进程后输出,故在 后。修改:父进程中调用 wait()/waitpid()。
- 解析:让父进程等待子进程结束再退出。
真题 ・ 2024 程序设计(1)
- 原题:Implement system() using fork(), wait(), and the execl() family.
- 翻译:用 fork()、wait()、execl 系列实现 system()。
- 答案: 子进程 exec 执行命令,父进程 wait 等待。见下方代码。
- 解析:system 本质 = fork + exec + wait。
c
int my_system(const char *command) {
pid_t pid = fork();
if (pid < 0) return -1;
if (pid == 0) { execlp("sh", "sh", "-c", command, (char*)0); _exit(127); }
int status; wait(&status); return status;
}
考点四 管道 pipe 与进程间通信(IPC)
【知识解析】 pipe(int fd2) 创建管道:fd0 读端、fd1 写端,单向先进先出字节流。要先 pipe 再 fork,子进程才能继承同一对描述符;先 fork 后各建各的管道则互不相通。常配合 close+dup 把标准输入/输出接到管道。消息队列(System V)用 msgget/msgsnd/msgrcv/msgctl,是内核持久对象,用完需 msgctl(IPC_RMID) 删除。
要点速记
- fd0 读、fd1 写;写端全关 → 读端 read 返回 0(EOF)。
- 管道通信必须"先 pipe 后 fork",否则无法共享。
- dup 重定向:父进程 close(1)+dup(fd1) → 输出写入管道;子进程 close(0)+dup(fd0) → 从管道读。
- 消息队列:msgsnd 发送、msgrcv 接收;msgrcv 的消息类型参数 =0 收所有、>0 只收特定类型。
- System V IPC(消息队列/信号量/共享内存)不随进程结束自动回收,需手动删除(ipcs 查看、ipcrm 删除)。
2025 程序阅读 3 --- 管道父子通信
c
int main() {
int fd[2], pid; char str[256];
if (pipe(fd) < 0) { perror("pipe"); exit(1); }
if ((pid = fork()) == -1) { perror("fork"); exit(1); }
if (pid == 0) { // 子进程
close(fd[1]); close(STD_INPUT); dup(fd[0]); // 0 -> 读端
fgets(str, sizeof(str), stdin); printf("%s\n", str);
} else { // 父进程
close(fd[0]); close(STD_OUTPUT); dup(fd[1]); // 1 -> 写端
printf("%d - output-:\n", (int)getpid());
execlp("ps", "ps", "-l", 0);
}
exit(0);
}
真题 ・ 2025 程序阅读3(1)
- 原题:Which processes hold read/write ends? Child's pipe descriptors before dup()?
- 翻译:哪个进程持读端/写端?子进程 dup 前的管道描述符?
- 答案: 子进程读端、父进程写端;子进程 dup 前读端是 fd0,写端 fd1 已被它关闭。
- 解析:子 close(fd1) 留读端,父 close(fd0) 留写端。
真题 ・ 2025 程序阅读3(2)
- 原题:What output does the child generate, and the mechanism?
- 翻译:子进程产生什么输出?机制是什么?
- 答案: "<父pid> - output-:" 和 ps -l 的结果。父进程把标准输出接到管道写端,输出经管道流给子进程;子进程把标准输入接到管道读端,读出后 printf 到屏幕。
- 解析:dup 重定向 + 管道实现父→子数据流。
真题 ・ 2025 程序阅读3(3)
- 原题:How would moving fork() before pipe() affect the child's output? Why?
- 翻译:把 fork() 移到 pipe() 之前会怎样?为什么?
- 答案: 子进程一直阻塞,管道无数据。因为父子各建各的管道,不能共享传递数据。
- 解析:管道必须先创建再 fork 才能被继承。
2024 程序阅读 2-4 --- 消息队列收发
c
/* receiver */
int main() {
int msgid; struct my_msg_st some_data; long msg_to_receive = 0;
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
while (running) {
msgrcv(msgid, &some_data, BUFSIZ, msg_to_receive, 0);
printf("You wrote: %s", some_data.some_text);
if (strncmp(some_data.some_text, "end", 3) == 0) running = 0;
}
msgctl(msgid, IPC_RMID, 0); // 删除队列
}
/* sender: fgets -> msgsnd 发送, 输入 end 结束 */
真题 ・ 2024 程序阅读2-4(1)(2)
- 原题:Describe how it works. Why msgctl(IPC_RMID)? Is it OK not to call it?
- 翻译:描述其工作原理。为何用 msgctl(IPC_RMID)?不调用可以吗?
- 答案: 用消息队列收发:sender 读键盘 msgsnd 发送,receiver msgrcv 接收打印,遇 "end" 结束。IPC_RMID 删除队列;不删也能运行,但队列会残留占用 IPC 资源,不是好做法。
- 解析:System V 消息队列内核持久,必须手动删除。
真题 ・ 2024 程序阅读2-4(3)
- 原题:How to make the receiver accept only messages of a specific type?
- 翻译:如何让 receiver 只接收特定类型的消息?
- 答案: 把 msgrcv 的消息类型参数(msg_to_receive)设为某正整数即可:=0 收所有,>0 只收该类型。
- 解析:同时 sender 用相同 my_msg_type 发送。
考点五 信号量与同步:System V / POSIX、生产者---消费者
【知识解析】 信号量用于互斥与同步。P 操作申请资源(值-1,可能阻塞),V 操作释放资源(值+1)。POSIX:sem_init/sem_wait§/sem_post(V)/sem_destroy。System V:semget/semop(sembuf 的 sem_op=-1 为 P、+1 为 V)/semctl(SETVAL 设初值, IPC_RMID 删除)。生产者---消费者三信号量:empty=N(空位)、full=0(产品)、mutex=1(互斥),资源信号量在外、mutex 在内。
要点速记
- POSIX sem_wait=P、sem_post=V;互斥锁 = 初值 1 的信号量。
- System V sem_op=-1 是 P、+1 是 V;值为 0 时 P 操作阻塞。
- 生产者:P(empty)→P(mutex)→放→V(mutex)→V(full);消费者:P(full)→P(mutex)→取→V(mutex)→V(empty)。
- mutex 必须在 empty/full 内层,否则缓冲区满/空时可能死锁。
- POSIX 命名信号量与 System V 信号量都不随进程结束自动销毁,需 sem_unlink / semctl(IPC_RMID)。
2025 程序阅读 4 --- 信号量同步 sem1(两进程 X/O)
c
int main(int argc, char *argv[]) {
int i, pause_time; char op_char = 'O';
srand(getpid());
sem_id = semget((key_t)1234, 1, 0666 | IPC_CREAT);
if (argc > 1) { set_semvalue(); op_char = 'X'; sleep(2); }
for (i = 0; i < 10; i++) {
semaphore_p(); // P
printf("%c", op_char); fflush(stdout);
sleep(rand()%3); // 临界区内停顿
printf("%c", op_char); fflush(stdout);
semaphore_v(); // V
sleep(rand()%2);
}
printf("\n%d - finished\n", getpid());
if (argc > 1) { sleep(10); del_semvalue(); }
}
真题 ・ 2025 程序阅读4(1)
- 原题:Values of argc, argv0, argv1 in both processes?
- 翻译:两个进程中 argc、argv0、argv1 的值?
- 答案: 进程1(sem1 1 &):argc=2, argv0="sem1", argv1="1";进程2(sem1):argc=1, argv0="sem1", argv1=NULL。
- 解析:带参数的进程负责初始化信号量并把字符设为 X。
真题 ・ 2025 程序阅读4(2)
- 原题:Concurrent output? How do the sleep statements affect it (OS perspective)?
- 翻译:并发输出是什么?sleep 语句从操作系统角度如何影响?
- 答案: 输出 20 个 X 和 20 个 O 混杂,连续的 X、O 都是偶数个。sleep 暂停当前进程让出 CPU;若无 sleep,很可能先连续 20 X 再连续 20 O(10 次循环太短,时间片轮转来不及切换)。
- 解析:P/V 把两次同字符 printf 包成临界区→连续同字符成对;临界区内 sleep 造成交错。
真题 ・ 2025 程序阅读4(3)
- 原题:How would deleting the P and V operations change the output? Why?
- 翻译:删除 P、V 操作会如何改变输出?为什么?
- 答案: 仍是 20 X、20 O 混杂,但连续的 X/O 可能偶可能奇。因为失去互斥,两进程并发又有停顿,随时可切换,结果不确定。
- 解析:去掉 P/V 后临界区不再受保护。
2024 程序阅读 2-3 --- 多线程生产者消费者写日志
c
sem_t empty, full; FILE *fp;
void *write_thread(void *id) { // 5 个写线程=生产者
int x; srand(getpid());
while (1) {
x = rand()%5; sleep(x);
sem_wait(&empty); // P(empty)
fprintf(fp, "thread %d write %d in log.file\n", (int)id, x);
sem_post(&full); // V(full)
}
}
void termination_handler(int signum) { // Ctrl+C(SIGINT) 触发
sem_destroy(&empty); sem_destroy(&full); fclose(fp); exit(0);
}
int main() { // 主线程=消费者(读)
sem_init(&empty, 0, 1); sem_init(&full, 0, 0);
signal(SIGINT, termination_handler);
fp = fopen("./log.file", "a+");
/* 创建 5 个 detached 写线程 */
for (;;) { sem_wait(&full); fseek(fp, offset*strlen(buf), SEEK_SET);
fgets(buf, sizeof(buf), fp); offset++;
printf("read from file: %s\n", buf); sem_post(&empty); }
}
真题 ・ 2024 程序阅读2-3(1)(2)
- 原题:How many threads? How many semaphores? Describe the synchronization.
- 翻译:创建多少线程?用几个信号量?描述同步过程。
- 答案: 6 个线程(5 写 + 1 读/主线程);2 个信号量 empty、full。5 写线程是生产者,1 读线程是消费者;empty(初1)/full(初0) 构成生产者---消费者同步。
- 解析:生产者 P(empty)/V(full),消费者 P(full)/V(empty)。
真题 ・ 2024 程序阅读2-3(3)(4)
- 原题:Trigger of termination_handler? What it does? Overall how does the code work?
- 翻译:termination_handler 的触发条件?它做什么?整体如何工作?
- 答案: 触发:Ctrl+C 产生 SIGINT(值2);该函数销毁 empty/full、关闭文件并退出。整体:5 线程写、1 线程读的标准生产者---消费者;文件以追加打开,用 offset+fseek 定位读取正确偏移。
- 解析:fseek 配合 offset 保证读到刚写入的内容。
真题 ・ 2024 判断(4)(5)
- 原题:(4) POSIX semaphore is auto-destroyed when the process ends. (5) System V semaphore is auto-destroyed when the process ends.
- 翻译:(4) POSIX 信号量在进程结束时自动销毁。(5) System V 信号量在进程结束时自动销毁。
- 答案: (4) F;(5) F。
- 解析:两者都不会自动销毁:命名 POSIX 需 sem_unlink,System V 需 semctl(IPC_RMID)。
考点六 多线程 pthread
【知识解析】 线程是进程内的执行流,同进程线程共享地址空间(全局变量、堆、文件描述符),各有独立栈。pthread_create 创建、pthread_join 等待回收、pthread_exit 退出;共享数据需信号量/互斥锁保护。可设 PTHREAD_CREATE_DETACHED 分离属性使线程结束自动回收。编译需加 -lpthread。
要点速记
- 共享:全局变量/堆/fd;独立:栈、寄存器。共享数据要加锁。
- pthread_create(&tid, attr, func, arg);pthread_join(tid, &ret) 等待结束。
- 严格交替输出可用两个信号量互相唤醒(一个初值 1、一个初值 0)。
2025 程序设计 1 --- 两线程严格交替输出 TJTJTJTJTJ(20 分)
官方《2025 参考答案》此题给的是 fork/重定向 模板,与题目不符;下面是本解析自行给出的正确解答。注意试卷把 sem_wait 注释成 V、sem_post 注释成 p 是写反了------标准语义 sem_wait=P、sem_post=V。
真题 ・ 2025 程序设计1
- 原题:Create two threads (same function, descriptors in a_thread2); thread1 prints arg1 5 times, thread2 prints arg2 5 times; with "T","J" output must be exactly TJTJTJTJTJ.
- 翻译:创建两个线程(同一函数,描述符存于 a_thread2);线程1输出参数1共5次、线程2输出参数2共5次;参数为 "T","J" 时输出必须严格为 TJTJTJTJTJ。
- 答案: 用两个信号量 s1(初1)、s2(初0) 互相唤醒实现严格交替。见下方代码。
- 解析:s1 初值 1 保证 T 先输出,输出后 V(s2) 唤醒 J;J 输出后 V(s1) 唤醒 T,交替 5 轮。
c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t s1, s2; char *arg1, *arg2;
void *thread_function(void *arg) {
int first = *(int*)arg; // 1=先手, 0=后手
for (int i = 0; i < 5; i++) {
if (first) { sem_wait(&s1); printf("%s", arg1); fflush(stdout); sem_post(&s2); }
else { sem_wait(&s2); printf("%s", arg2); fflush(stdout); sem_post(&s1); }
}
return NULL;
}
int main(int argc, char *argv[]) {
pthread_t a_thread[2]; arg1 = argv[1]; arg2 = argv[2];
sem_init(&s1, 0, 1); sem_init(&s2, 0, 0);
int f1 = 1, f0 = 0;
pthread_create(&a_thread[0], NULL, thread_function, &f1);
pthread_create(&a_thread[1], NULL, thread_function, &f0);
pthread_join(a_thread[0], NULL); pthread_join(a_thread[1], NULL);
sem_destroy(&s1); sem_destroy(&s2); return 0;
}
// ./a.out T J -> TJTJTJTJTJ
真题 ・ 2024 判断(3)
- 原题:In a Linux threaded environment, the resources shared by threads should be located at an address visible to all threads.
- 翻译:多线程环境中,线程共享的资源应位于所有线程都可见的地址处。
- 答案: T(对)。
- 解析:共享数据须放全局/堆等所有线程可见处,并配合加锁。
考点七 Socket 网络编程(TCP/IP)
【知识解析】 TCP 面向连接。服务器流程:socket→bind→listen→accept→read/write→close;客户端流程:socket→connect→write/read→close(客户端一般无需 bind)。SOCK_STREAM=TCP,SOCK_DGRAM=UDP;htons/htonl 做主机→网络字节序转换;accept 返回新的已连接套接字。
要点速记
- bind 第二参数是 (struct sockaddr*) 指针;accept 第三参数是 socklen_t* 地址长度指针。
- 客户端连接需与服务器相同的 IP 和端口。
- inet_addr/inet_pton 设置 IP;read/write 收发数据。
2025 程序设计 2 --- Socket 服务器填空 + 编写客户端(18 分)
c
int main() {
int sers, clis; struct sockaddr_in saddr, caddr; int clen; char buf[256];
sers = socket(AF_INET, SOCK_STREAM, 0);
saddr.sin_family = AF_INET; saddr.sin_port = 9880;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
clen = sizeof(caddr);
bind(sers, ____A____, sizeof(saddr));
listen(sers, 5);
while (1) {
clis = accept(sers, (struct sockaddr*)&caddr, ____B____);
read(clis, buf, 255); printf("read from cli:%s\n", buf);
write(clis, "helloa", 6); close(clis);
}
}
真题 ・ 2025 程序设计2(1)
- 原题:What should be filled at A and B?
- 翻译:A、B 两处分别填什么?
- 答案: A: (struct sockaddr*)&saddr;B: &clen(即 (socklen_t*)&clen)。
- 解析:bind 需 sockaddr 指针;accept 第三参数是地址长度指针。
真题 ・ 2025 程序设计2(2)
- 原题:Write a socket client that sends "hello" and prints the server response.
- 翻译:编写客户端,向服务器发送 "hello" 并打印响应。
- 答案: 流程 socket→connect→write("hello")→read→打印→close,IP/端口须与服务器一致。见下方代码。
- 解析:服务器回 "helloa",客户端读出打印;客户端无需 bind。
c
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int clis; struct sockaddr_in saddr; char buf[256];
clis = socket(AF_INET, SOCK_STREAM, 0);
saddr.sin_family = AF_INET; saddr.sin_port = 9880;
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(clis, (struct sockaddr*)&saddr, sizeof(saddr));
write(clis, "hello", 5);
memset(buf, '\0', 256); read(clis, buf, 255);
printf("read from server: %s\n", buf);
close(clis); return 0;
}
考点八 编译、Makefile 与信号
【知识解析】 gcc 常用选项:-c 只编译、-o 指定输出名、-I 指定头文件目录、-L 指定库目录、-l 链接库(-lfoo 链接 libfoo)。Makefile 规则由 目标、依赖、命令 组成,描述如何由源文件构建目标。signal(signum, handler) 注册信号处理函数;Ctrl+C 产生 SIGINT(值2),常用于资源清理。
要点速记
- 头文件目录用 -I,库目录用 -L,链接库用 -l;三者不要混淆。
- Makefile:target: prerequisites \n commands。
- 常见信号:SIGINT(2, Ctrl+C)、SIGKILL(9)、SIGTERM(15);信号处理函数里做清理后退出。
该考点真题
真题 ・ 2024 判断(1)
- 原题:To compile foo.c needing libfoo.so in /home/stu/lib, the correct command is: gcc foo.c -I/home/stu/lib -lfoo -o foo.
- 翻译:编译需要 /home/stu/lib 下 libfoo.so 的 foo.c,正确命令为:gcc foo.c -I/home/stu/lib -lfoo -o foo。
- 答案: F(错)。
- 解析:库目录应用 -L 而非 -I;正确为 gcc foo.c -L/home/stu/lib -lfoo -o foo。-I 是头文件目录。
真题 ・ 2024 判断(2)
- 原题:A rule in the makefile tells Make how to execute commands to build a target from source files.
- 翻译:makefile 中的规则告诉 Make 如何执行命令、从源文件构建目标。
- 答案: T(对)。
- 解析:Makefile 规则 = 目标 + 依赖 + 命令。
真题 ・ 2024 程序阅读2-3 信号部分
- 原题:Give the trigger condition for termination_handler(int signum) and what it does.
- 翻译:给出 termination_handler(int signum) 的触发条件及其作用。
- 答案: 触发:Ctrl+C 产生 SIGINT(值2);作用:销毁信号量 empty/full、关闭共享文件、退出。
- 解析:通过 signal(SIGINT, termination_handler) 注册,用于收到中断时清理资源。
本文档把 2024、2025 两套真题按考点重新归类,并配知识解析。答案以官方为准,个别笔误/与试卷不符处已在解析中标注修正。