所用教材:徐钦桂、徐治根、黄培灿、谢伟鹏(编著) 出版社:清华大学出版社
实战和理论相结合的一本好书,但也很晦涩抽象。下方有考试原题!
常问:书中有些Linux-C程序实例的头文件(如wrapper.h)在编译时报“无法找到”?
解决方法:程序里用到哪些函数,就用【man命令+函数名】查包含此函数的头文件(来替换wrapper.h)
1. 写出20条命令。 P24
su ls touch tar gcc mkdir rmdir chmod apt wc cd pwd cp kill mv grep cat more less find remove read ln rm
cd:切换目录
Is:列出目录下的文件
mv:移动/重命名文件/目录 rm:删除文件
mkdir:创建目录 rmdir:删除目录(但文件夹不为空时无法执行)
cat:显示文件的内容(Concatenate)
find:在指定目录下查找文件
pwd:以绝对路径的方式显示用户当前工作目录
rm -参数:删除N个文件/整个目录
touch:创建新的空文件
cp:复制文件/目录
vi:修改文件内容
echo:创建/覆盖文件
tar:文件打包/解压
scp:远程拷贝文件(Secure copy)
2. 写出shell脚本的执行方法。 P27
3. 说明linux程序的执行时间包括哪些部分。 P110
用户态:执行用户地址空间中的指令
内核态:执行内核地址空间中的指令
睡眠:执行其他进程的时间
实用时间:用户态+内核态
真实时间:用户态+内核态+睡眠
在Linux系统中,进程以时间片的形式分享CPU;同时,当进程被调度进入运行状态时,进程的执行有两种运行模式,用户态和内核态。当进程执行的是用户地址空间中的代码时,我们称进程运行于用户态;当进程进入系统调用或陷入硬件中断时,则称进程处于内核态。因此,可以从不同的角度为进程计时。
进程并非每时每刻都在运行,而是在用户态、内核态和休眠态之间切换。
4. Shell脚本编程。
(1)写一个脚本计算整数1至1000的和
#!/bin/bash
sum=0
for i in `seq 1 1000`
do
sum=$[$i+$sum]
done
echo $sum
运行结果:
bash oneto1000add.sh
500500
(2)写一个脚本计算整数1至1000的乘积
测试了累乘到更大的数,溢出了最大值,结果显示为0。测试1到10的乘积,结果正确
#!/bin/bash
var=1
for i in {1..10}
do
var=$[$var*$i]
done
echo $var
运行结果:
bash oneto1000mi.sh
3628800
5. Linux自带的库函数有哪些类型?P68
包括输入输出、数学运算、字符串处理、时间日期、环境控制、内存分配、多线程并发、数据结构算法在内的很多系统函数
(1)数学函数(math.h, libm.so, libm.a)
pow(x,y)、sqrt(x)、exp(x)、log(x)、log10(x)、ceil(x)、floor(x)、fabs(x),sin、cos、tan、ctan、cosh、tanh、cosh
(2)环境控制函数
getenv\setenv\unsetenv
(3)字符串处理函数
strcat,strcpy,strncpy,bcopy,memcpy,strcmp,strncmp,strcasecmp,strncasecmp,bzero,memset,index,strchr,rindex,strrchr,memchr,memrchr,strstr,strcasestr,strtok,strupr,atoi,strtol,strtod
(4)时间函数
time , asctime, ctime
(5)数据结构算法函数
二分搜索: bsearch;线性搜素: lfind,lsearch;快速排序: qsort;二叉树算法: tsearch,tfind,twalk,tdelete,tdestroy
6. 列出linux操作系统中文件的类型。 P13
Linux系统在文件目录列表中用以下字符表示这些文件:
-、常规文件 d、目录文件 c、字符设备文件 b、块设备文件 p、管道文件 l、符号链接文件 s、套接字文件
7. 写出linux系统向进程发送信号的几种机制。 P188
(1)用/bin/kill发送信号
(2)从键盘发送信号
(3)用kill和raise函数发送信号
(4)用alarm函数发送信号
1. 列出与文件I/O操作相关的应用编程接口。
①open
int fd = open(char* filename,int flags,mode_t mode);
②close
int close(int fd);
③lseek
off_set lseek(int fd,off_t offset,int whence);
④read
ssize_t read(int fd,void *buf,size_t n);
⑤write
ssize write(int fd,const void *buf,size_t n);
2. 列出进程间通信的应用编程接口。
(1)管道
①mkfifo
mkfifo [OPTION]... NAME...
②pipe
int pipe(int pipefd[2]);
(2)消息队列
①msgget
int msgget(key_t key, int msgflg);
②msgsnd
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
③msgrcv
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
④msgctl
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
(3)共享内存
①shmget
int shmget(key_t key, size_t size, int shmflg);
②shmat
void *shmat(int shmid, const void *shmaddr, int shmflg);
③shmdt
int shmdt(const void *shmaddr);
④shmctl
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
(4)信号量集
①semget
int semget(key_t key, int nsems, int semflg);
②semop
int semop(int semid, struct sembuf *sops, size_t nsops);
③semctl
int semctl(int semid, int semnum, int cmd, ...);
1. 论述linux内核用哪三个相关的数据结构来表示打开的文件。 P117
文件描述符表(descriptor table):每个进程都有一个独立的文件描述符表,数据类型定义是struct file *fd_arrary[NR_OPEN_DEFAULT],是指向文件对象的指针数组。UNIX I/O用描述符表项的索引号作为open系统调用函数的返回值,称为文件描述符(descriptor)。其后的文件操作都是用文件描述符来定位文件对象,进而定位v-node对象来获取文件属性。
文件表(file table):Linux将打开文件信息存储在文件对象(file object)中,又称file结构,主要成员包括打开方式(f_mode)、读写指针(f_pos)、引用计数(f_count)三个字段,其中引用计数记录指向结构体的指针数。对一个文件执行一次打开操作,就会创建一个文件对象,系统的所有文件对象组成一个文件表。
V 节点表(v-node table):每个打开文件(或设备)都有一个 v 节点(v-node)结构。v 节点包含了文件类型和对此文件进行各种操作的函数的指针。v 节点还包含了从磁盘读取的 i 节点(i-node)的信息,i 节点信息包含了文件的所有者、文件长度、文件所在的设备、指向文件的实际数据块在磁盘上的所在位置的指针等。
2. 论述在linux多线程程序中有哪些变量类型、被映射到哪段地址空间、有几个运行实例。 P218~223
全局变量:
是定义在函数之外的变量,只有一个实例,映射到进程虚拟存储器(进程地址空间)的可读写数据区域(data段、bss段)。任何线程可以引用,最典型的共享变量,示例:sharing.c中的ptr。
本地自动变量:
定义在函数内部但没有static属性的变量,函数被某个线程调用时,该函数所有本地实例变量在该线程堆栈中有一个运行实例,若多个线程执行同一个函数(或例程),该例程中的变量就拥有多个运行实例。示例:
sharing.c中main函数的本地变量tid,在主线程中有一个运行实例tid.m;
函数thread中有本地变量myid,因函数被两个对等线程p0、p1调用,有两个变量实例myid.p0, myid.p1。
本地静态变量:
定义在函数内部并有static属性的变量。即使函数被多个线程调用,也仅有一个运行实例,位于进程虚拟存储器(进程地址空间)的可读写区域(data段、bss段)。示例:函数thread内部的cnt。
1. 画图说明linux进程虚拟地址空间结构。 P222
进程用户地址空间:所有线程共享进程的用户地址空间(或称虚拟地址空间),它是由进程可访问所有存储位置构成,包括:
文本段(text段,程序代码和只读数据)
数据段(data和bss段,全局变量)
存储堆(动态申请存储器)
堆栈(局部变量)
共享库代码和数据(不同进程的用户地址空间可以重叠或重合)
2. 画出linux进程、linux内核与系统调用间关系图。P204
3. 画图说明当一个新的程序开始时用户栈的典型组织结构。 P179
main的函数原型:int main(int argc,int *argv[],char *envp[])
首先是main的栈帧,它是mian函数调用的局部变量。
接下来是命令行参数格式argc、命令行参数列表指针argv和环境变量参数列表指针envp。
再接下来就是命令行参数列表argv[]和环境变量列表envp[]。
最后,栈底是命令行参数串和环境变量串。
*全局变量environ指向这些指针中的第一个envp[0]
1. 说明使用fork系统调用创建进程的过程 P163~166
父进程执行fork系统调用后,子进程就诞生了,新创建的子进程几乎但不完全与父进程相同:程序代码与父进程相同,变量值从父进程复制而来,接下来也从fork函数调用返回,再往下执行;不同的是,fork系统调用的返回值不同,程序代码可根据返回值判断父进程还是子进程,并据此执行不同的处理工作。Linux系统规定,父进程fork系统调用的返回值为子进程的PID,子进程的fork返回值为0,并由系统填入父子进程的数据集中。此后,子进程作为一个独立的进程开启了自己的生命周期,往下执行。
2. 假设下面程序运行时子进程的pid是3000,父进程的pid是2999.请写程序运行结果,并画图说明父子进程运行时用户地址空间变化情况。P163
#include
#include
#include
#include
int glob=10;
int main(void){
int local;
pid_t pid;
local=8;
if((pid=fork())==0){
sleep(4);
}
else{
glob++;
local--;
sleep(10);
}
printf("pid=%d,glob=%d,local=%d\n",getpid(),glob,local);
exit(0);
}
运行结果:
pid=3000,glob=10,local=8 //子进程
pid=2999,glob=11,local=7 //父进程