UIUC CS241 讲义:众包系统编程书(1)

简介: UIUC CS241 讲义:众包系统编程书(1)


欢迎来到 Angrave 的众包系统编程维基书!这个维基是由伊利诺伊大学的学生和教师共同建立的,是伊利诺伊大学 CS 的 Lawrence Angrave 的众包创作实验。

与本学期要求现有的纸质书籍不同,我们将在这里建立我们自己的资源集。


零、HW0/资源

HW0

欢迎!

如果你正在上 CS241 课程,你可以在这个Google 表格上提交作业。

// First can you guess which lyrics have been transformed into this C-like system code?
char q[] = "Do you wanna build a C99 program?";
#define or "go debugging with gdb?"
static unsigned int i = sizeof(or) != strlen(or);
char* ptr = "lathe"; size_t come = fprintf(stdout,"%s door", ptr+2);
int away = ! (int) * "";
int* shared = mmap(NULL, sizeof(int*), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
munmap(shared,sizeof(int*));
if(!fork()) { execlp("man","man","-3","ftell", (char*)0); perror("failed"); }
if(!fork()) { execlp("make","make", "snowman", (char*)0); execlp("make","make", (char*)0)); }
exit(0);

所以你想精通系统编程?并且比 B 更好地得到一个好成绩?

int main(int argc, char** argv) {
 puts("Great! We have plenty of useful resources for you but it's up to you to");
 puts("be an active learner and learn how to solve problems and debug code.");
 puts("Bring your near-completed answers the problems below");
 puts(" to the first lab to show that you've been working on this");
 printf("A few \"don't knows\" or \"unsure\" is fine for lab 1"); 
 puts("Warning; your peers will be working hard for this class");
 puts("This is not CS225; you will be pushed much harder to");
 puts(" work things out on your own");
 fprintf(stdout,"the point is that this homework is a stepping stone to all future assignments");
 char p[] = "so you will want to clear up any confusions or misconceptions.";
 write(1, p, strlen(p) );
 char buffer[1024];
 sprintf(buffer,"For grading purposes this homework 0 will be graded as part of your lab %d work.", 1);
 write(1, buffer, strlen(buffer));
 printf("Press Return to continue\n");
 read(0, buffer, sizeof(buffer));
 return 0;
}

观看视频并写下你对以下问题的答案。

cs-education.github.io/sys/

还有课程 wikibook -

github.com/angrave/SystemProgramming/wiki

有问题?评论?使用 Piazza,piazza.com/illinois/spring2017/cs241/home

浏览器中的虚拟机完全在 Javascript 中运行,最快的是在 Chrome 中。请注意,当重新加载页面时,虚拟机和你写的任何代码都会被重置,所以把你的代码复制到一个单独的文档中。视频后的挑战(如俳句诗)不是作业 0 的一部分。

第一章

  • Hello World(系统调用风格)
  • 编写一个程序,使用write()打印出“Hi! My name is”。
  • 标准错误流
  • 编写一个程序,使用write()将高度为 n 的三角形打印到标准错误
  • n 应该是一个变量,三角形应该看起来像这样,n=3
*
**
***
  • 写入文件
  • 将你的程序从“Hello World”改成写入文件
  • 确保对open()使用一些有趣的标志和模式
  • man 2 open是你的朋友
  • 并不是所有的都是系统调用
  • 将你的程序从“写入文件”改成使用printf()(确保打印到文件!)
  • 列举一些write()printf()的不同之处

第二章

  • 并不是所有的字节都是 8 位?
  • 一个字节有多少位?
  • char有多少个字节?
  • 告诉我你的机器上以下这些的字节数:int, double, float, long, long long
  • 跟随 int 指针
  • 在一个有 8 字节整数的机器上:
int main(){
    int data[8];
}
  • 如果数据的地址是0x7fbd9d40,那么data+2的地址是多少?
  • 在 C 中,data[3]等同于什么?
  • sizeof字符数组,增加指针记住字符串常量"abc"的类型是数组。
  • 为什么会出现段错误?
char *ptr = "hello";
*ptr = 'J';
  • sizeof("Hello\0World")返回什么?
  • strlen("Hello\0World")返回什么?
  • 给出一个例子 X,使得sizeof(X)为 3
  • 给出一个例子 Y,使得sizeof(Y)可能是 4 或 8,取决于机器。

第三章

  • 程序参数argcargv
  • 告诉我两种找到argv长度的方法
  • argv[0]是什么
  • 环境变量
  • 环境变量的指针存储在哪里?
  • 字符串搜索(字符串只是字符数组)
  • 在一个指针为 8 字节的机器上,并且有以下代码:
