Linux编程期末复习(超实用)

所用教材:徐钦桂、徐治根、黄培灿、谢伟鹏(编著)  出版社:清华大学出版社

实战和理论相结合的一本好书,但也很晦涩抽象。以下为考试原题!

一、简答及编程

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、套接字文件

文件或目录常用的9种属性

Linux文件类型和访问权限位的结构

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<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
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 //父进程

发布了38 篇原创文章 · 获赞 130 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41587612/article/details/103932439