安工大Linux考点分类真题解析(含知识点是试卷原题了)

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 两套真题按考点重新归类,并配知识解析。答案以官方为准,个别笔误/与试卷不符处已在解析中标注修正。

相关推荐
且从容.3 小时前
Ubuntu安装flume
linux·ubuntu·flume
承渊政道3 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
讯捷蓝达5 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
且从容.6 小时前
ubuntu安装flink
linux·ubuntu·flink
数字新视界7 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落7 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
程序员沉梦听雨7 小时前
WSL2的安装与使用
linux·wsl
微小的鱼8 小时前
Linux的渲染与显示系统链路
android·java·linux
lisanmengmeng8 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
严肃小白兔8 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化