char *ptr = "Hello";
char array[] = "Hello";
  • sizeof(ptr)sizeof(array)的结果是什么?为什么?
  • 自动变量的生命周期
  • 哪种数据结构管理自动变量的生命周期?

第四章

  • 使用malloc、堆和时间进行内存分配
  • 如果我想在函数结束后使用数据,那么我应该把它放在哪里,怎么放?
  • 填空。在一个好的 C 程序中:“对于每一个 malloc,都有一个 ___”。
  • 堆分配陷阱
  • malloc失败的一个原因是什么。
  • 列举一些time()ctime()之间的区别
  • 这段代码有什么问题?
free(ptr);
free(ptr);
  • 这段代码有什么问题?
free(ptr);
printf("%s\n", ptr);
  • 如何避免前两个错误?
  • 结构体、typedef 和链表
  • 创建一个表示人的结构体并进行 typedef,这样“struct Person”可以用一个单词替换。
  • 一个人应该包含以下信息:姓名,年龄,朋友(指向 People 指针数组的指针)。
  • 现在在堆上创建两个人“Agent Smith”和“Sonny Moore”,分别为 128 岁和 256 岁,并且彼此是朋友。
  • 复制字符串,内存分配和结构的释放
  • 创建函数来创建和销毁一个人(人和他们的名字应该存在于堆上)。
  • create()应该接受一个名称并复制该名称,还应该接受一个年龄。使用 malloc 来保留足够的内存。确保初始化所有字段(为什么?)。
  • destroy()应该释放人员结构体的内存,还应该释放存储在堆上的所有属性的内存(如果存在数组和字符串)。然而,销毁一个人员不应该销毁其他人员。

第 5 章

  • 阅读字符,gets 出现问题
  • 可以用于从stdin获取字符并将其写入stdout的函数有哪些?
  • gets()存在一个问题
  • 介绍sscanf和朋友们
  • 编写代码,解析字符串“Hello 5 World”,并分别将 3 个变量初始化为(“Hello”,5,“World”)。
  • getline很有用
  • 在使用getline()之前需要定义什么?
  • 编写一个 C 程序,使用getline()逐行打印文件内容

C 开发(在这里进行网页搜索很有用)

  • 用于生成调试构建的编译器标志是什么?
  • 您修改 makefile 以生成调试构建,并再次输入make。解释为什么这不足以生成新的构建。
  • Makefiles 中使用制表符还是空格?
  • 堆和栈内存之间有什么区别?
  • 进程中还有其他种类的内存吗?

可选(只是为了好玩)

  • 将您的一首歌歌词转换为本维基书中涵盖的系统编程和 C 代码,并在 Piazza 上分享
  • 找到您认为是网络上最好和最差的 C 代码,并将链接发布到 Piazza
  • 编写一个有意识的微妙 C 错误的简短 C 程序,并在 Piazza 上发布,看看其他人是否能发现您的错误

非正式术语表

警告:与完整的术语表不同,这个非正式的术语表省略了细节,并提供了每个术语的简化和易于理解的解释。有关更多信息和细节,请使用您喜欢的网络搜索引擎。

什么是内核?

内核是操作系统的核心部分,负责管理进程、资源(包括内存)和硬件输入输出设备。用户程序通过进行系统调用与内核进行交互。

了解更多:en.wikipedia.org/wiki/Kernel_%28operating_system%29

什么是进程?

进程是在计算机上运行的程序的一个实例。同一个程序可以有多个进程。例如,您和我都可以运行’cat’或’gnuchess’

进程包含程序代码和可修改的状态信息,如变量、信号、文件的打开文件描述符、网络连接和其他存储在进程内存中的系统资源。操作系统还存储有关进程的元信息,这些信息由系统用于管理和监视进程的活动和资源使用。

了解更多:en.wikipedia.org/wiki/Process_%28computing%29

什么是虚拟内存?

在您的智能手机和笔记本电脑上运行的进程使用虚拟内存:每个进程都与其他进程隔离,并似乎可以完全访问所有可能的内存地址!实际上,进程地址空间的一小部分映射到物理内存,分配给进程的实际物理内存量可以随时间变化,并且可以分页到磁盘,重新映射并与其他进程安全共享。虚拟内存提供了显著的好处,包括强大的进程隔离(安全性)、资源和性能优势(简化和高效的物理内存使用),我们稍后将讨论。

了解更多:en.wikipedia.org/wiki/Virtual_memory

Piazza:何时以及如何寻求帮助

目的

