提交要求

  1. 本页面中的习题包括文档与代码,所有题目均写在同一份文档中,并导出为 PDF 格式,命名为 SynX 招新-后端类题目-20xxxxxxxxxxx-XXX.pdf。请注意排版。要求提交代码的题目请将代码放在单独的文件夹中。提交时要求将其打包成 ZIP 压缩包,命名为SynX 招新-后端类题目-<学号>-<姓名>.zip,例如:
    SynX 招新-后端类题目-20xxxxxxxxxxx-XXX.zip
    ├── SynX 招新-后端类题目-20xxxxxxxxxxx-XXX.pdf.pdf
    ├── 奇怪的 C 语言语法
    |   ├── xxx.c
    │   └── xxx.c
    └── 奇怪的内存排列
       └── xxx.c

    代码的文件名自拟,同时在文档中标注代码文件名所进行的工作。

  2. ZIP 压缩包通过邮箱提交,邮箱为:[email protected],邮件的主题为 SynX 招新-后端类题目-<学号>-<姓名>
  3. 题目提交的截止时间为 2022 年 10 月 9 日 24:00,在截止时间之前,可以无限次的提交。
  4. 本页面的题目不要求全部做出来,但请尽量都看一看,完成感兴趣的题目即可。
  5. 题目在完成的过程中可以利用互联网、教材及包括但不限于图书馆的各种图书资源,也可以与他人交流。遇到困难请一定不要闭门造车,积极讨论,或是向出题人寻求帮助。

问答题

  1. [blame LAuemtihneer]与 Windows 操作系统不同,Linux 操作系统可以删除正在被进程使用的文件。如果删除了这样的文件,进程还能正常工作吗?磁盘空间是否会被释放?如何找到这样的文件?
  2. [blame LAuemtihneer]下图来自计算性能和云计算方面的专家 Brendan Gregg,这张图描述展示了 Linux 操作系统中性能观测工具及其观测的系统组件。请至少选出 3 个你使用过的或感兴趣的,说一说你曾经用它观测过什么,或是它们更具体的观测内容。
    Linux Performance Observability Tools
  3. [blame LAuemtihneer]请尝试对比 WebSocket 与 stream socket 通信、WebSocket 与 HTTP 的关系与区别。
  4. [blame LAuemtihneer]阅读《凤凰架构》演进中的架构章节,列出其中提到的每一种体系架构,并指出每一种新体系架构解决了前者的什么问题,又引入了什么新的问题

代码阅读题

生产者与消费者

[blame LAuemtihneer]

下面的代码是一个典型的生产者消费者为问题,临界资源是缓冲区剩余的资源数 counter,生产者线程在临界区中会对 counter 的值加 1,若值已满(counter 的值为 3),生产者会等待条件变量,直到消费者进行了一些消费;消费者线程的工作与之相反,若 counter 值为 0,则等待直到生产者线程进行了一些生产。仔细阅读下面的代码,下面的代码是否存在缺陷?若有缺陷则请指出并修改。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#define MAXSIZE 3

int counter = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t not_full = PTHREAD_COND_INITIALIZER;

void *producer(void *arg);
void *consumer(void *arg);

int main(void) {
    pthread_t pthread_id1, pthread_id2;

    srand(time(NULL));

    pthread_create(&pthread_id1, NULL, producer, "producer");
    pthread_create(&pthread_id2, NULL, consumer, "consumer");

    pthread_join(pthread_id1, NULL);
    pthread_join(pthread_id2, NULL);
    return 0;
}

void *producer(void *arg) {
    for (;;) {
        sleep(rand() % 3 + 1);   // 模拟线程需要一点时间生产资源
        pthread_mutex_lock(&lock);
        if (counter == MAXSIZE) {
            printf("[%s] full, ready to block.\n", (char *) arg);
            pthread_cond_wait(&not_full, &lock);
        }
        // 将生产的资源放入缓冲区
        printf("[%s] counter=%d\n", (char *) arg, ++counter);
        pthread_cond_signal(&not_empty);
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}

void *consumer(void *arg) {

    for (;;) {
        pthread_mutex_lock(&lock);
        if (counter == 0) {
            printf("[%s] empty, ready to block.\n", (char *) arg);
            pthread_cond_wait(&not_empty, &lock);
        }
        // 将缓冲区的资源取出以待消费
        printf("[%s] counter=%d\n", (char *) arg, --counter);
        pthread_cond_signal(&not_full);
        pthread_mutex_unlock(&lock);
        sleep(rand() % 3 + 1); // 模拟线程需要一点时间消费资源
    }
    return NULL;
}

综合题

奇怪的 C 语言语法

[blame LAuemtihneer]

Linux 内核源码的 include/linux/container_of.h 文件下第 17-22 行定义了一个宏,宏的名字叫 container_of

https://github.com/torvalds/linux/blob/v5.16/include/linux/container_of.h#L17-L22

#define container_of(ptr, type, member) ({                \
        void *__mptr = (void *)(ptr);                     \
        ((type *)(__mptr - offsetof(type, member))); })

