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

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

常问:书中有些Linux-C程序实例的头文件(如wrapper.h)在编译时报“无法找到”?

解决方法:程序里用到哪些函数,就用【man命令+函数名】查包含此函数的头文件(来替换wrapper.h)

linux编程 java 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

linux编程 java linux编程徐钦桂答案_linux编程 java_02

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

linux编程 java linux编程徐钦桂答案_linux编程 java_03

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

linux编程 java linux编程徐钦桂答案_地址空间_04

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段,全局变量)
        存储堆(动态申请存储器)
        堆栈(局部变量)
        共享库代码和数据(不同进程的用户地址空间可以重叠或重合)

linux编程 java linux编程徐钦桂答案_Linux原理_05

2. 画出linux进程、linux内核与系统调用间关系图。P204

linux编程 java linux编程徐钦桂答案_地址空间_06

3. 画图说明当一个新的程序开始时用户栈的典型组织结构。 P179

main的函数原型:int main(int argc,int *argv[],char *envp[])

首先是main的栈帧,它是mian函数调用的局部变量。

接下来是命令行参数格式argc、命令行参数列表指针argv和环境变量参数列表指针envp。

再接下来就是命令行参数列表argv[]和环境变量列表envp[]。

最后,栈底是命令行参数串和环境变量串。

*全局变量environ指向这些指针中的第一个envp[0]

linux编程 java linux编程徐钦桂答案_地址空间_07

五、分析进程家族关系

1. 说明使用fork系统调用创建进程的过程 P163~166

       父进程执行fork系统调用后,子进程就诞生了,新创建的子进程几乎但不完全与父进程相同:程序代码与父进程相同,变量值从父进程复制而来,接下来也从fork函数调用返回,再往下执行;不同的是,fork系统调用的返回值不同,程序代码可根据返回值判断父进程还是子进程,并据此执行不同的处理工作。Linux系统规定,父进程fork系统调用的返回值为子进程的PID,子进程的fork返回值为0,并由系统填入父子进程的数据集中。此后,子进程作为一个独立的进程开启了自己的生命周期,往下执行。

linux编程 java linux编程徐钦桂答案_子进程_08

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 //父进程

linux编程 java linux编程徐钦桂答案_子进程_09