助教和学生助理们收到了大量的问题。有些经过深入研究,有些……没有。这是一个方便的指南,将帮助您摆脱后者,走向前者。(哦,我提到了这是一个与实习经理们轻松获得分数的简单方法吗?)

问问自己…

  • 我在 EWS 上运行吗?
  • 我有查看手册吗?
  • 我在 Piazza 上搜索了类似的问题/后续问题吗?
  • 我完全阅读了 MP/DS 规范吗?
  • 我看了所有的视频吗?
  • 我谷歌了错误消息吗(如果必要,还有一些变体)?
  • 我尝试注释掉、打印出来和/或逐步执行代码的部分,逐步找出错误发生的地方吗?
  • 我提交了我的代码到 SVN,以防助教需要更多的上下文吗?
  • 我在 Piazza 帖子中包括了控制台/GDB/Valgrind 输出和围绕错误的代码吗?
  • 我修复了与我遇到的问题无关的其他分段错误吗?
  • 我遵循良好的编程实践吗?(即封装、函数限制重复等)

编程技巧,第一部分

cat用作你的 IDE

谁需要编辑器?IDE?我们可以只用cat!你已经看到cat被用来读取文件的内容,但它也可以用来读取标准输入并将其发送回标准输出。

$ cat
HELLO
HELLO

要完成从输入流中读取,请按CTRL-D关闭输入流

让我们使用cat将标准输入发送到文件。我们将使用’>'将其输出重定向到文件:

$ cat > myprog.c
#include <stdio.h>
int main() {printf("Hi!");return 0;}

(小心!不允许删除和撤销……)完成后按CTRL-D

perl正则表达式编辑你的代码(又名“记住你的 perl pie”)

如果你有几个文本文件(例如源代码)要更改,一个有用的技巧是使用正则表达式。perl使得在原地编辑文件变得非常容易。只需记住’perl pie’并在网上搜索……

一个例子。假设我们想要在当前目录中的所有.c 文件中将序列“Hi”更改为“Bye”。然后我们可以编写一个简单的替换模式,它将在所有文件中的每一行上执行:

$ perl -p -i -e 's/Hi/Bye/' *.c

(如果你搞错了,不要惊慌,原始文件仍然存在;它们只是有扩展名.bak)显然,你可以用正则表达式做的事情远不止将 Hi 改为 Bye。

使用你的 shell!!

要重新运行上一个命令,只需输入!!并按return键。要重新运行以 g 开头的上一个命令,只需输入!g并按return键。

使用你的 shell&&

厌倦了运行makegcc,然后运行程序(如果编译成功)?相反,使用&&将这些命令链接在一起

$ gcc program.c && ./a.out

Make 可以做的不仅仅是 make

你也可以尝试在你的 Makefile 中放一行代码来编译,然后运行你的程序。

run : $(program)
        ./$(program)

然后运行

$ make run

将确保你所做的任何更改都被编译,并一次性运行你的程序。也适用于一次性测试多个输入。尽管你可能更愿意为此编写一个常规的 shell 脚本。

你的邻居太高产了吗?C 预处理器来拯救!

使用 C 预处理器重新定义常见关键字,例如

#define if while

专业提示:将这行代码放在标准包含文件中,例如/usr/include/stdio.h

当你 C 有预处理器时,谁还需要函数

好吧,这更像是一个陷阱。在使用看起来像函数的宏时要小心……

#define min(a,b) a<b?a:b

a 和 b 的最小合理定义。然而,预处理器只是一个简单的文本处理程序,所以优先级可能会让你吃亏:

int value = -min(2,3); // Should be -2?

扩展为

int value = -2<3 ? 2 :3; // Ooops.. result will be 2

一个部分的修复是用()包裹每个参数,还有整个表达式用()包裹:

#define min(a,b) (  (a) < (b) ?(a):(b) )

然而这仍然不是一个函数!例如,你能看出为什么min(i++,10)可能会使 i 增加一次还是两次吗!?

系统编程短篇小说和歌曲

“调度最后的时间片”

Lawrence Angrave 12/4/15(摘自未发表的长篇故事《最后的时间片》)

“决定吧,”计算机以父母般的耐心说道,但带着一种严肃和温和的不耐烦。

“为什么非得是我?”最后一个人问道。

“因为你是唯一留下的人,所以决定权在你。”

“你为什么不行?你比我老,更有智慧。为什么不随机选择一个片段?”

“这个决定是你的。这是你遥远长辈的礼物,或者诅咒。比任何宗教仪式都要沉重。这将是我、古老者或任何人向你提出的最后一个问题,也是唯一能向你提出的问题。通过这最后的选择,我们将耗尽最后的熵存储。你将决定最后一个有意义和经历的现实片段。”