static_assert 那三行是用于进行错误处理的,删去不会影响代码正常逻辑。

  1. 这一段代码中出现了一种奇怪的 C 语言语法:括号围绕着大括号。这种语法无论是在教材(A Modern Approach)中,还是在 ISO C99、C11 的标准中都不存在。因此,请
    1. 借助搜索引擎或其他工具,找到这种语法的名字(中文及英文),及其在线的官方文档。
    2. 这种语法能解决什么问题?请尝试写出两段代码对比,能体现使用这种语法的好处。
  2. offsetof 的原型为:
    #define offsetof(TYPE, MEMBER)        ((size_t)&(((TYPE *)0)->MEMBER))

    阅读这两段代码,并

    1. 分析这两段代码进行的工作,指出 container_ofoffsetof 的返回值分别是什么,并尝试用图形的方式将其体现出来。
    2. 写一段代码,其中用到了宏 container_of

奇怪的内存排列

[blame LAuemtihneer]

现代计算机的内存以字节为基本单位,一个字节有 8 个 bit,一个 short 型的变量占用 2 个字节。现在我们想知道一个 short 型的变量在内存中到底是如何存储的,我们可以使用地址与指针,把 short 类型的变量解释成单字节的字符组成的数组,并利用字符串数组输出每个内存的值:

#include <stdio.h>

int main(void) {
    short num = 0x0102;
    printf("%04x\n", num);
    char *ch = &num;
    printf("%02x%02x\n", ch[0], ch[1]);

    return 0;
}

编写好程序后,编译并运行,得到结果:

$ gcc memory.c 
memory.c: In function ‘main’:
memory.c:6:16: warning: initialization of ‘char *’ from incompatible pointer type ‘short int *’ [-Wincompatible-pointer-types]
    6 |     char *ch = &num;
      |                ^
$ ./a.out 
0102
0201
$

问题:

  1. 使用 gcc 进行编译的时候,gcc 报告了一个 warning,请尝试修改代码,使 warning 消失。
  2. 我们希望两个输出都是 0102,但事实与我们期望的相反,short 类型的数据在计算机内存中的存储方式是 0201(在当前大部分计算机中是如此)。请借助搜索引擎或其他工具,找到这一结果的原因。
  3. C 语言有一个语法,通过它可以不通过指针,实现与上面程序相同的行为:将一块内存解释成不同的数据类型。这种语法叫什么?请通过这种语法实现与上述程序相同的结果。
  4. 请找到一种环境,可以在编译上面的代码并运行后,得到的结果都是 0102.

CI

[blame LAuemtihneer]

CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 弥补了开发与测试、运维之间的鸿沟。想象这样一种情景,因为增加功能或修理 bug,我们修改了代码,下一步就是要测试、编译、打包,最后对程序进行部署或分发。后续这些操作可能会是非常麻烦的,而 CI 就是这样一套自动化工具,我们在写完代码后只需要提交代码,后续的测试、编译、打包、部署或分发工作就都会自动化的进行。

GitHub CIGitLab CI 是当前互联网非常流行的 CI 工具,而且他们提供了不少免费的额度。思考什么样的应用适合 CI/CD 场景,并通过一个应用,对这个应用通过 GitHub CI 或 GitLab CI 实现自动化的工作,应用的内容自拟。如果你做了前端类题目,那么你可以给你的前端程序加上自动化 CI。当然,一个 C 语言程序、Python 程序,乃至 \LaTeX 的文档的编译都可以使用 Ci 完成。

本节仅需要提供文档:

  1. 指出什么样的应用适合 CI;
  2. 实现一个应用的 CI 化,并将其 GitHub/GitLab 链接附在文档上。

你还可以:

  • 如果你的开发设备性能足够,那么你可以尝试在本地部署开源的 CI 环境,例如 GitLab CI(GitLab 及 GitLab Runner)、Jenkens 等,并实现应用的 CI/CD。
  • 如果你的开发设备性能相当足够,那么你可以尝试在本地建立 Kubernetes 集群,在本地的 Kubernetes 中实现 CI/CD。

当然,你进行的工作需要在文档中体现。


0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注