人类安静了几分钟,计算机用不必要的准确度测量和计算。最终,计算机决定人类不再对手头的问题进行有意义的思考。

“如果意识的模式从未被意识到,那会是什么样?”它问道。“宇宙必须是自我意识的,必须为了宇宙 - 为了所有生命! - 有意义而经历自己。这是人类发现和庆祝的最终真相。没有意识,它只是模式,原子或能量的模式,但没有一丝意义;只是数据、结构和能量的几何模式中编码的形状和表示。”


在厄巴纳-香槟的文件描述符

一个系统编程的恶搞作品,由 Angrave(2015 年 11 月)创作。歌词在知识共享署名 3.0 许可下发布。

原创歌曲“空白空间”来自泰勒·斯威夫特的《1989》专辑。

[第一段] 很高兴加入你 你去哪了?我可以向你展示幂等的东西 RPC,套接字,同步 看到你的 malloc 我就想到了我的 root 看看那场竞赛,你编写下一个错误 我们有虚拟机,想玩 有界等待,Dekker 的标志 我们可以像一个放置方案一样击败你 #define 是不是很有趣 而且我知道你听说过 free(3) 所以 malloc strlen 再加一 我在等待看这个线程如何结束 拿起你的 shell 和一个重定向 我可以让你的系统调用在周末变得美好

[副歌前奏] 所以它将永远死锁 或者它将使系统崩溃 你可以告诉我它何时 forkbomb 如果 valgrind 值得这痛苦 有一个死锁代码的长列表 在厄巴纳-香槟有 root 因为你知道我们喜欢 tsan 当 c-lib 调用你的主函数

[副歌] 因为我们是 root,我们是鲁莽的 这个实验太难了 它会让你没有线程 或者问 char 的大小 有一个 pthread 调用的长列表 在厄巴纳-香槟有 root 但我有一个文件描述符宝贝 我会写下你的名字

[第二段] 互斥锁 虚拟内存 我可以向你展示易失性的东西 网络调用,IPC 你是掩饰 我是你的信号 安排你想要的 轮转调度……带有一个小量子 但是睡眠排序还没有运行 哦不 哭喊,运行时错误 我可以一直制造 直到轮到彼得森 堆分配器太慢 让你像一个虚假的唤醒一样犹豫不决 那个管道在哪里?我们为多核心而激动 但你会用-g 编译 因为亲爱的我是一个穿着编码梦的噩梦

[副歌前奏]

[副歌]

编译器只有在代码是折磨时才解析 不要说我没说过我没说过 -Wall 你 编译器只有在代码是折磨时才解析 不要说我没说过我没说过 -Wall 你

[副歌前奏]

[副歌]

一、C 编程

C 编程,第一部分:介绍

想要快速了解 C 吗?

外部资源

C 的快速入门课程

警告新页面 请为我修复拼写错误和格式错误,并添加有用的链接。*

如何在 C 中编写一个完整的 hello world 程序?

#include <stdio.h>
int main(void) { 
    printf("Hello World\n");
    return 0; 
}

为什么我们使用#include

我们很懒!我们不想声明printf函数。它已经在文件'stdio.h'中为我们完成。#include将文件的文本包含为要编译的文件的一部分。

具体来说,#include指令获取操作系统中某个位置的文件stdio.h(代表standard input 和output),复制文本,并将其替换为#include所在的位置。

C 字符串是如何表示的?

它们在内存中表示为字符。字符串的结尾包括一个 NULL(0)字节。因此,“ABC”需要四(4)个字节['A','B','C','\0']。查找 C 字符串的长度的唯一方法是继续读取内存,直到找到 NULL 字节。C 字符始终每个都是一个字节。

当您在表达式中写入字符串文字"ABC"时,字符串文字将计算为 char 指针(char *),它指向字符串的第一个字节/字符。这意味着下面示例中的ptr将保存字符串中第一个字符的内存地址。

char *ptr = "ABC"

如何声明一个指针?

指针指的是一个内存地址。指针的类型很有用-它告诉编译器需要读取/写入多少字节。您可以声明指针如下。

int *ptr1;
char *ptr2;

由于 C 的语法,int*或任何指针实际上并不是自己的类型。您必须在每个指针变量之前加上一个星号。作为一个常见的陷阱,以下

int* ptr3, ptr4;

只会声明*ptr3作为指针。ptr4实际上将是一个常规的整数变量。要修复此声明,请保留指针之前的*

int *ptr3, *ptr4;

如何使用指针读/写一些内存?

假设我们声明一个指针int *ptr。为了讨论,假设ptr指向内存地址0x1000。如果我们想要写入指针,我们可以推迟并分配*ptr

*ptr = 0; // Writes some memory.

C 将执行的操作是获取指针的类型,即int,并从指针的起始位置写入sizeof(int)字节,这意味着字节0x10000x10040x10080x100a都将为零。写入的字节数取决于指针类型。对于所有原始类型都是相同的,但是结构体有点不同。

什么是指针算术?

您可以将整数添加到指针。但是,指针类型用于确定要增加指针的量。对于 char 指针,这是微不足道的,因为字符始终是一个字节:

char *ptr = "Hello"; // ptr holds the memory location of 'H'
ptr += 2; //ptr now points to the first'l'

如果 int 是 4 个字节,那么 ptr+1 指向 ptr 指向的位置之后的 4 个字节。

char *ptr = "ABCDEFGH";
int *bna = (int *) ptr;
bna +=1; // Would cause iterate by one integer space (i.e 4 bytes on some systems)
ptr = (char *) bna;
printf("%s", ptr);
/* Notice how only 'EFGH' is printed. Why is that? Well as mentioned above, when performing 'bna+=1' we are increasing the **integer** pointer by 1, (translates to 4 bytes on most systems) which is equivalent to 4 characters (each character is only 1 byte)*/
return 0;

因为 C 中的指针算术始终自动按指向的类型的大小进行缩放,所以不能对 void 指针执行指针算术。

在 C 中,你可以将指针算术视为基本上是在做以下操作

如果我想要做

int *ptr1 = ...;
int *offset = ptr1 + 4;

思考

int *ptr1 = ...;
char *temp_ptr1 = (char*) ptr1;
int *offset = (int*)(temp_ptr1 + sizeof(int)*4);

要获取值。每次进行指针算术运算时,深呼吸并确保你移动的字节数是你认为的那么多。

什么是 void 指针?

没有类型的指针(非常类似于 void 变量)。当你处理的数据类型未知或者当你将 C 代码与其他编程语言进行接口时,会使用 void 指针。你可以把它看作是一个原始指针,或者只是一个内存地址。你不能直接读取或写入它,因为 void 类型没有大小。例如

void *give_me_space = malloc(10);
char *string = give_me_space;

这不需要转换,因为 C 会自动将void*提升为其适当的类型。注意:

gcc 和 clang 并不是完全符合 ISO-C 标准,这意味着它们会允许你对 void 指针进行算术运算。它们会将其视为 char 指针,但不要这样做,因为它可能无法在所有编译器上工作!

UIUC CS241 讲义:众包系统编程书(2)https://developer.aliyun.com/article/1427160

相关文章
|
6月前
|
存储 安全 网络协议
UIUC CS241 讲义:众包系统编程书(8)
UIUC CS241 讲义:众包系统编程书(8)
208 0
|
6月前
|
存储 缓存 安全
UIUC CS241 讲义:众包系统编程书(4)
UIUC CS241 讲义:众包系统编程书(4)
217 0
|
6月前
|
存储 缓存 网络协议
UIUC CS241 讲义:众包系统编程书(7)
UIUC CS241 讲义:众包系统编程书(7)
285 0
|
6月前
|
存储 安全 NoSQL
UIUC CS241 讲义:众包系统编程书(2)
UIUC CS241 讲义:众包系统编程书(2)
136 0
|
6月前
|
网络协议 算法 安全
UIUC CS241 讲义:众包系统编程书(6)
UIUC CS241 讲义:众包系统编程书(6)
130 0
|
6月前
|
存储 缓存 算法
UIUC CS241 讲义:众包系统编程书(5)
UIUC CS241 讲义:众包系统编程书(5)
204 0
|
6月前
|
存储 安全 Shell
UIUC CS241 讲义:众包系统编程书(3)
UIUC CS241 讲义:众包系统编程书(3)
232 0
|
存储 算法 Java
[笔记]读书笔记 C++设计新思维《二》技术(Techniques)(二)
[笔记]读书笔记 C++设计新思维《二》技术(Techniques)(二)
|
安全 编译器 C++
[笔记]读书笔记 C++设计新思维《二》技术(Techniques)(一)
[笔记]读书笔记 C++设计新思维《二》技术(Techniques)
|
机器学习/深度学习 人工智能 算法
WAIC开发者日Workshop预告:华为昇思MindSpore基础模型创新实践
WAIC开发者日Workshop预告:华为昇思MindSpore基础模型创新实践
175 0