Unix/Linux 程序设计 实验指导

Unix/Linux 程序设计
实验指导书
实验 1 vi 编辑器以及 X Window 桌面的使用
一、实验目的
练习 vi 编辑器以及 X Window 桌面的使用。
需要的知识:第 3 章 vi 编辑器的使用和第 4 章建立快速链接的内容。
二、实验内容
首先使用 vi 编辑器编辑一个 c 语言源程序 hello.c,程序的功能为:接受用户
输入的姓名,然后向屏幕输出一条语句“Welcome 姓名”。使用命令“gcc –o hello
hello.c”编译源程序,如果发现程序错误则需要反复使用 vi 修改源文件并且在修
改后重新编译。最后在 X Window 的桌面上放置源程序 hello.c 的链接。
三、实验步骤
步骤 1:使用 vi 编辑文件 hello.c
#include <stdio.h>
int main()
{
char name[20];
printf(“Please input your name:”);
scanf("%s",name); //输入姓名
printf(“Welcome %s!\n”,name); //输出 welcome 姓名
return 0;
}
步骤 2:编译文件 gcc –o hello hello.c
步骤 3:如果有错误,再次使用 vi 修改 hello.c 的内容;然后再次编译。直至编
译时不报任何错误。
步骤 4:在桌面建立链接:
创建链接有如下两个方法:
方法 1:打开文件管理器,找到 hello.c 文件,单击鼠标右键,选择【创建链
接】命令,则在文件管理器的窗口中出现一个图标,名字为“到…的链接”,并
且带一个小箭头符号。只要将该图标拖放到桌面,就可以建立到该项目的链接。
方法 2:选中 hello.c 文件,按住鼠标中键(三键鼠标)的滚轮键,拖放到桌
面,当放开鼠标中键时,会弹出【移动到此处】、【复制到此处】、【在此处创建链
接】等命令,选择【在此处创建链接】命令,就可以在桌面创建到该项目的链接。
提示:本题用到了编译命令,题目中已经指出了该命令的使用方式,不要求理解每个项目的含义,在第 12 章会有具体介绍。
四、实验结果
(一)、 vi 使用的实验:
1、编辑源代码
①建立 hello.c 源代码文件
②输入源文件
2、编译源代码
3、运行可执行文件(二)、 link 文件的实验实验 2 存储设备挂载及管理任务设置
一、实验目的
练习存储设备的挂载以及编写 shell 程序将日常管理任务简化。
需要的知识:第 6 章存储设备的挂载和第 11 章 shell 编程的内容
二、实验内容
假设有一台 Windows 和 Linux 双启动的主机, Window 有 C、 D 两个分区,
编写一个负责挂载的脚本程序 mmnt,参数为 cdrom 时挂载光驱、参数为 floppy
时挂载软驱、参数为 usb 时挂载 U 盘、参数为 C(大小写都可以)时挂载 Windows
的 C 盘(有中文字符),参数为 D(大小写都可以)时挂载 Windows 的 D 盘(有
中文字符),参数为其它时显示帮助信息: Usage: mmnt cdrom|floppy|usb|C|c|D|d
编写一个负责卸载的脚本程序 umnt,完成相应的存储设备的卸载,参数的含
义同上。
三、实验步骤
步骤 1:以 root 身份登录 Linux 系统,使用 fdisk –l 命令查看系统连接的存储设
备,主要查看 windows 的两个分区的名称,比如 hda3, hda5 等。
步骤 2:建立挂载点:一般地,光驱和软驱的挂载点/mnt/cdrom、 /mnt/floppy 由
系统缺省建立,查看一下这两个目录是否存在,如果没有,手工建立;此外使用
mkdir 命令建立 U 盘和 windows 的 C 盘和 D 盘的挂载点:
mkdir /mnt/usb
mkdir /mnt/C
mkdir /mnt/D
步骤 3:编写/etc/fstab 文件,编辑或者添加如下 5 行:
(操作时只需要添加 usb 行,其他四行暂未用到,不影响。)
/dev/hda3 /mnt/C vfat noauto,iocharset=cp936 0 0
/dev/hda5 /mnt/D vfat noauto,iocharset=cp936 0 0
/dev/cdrom /mnt/cdrom iso9660 noauto,owner,ro 0 0
/dev/fd0 /mnt/floppy auto noauto,owner 0 0
/dev/sdb1 /mnt/usb auto noauto 0 0
步骤 4:编写 shell 脚本 mmnt (vi mmnt)
#!/bin/sh
case $1 in
cdrom) mount /mnt/cdrom ;;
floppy) mount /mnt/floppy ;;
usb) mount /mnt/usb;;
C|c) mount /mnt/C ;;
D|d) mount /mnt/D ;;
*) echo “Usage: mmnt cdrom|floppy|usb|C|c|D|d”;;
esac
步骤 5:编写 shell 脚本 umnt (vi umnt)#!/bin/sh
case $1 in
cdrom) umount /mnt/cdrom ;;
floppy) umount /mnt/floppy ;;
usb) umount /mnt/usb;;
C|c) umount /mnt/C ;;
D|d) umount /mnt/D ;;
*) echo “Usage: umnt cdrom|floppy|usb|C|c|D|d”;;
esac
步骤 6:使脚本 mmnt 和 umnt 可执行
chmod +x mmnt
chmod +x umnt
提示:此实验可以分两步,学完第 6 章后完成步骤 1 到步骤 3,使用系统命令
mount 和 umount 测试自己的设置,学完第 11 章 shell 编程之后完成最后几步。
四、实验结果
1、查看系统外接存储
2、建立挂载点
3、编辑 fstab 文件4、编写 mmnt 和 umnt 脚本
5、赋予脚本可执行权限a 6
、运行脚本.实验 3 网络服务的配置与管理
一、实验目的
练习网络服务的配置与管理。
需要的知识:第 7 章网络配置、第 8 章网络服务器的配置和使用。
二、实验内容
某局域网(yys.com)具有 3 台安装了 Linux 系统的计算机,分别用于提供
WWW、 FTP 和 DNS 服务,要求:设计分配 IP 地址和主机名,配置三种服务器,
使得可以通过域名访问网络服务。
三、实验步骤
步骤 1:设计分配各主机的 IP 地址和域名,假设三台主机分别为 A、 B、 C,可
以如下分配 IP 地址和域名:
主机 IP 主机名 域名
A. 192.168.1.10 www www.yys.com
B 192.168.1.11 ftp ftp.yys.com
C 192.168.1.12 dns dns.yys.com
步骤 2:构建 WWW 服务器

  1. 在 A 主机中安装最新版的 Apache 服务器软件
  2. 配置 Apache
  3. 在其主文档目录中建立 web 主页
  4. 启动 Apache 服务器
  5. 在本机中测试服务器与网页是否正常运行
    步骤 3:构建 FTP 服务器
  6. 在 B 主机中安装最新版的 vsFtpd 服务器软件
  7. 配置 vsFtpd,按需求建立不同形式的登录方式
  8. 在对应的文件目录中复制有关文件资料
  9. 启动 vsFtpd 服务器
  10. 在本机中测试服务器是否正常运行
    步骤 4:构建 DNS 服务器
  11. 在 C 主机中安装最新版的 bind 服务器软件
  12. 配置系统,建立主机与 IP 地址的映射。
  13. 启动 bind 服务器
  14. 在本机中测试服务器是否正常运行
    步骤 5:设置客户机
  15. 在客户机中修改本地 DNS 服务器的设置。
  16. 打开网页浏览器,在地址栏中输入: http://www.yys.com ,检验是否连接 WWW
    服务器
  17. 打开网页浏览器,在地址栏中输入: ftp://ftp.yys.com ,检验是否连接 FTP 服务

    四、实验结果(一)、 ftp( fedora 9 自带 vsftp)
    1、修改配置文件
    2、重启 vsftp
    3、访问 ftp实验 4 多线程应用程序设计
    一、实验目的
  18. 了解多线程程序设计的基本原理
  19. 学习pthread库函数的使用。
    二、实验内容
    1、 读懂pthread.c的源代码,熟悉几个重要的pthread库函数的使用。掌握共享
    锁和信号量的使用方法。
    2、 运行make产生pthread程序,使用minicom串口方式连接开发主机进行运行实
    验。
    三、预备知识
    1.有C 语言基础。
    2.会使用LINUX 下常用的编辑器。
    3.掌握Makefile 的编写和使用。
    4.了解Linux 下的编译程序与交叉编译的过程。
    四、实验设备及工具
    硬件: Mini6410嵌入式实验平台、 PC机Pentium 500以上, 硬盘10G以上。
    软件: PC机操作系统Fedora9+MINICOM+ARM-LINUX 开发环境
    五、实验原理
    1、多线程程序的优缺点
    多线程程序作为一种多任务、并发的工作方式,有以下的优点:
    提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很
    长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作置于一个新的线程,可以避免这种尴
    尬的情况。
    使多CPU系统更加有效。操作系统会保证当线程数不大于CPU数目时,不同
    的线程运行于不同的CPU上。
    改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个
    独立或半独立的运行部分,这样的程序会有利于理解和修改。
    LIBC中的pthread库提供了大量的API函数,为用户编写应用程序提供支持。
    2 实验源代码结构流程图
    实验为著名的生产者-消费者问题模型的实现,主程序中分别启动生产者
    线程和消费者线程。生产者线程不断顺序地将0到1000的数字写入共享的循环缓
    冲区,同时消费者线程不断地从共享的循环缓冲区读取数据。流程图如图2-1
    所示:3 生产者写入共享的循环缓冲区函数PUT
    Void put(struct prodcons *b,int data)
    {
    Pthread_mutex_lock(&b->lock); //获取互斥锁
    While ((b->writepos+1)%BUFFER_SIZE == b->readpos) //如果读写位
    置相同
    {
    Pthread_cond_wait(&b->notfull,&b->lock);
    //等待状态变量b->notfull,不满则跳出阻塞
    } b
    ->buffer[b->writepos] = data; //写入数据
    b->writepos++;
    if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
    pthread_cond_signal (&b->notempty); //设置状态变量
    pthread_mutex_unlock(&b->lock); //释放互斥锁
    }
    4 消费者读取共享的循环缓冲区函数GET
    Int get (struct prodcons *b)
    {
    Int data;
    Pthread_mutex_lock(&b->lock); //获取互斥锁
    While (b->writepos == b->readpos) // 如果读写位置相同{
    Pthread_cond_wait(&b->notempty,&b->lock);
    //等待状态变量b->notempty,不空则跳出阻塞。否则无数据可读
    } D
    ata = b->buffer[b->readpos]; //读取数据
    b->readpos++;
    if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
    pthread_cond_signal(&b->notfull); //设置状态变量
    pthread_mutex_unlock(&b->lock); //释放互斥锁
    return data;
    }
    5 生产、消费流程图:
    生产消费流程图如下图2-2所示:6、 主要的多线程API
    1、线程创建函数:
    Int pthread_create (pthread_t * thread_id,_const pthread_attr_t
    *_attr,void (_start_routine) (void *) ,void * _restrict_arg);
    2、获得父进程ID
    Pthread_t pthread_self (void)
    3、测试两个线程号是否相同:
    Int pthread_equal (pthread_t __thread1,pthread_t __thread2)
    4、线程退出:
    Void pthread_exit (void * __retval)5、等待指定的线程结束:
    Int pthread_join (pthread_t __th,void **__thread_return)
    6、互斥量初始化:
    Pthread_mutex_init (pthread_mutex_t *,__const pthread_mutexattr_t
    *)
    7、销毁互斥量:
    Int pthread_mutex_destroy (pthread_mutex_t *__mutex);
    8、再试一次获得对互斥量的锁定(非阻塞):
    Int pthread_mutex_trylock (pthread_mutex_t *__mutex);
    9、锁定互斥量(阻塞):
    Int pthread_mutex_lock (pthread_mutex_t *__mutex);
    10、解锁互斥量
    Int pthread_mutex_unlock (pthread_mutex_t *_mutex)
    11、条件变量初始化
    Int pthread_cond_init (pthread_cond_t *_restrict __cond,
    _const pthread_condattr_t *__restrict__cond_attr)
    12、销毁条件变量COND
    Int pthread_cond_destory (pthread_cond_t *__cond)
    13、唤醒线程等待条件变量
    Int pthread_cond_signal (pthread_cond_t *__cond)
    14、等待条件变量(阻塞)
    Int pthread_cond_wait (pthread_cond_t *__restrict __cond,Pthread_mutex_t *__restrict __mutex)
    15、在指定的时间到达前等待条件变量
    Int pthread_cond_timedwait (pthread_cond *__restrict__cond,
    Pthread_mutex_t *_restrict__mutex,
    _const struct timespec *_restrict __abstime)
    PTHREAD库中还有大量的API函数,用户可以参考其他相关书籍。
    7、 主要函数说明
    Pthread_create 线程创建函数
    Int pthread_create (pthread_t * thread_id,_const pthread_attr_t
    *_attr,void (_start_routine) (void *) ,void * _restrict_arg);
    线程创建函数第一个参数为指向线程标识符的指针,第二个参数用来设
    置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函
    数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指
    针。第二个参数我们也设为空指针,这样将生成默认属性的线程。当创建线程
    成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为
    EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后
    者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程运
    行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。
    pthread_join函数
    用来等待一个线程的结束,函数原型为:
    Int pthread_join(pthread_t __th,void **__thread_return)
    第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,
    它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用
    它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的
    资源被收回。pthread_exit函数
    一个线程的结束有两种途径,一种是像我们上面的例子一样,函数结束了,
    调用它的线程也就结束了,另一种方式是通过函数pthread_exit来实现。它的
    函数原型为:
    Void pthread_exit(void *_retval)
    唯一的参数是函数的返回代码,只要pthread_join中的第二个参数
    thread_return不是NULL,这个值将被传递给thread_return.最后要说明的是,
    一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余
    调用pthread_join的线程则返回错误代码ESRCH。
    8、 条件变量
    使用互斥锁来实现线程间数据的共享和通信,互斥锁一个明显的缺点是它
    只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线
    程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条
    件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并
    等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条
    件变量唤醒一个或多个正被条件变量阻塞的线程。这些线程将重新锁定互斥锁
    并重新测试条件是否满足。一般说来,条件变量被用来进行线程间的同步。
    pthread_cond_init 函数
    条件变量的结构为pthread_cond_t ,函数pthread_cond_init () 被用来初
    始化一个条件变量,它的原型为:
    Intt pthread_cond_init(pthread_cond_t *cond,__const
    pthread_condattr_t * cond_attr)
    其中cond是一个指向结构pthread_cond_t的指针, cond_attr是一个指向结
    构pthread_condattr_t的指针。结构pthread_condattr_t是条件变量的属性结
    构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,
    默认值是PTHREAD_PROCESS_PRIVATE,即此条件被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件
    变量的函数为pthread_cond_destroy(pthread_cond_t cond)
    pthread_cond_wait函数
    使线程阻塞在一个条件变量上。它的函数原型为:
    Extern int pthread_cond_wait(pthread_cond_t *_restrict__cond,
    Pthread_mutex_t *_restrict _mutex )
    线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数
    pthread_cond_signal和函数pthread_cond_broadcast唤醒,但是要注意的是,
    条件变量只是起阻塞和唤醒线程的作用,具体的判断条件还需用户给出,例如
    一个变量是否为0等等,这一点我们从后面的例子中可以看到。线程被唤醒后,
    它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在
    这里,被等待下一次唤醒。这个过程一般用while语句实现。
    pthread_cond_timewait函数
    另一个用来阻塞线程的函数是pthread_cond_timewait() ,它的原型为:
    Extern int pthread_cond_timewait __P(pthread_cond_t
    *_cond,pthread_mutex_t *__mutex,__const struct timespec *__abstime) ;
    它比函数pthread_cond_wait()多了一个时间参数,经历abstime段时间后,
    即使条件变量不满足,阻塞也被解除。
    函数pthread_cond_signal
    函数pthread_cond_signal()原型为
    Extern int pthread_cond_signal (pthread_cond_t *__cond);
    它用来释放被阻塞在条件变量cond上的一个线程。多个线程阻塞在此条件
    变量上时,哪一个线程被唤醒是由线程的调度策略所决定的。要注意的是,必
    须用保护条件变量的互斥锁来保护这个函数,否则条件满足信号又可能在测试
    条件和调用pthread_cond_wait函数之间被发出,从而造成无限制的等待。六、 实验步骤
    1.使用vi编辑器或其他编辑器阅读理解源代码。
  20. 运行make产生pthread可执行文件
    3.运行pthread,观察运行结果的正确性。
  21. 修改一些参数,再次运行调试,加深对多线程的理解。
    七、思考题
  22. 加入一个新的线程用于处理键盘的输入,并在按键为ESC时终止所有线程。
  23. 线程的优先级的控制。实验 5 Linux 环境下 gcc 的使用
    一、实验目的
    了解 Linux 下的用户管理机制,熟练掌握 Linux 下的 C 语言编程以及 gcc 工
    具的使用。
    需要的知识: C/C++语言的基本技能、第 9 章用户管理知识、第 12 章 gcc 的使
    用。
    二、实验内容
    Linux 系统管理的一项重要工作就是用户管理。用户的口令以加密的形式存
    储在口令文件/etc/shadow 中。弱口令就是很容易被猜出来的口令,比如与用户名
    相同的口令、像“123456、 admin、 computer”这类常用的口令等,口令字典是
    指将一些常用弱口令写在一个文本文件。管理员应该定期检测系统中是否存在弱
    口令。试编写一个 c 语言程序,主动检查自己的 Linux 系统中是否存在弱口令。
    三、实验步骤
    步骤 1:分析/etc/shadow 文件的格式。
    可以查看自己的系统中该文件的格式,参照 9.1 节的介绍,该文件的每一行
    对应一个用户,下面是一个系统中/etc/shadow 文件的实例:
    user1: 6 6 6DVLiBPZG$IrR9o0KpjtGQOj7I5WvgxQ.jMQ/Qzl3cJp4w0loUMZs4xQSQ8wk
    dIK7Sdkdk2pMXeAfOYq9O07r/QuDdJ8f3c0:14748:0:99999:7:::
    user2: 6 6 6RR8pmW2aerqIkySA$PcMV7/Z37QFCe9hJrf1rlUjPTAOdmKsW/mfG40V343k
    xG1QNsWWI7mdzl.50SCJpI4TQ/x4z4zsCoiA48EjAn1:14748:0:99999:7:::
    user3: 6 6 6oGG.739y$9ysybZ.VaTQ7dmR1zyz1vR8OiCUSCnzqnFd1PUYvedJMt.t6ElI
    SwUohtOAlqAuT7.sPDjfy.bKCKar82mSp2.:14748:0:99999:7:::
    user4: 6 6 6AJerec5o$bwgJnQ0mTPzZMRCZYuivQVsWtD9mlh3.pWK2tR2pZPr4NzSlqk6
    hhFq3/zfWJXQCNmXJTlZhubwwW9x6a8mtM0:14748:0:99999:7:::
    user5: 6 6 6o1DA6WOiXtme7Zsw$Lj6bXgI5c5Kg/GewGWYv.4pQ0fD/AnSYEjMvmXxuvzE
    K3IYhNYjnTIEnTPQr9pZIMzatBOyrC4FgBjYtR5R.n1:14749:0:99999:7:::
    user6: 6 6 6UUXC4WKX$mKyl/32n7xrU1ChPIJYs2gwYuEoObdkNsPcVhxR22xFBIspNjVI
    fRv4FgoDWsJIxy4TZ.ci70MeDvDMEeExXL0:14749:0:99999:7:::
    步骤 2:了解 Linux 下口令加密的原理
    口令的加密是使用 Linux 系统的 crypt 函数。使用 info 或者 man 可以了解
    该命令的工作原理: Red Hat Linux9 中使用的 MD5 算法,在 Fedora 10 中默认
    使用了 SHA-512 算法。它使用一个字符串作为 salt(翻译成盐),长度是 8 到 16
    个字节,将用户输入的口令 key 进行加密。函数 crypt(key,salt)的结果存入
    /etc/shadow 文件的第 2 个域。这个域前面 6 6 6salt 就 是 c r y p t 函 数 的 s a l t 参 数 。 当 给 用 户 设 置 口 令 时 , s a l t 是 由 系 统 随 机 选 取 的 , 因 此 即 使 是 相 同 的 k e y , 因 s a l t 不 同 而 第 二 个 域 的 值 也 不 同 。 当 用 户 在 登 录 中 输 入 自 己 的 口 令 时 , 系 统 使 用 c r y p t 进 行 同 样 的 计 算 , 如 果 所 得 的 结 果 与 / e t c / s h a d o w 中 存 储 的 一 致 , 则 接 受 用 户 的 登 录 。 步 骤 3 : 什 么 是 弱 口 令 : 弱 口 令 就 是 很 容 易 被 猜 出 来 的 口 令 , 比 如 a d m i n , g u e s t , f l o w e r , 123456 , b e a u t y 等 等 。 在 口 令 猜 测 或 者 破 解 时 , 一 般 不 会 穷 尽 所 有 字 符 的 所 有 排 列 组 合 , 一 般 都 使 用 一 个 弱 口 令 字 典 , 字 典 中 包 含 了 常 被 用 来 作 为 口 令 的 字 符 串 。 可 以 到 网 上 查 找 口 令 字 典 。 步 骤 4 : 弱 口 令 检 查 的 原 理 对 每 个 用 户 而 言 , 逐 个 将 那 些 经 常 被 用 来 作 为 口 令 的 词 , 使 用 c r y p t 做 运 算 , 如 果 运 算 结 果 与 / e t c / s h a d o w 所 存 储 的 加 密 后 的 口 令 相 同 , 则 口 令 被 猜 出 来 。 步 骤 5 : 用 c 语 言 编 写 实 现 口 令 检 查 的 程 序 关 键 部 分 的 代 码 如 下 : 通 过 读 取 / e t c / s h a d o w 的 每 一 行 , 获 得 一 个 关 于 用 户 的 数 据 结 构 , 存 放 在 p w d 中 。 c h a r s a l t s t r [ 21 ] ; / / 存 放 种 子 , 最 多 是 20 个 字 节 b z e r o ( s a l t s t r , s i z e o f ( s a l t s t r ) ) ; / / 将 变 了 全 部 清 0 s t r n c p y ( s a l t s t r , p w d − > p w p a s s w d , 20 ) ; / / 将 s a l t 取 出 , 最 多 20 个 字 节 , 以 其 中 就是 crypt 函数的 salt 参 数。当给用户设置口令时, salt 是由系统随机选取的,因此即使是相同的 key, 因 salt 不同而第二个域的值也不同。 当用户在登录中输入自己的口令时,系统使用 crypt 进行同样的计算,如果 所得的结果与/etc/shadow 中存储的一致,则接受用户的登录。 步骤 3:什么是弱口令:弱口令就是很容易被猜出来的口令,比如 admin, guest,flower,123456, beauty 等等。在口令猜测或者破解时,一般不会穷尽所有字符 的所有排列组合,一般都使用一个弱口令字典,字典中包含了常被用来作为口令 的字符串。可以到网上查找口令字典。 步骤 4:弱口令检查的原理 对每个用户而言,逐个将那些经常被用来作为口令的词,使用 crypt 做运算, 如果运算结果与/etc/shadow 所存储的加密后的口令相同,则口令被猜出来。 步骤 5:用 c 语言编写实现口令检查的程序 关键部分的代码如下: 通过读取/etc/shadow 的每一行,获得一个关于用户的数据结构,存放在 pwd 中。 char saltstr[21]; //存放种子,最多是 20 个字节 bzero(saltstr,sizeof(saltstr));//将变了全部清 0 strncpy(saltstr,pwd->pw_passwd,20); //将 salt 取出,最多 20 个字节,以其中 cryptsaltsalt使keysalt使crypt/etc/shadow3adminguestflower,123456beauty使4使crypt/etc/shadow5c/etc/shadowpwdcharsaltstr[21];//20bzero(saltstr,sizeof(saltstr));//0strncpy(saltstr,pwd>pwpasswd,20);//salt,20作为边界
    cp = crypt (guess, saltstr); //调用 crypt 加密函数
    if (strcmp (cp, pwd -> pw_passwd)) //与口令文件中存储的信息比较
    return (0); //若不一致则返回
    printf (“Warning! Password Problem: Guessed:\t%s\tpasswd: %s\n”,
    pwd -> pw_name, guess); //找到口令,显示口令
    步骤 6:编译程序
    gcc –o passchk pass.c –lcrypt
    其中-o 选项指定编译后的可执行文件名, -l 选项表示使用 crypt 函数库
    步骤 7:执行口令检查程序
    ./passchk –P myshadow –w words –p
    其中 passchk 是可执行文件名, myshadow 是口令文件,如果没有-P 选项,
    则缺省使用 Linux 系统的/etc/shadow 文件, words 是存放弱口令的口令字典文
    件,选项-p 表示将破解后的口令输出。
    四、实验结果
    1、将/etc/shadow 拷贝到 myshadow
    2、编辑口令字典 words3、编译程序
    4、运行程序实验 6 Gtk+编程和 gcc 工具的使用
    一、实验目的
    练习 Gtk+编程和 gcc 工具的使用。
    需要的知识: C/C++语言的基本技能、第 12 章 gcc 的使用与开发、第 13 章 Gtk+
    图形程序设计。
    二、实验内容
    日志的查询统计是系统管理的一项重要任务。 Linux 系统的日志以文本文件
    的形式存在,使用 Gtk+编写一个图形界面的系统日志管理器(针对日志文件
    /var/log/messages* ), 它 可 以 列 出 系 统 当 前 的 日 志 个 数 ( /var/log/message,
    /var/log/message.1, /var/log/message.2,……共多少个),对指定的日志文件可以查
    询哪些条目含有(include)指定信息(比如含有“succeed”的条目),哪些不含
    有(exclude)指定信息。
    图 5-1 是一个示例程序的效果图:
     数字 1 为一个标签,显示日志文件的数量;
     显示 succeed 的单行文本框用来输入包含的指定信息;
     显示 kernel 的单行文本框用来输入不包含的指定信息;
     点击标签为“/var/log/messages”的按钮后,可以选择日志文件;
     点击标签为“View”的按钮后,可以根据输入的条件查看日志内容;
    下面的多行文本框用来显示日志内容。
    图 5-1 程序效果图
    程序目录结构如图 5- 2 所示:图 5- 1 程序目录结构图
    其中:
     文件 main.cpp:为程序的主函数所在文件,通过对其它文件的调用启动和结
    束程序;
     目录 core:其中的文件分为三组,每一组的一个.h 文件和一个.cpp 文件共同
    定义了一个类,其中的类名和对应的文件名相同。目录 core 下定义的类实现
    了与界面无关的大部分功能。包括:检索日志文件个数、检索和根据条件过
    滤日志内容等作用。
     目录 widgets:包括两个文件,实现程序界面,并和目录 core 下的类交互,
    利用图形界面接收用户的输入和显示程序的运行结果。
    三、实现步骤
  1. 根据目录结构创建目录和文件;
    为了方便组织程序代码,可以将本实验的代码放到单独的目录下。因此,先
    建立一个目录作为项目的根目录,本例中根目录命名为: ViewLogGtk。
    • 在当前工作目录下,建立项目根目录: mkdir ViewLogGtk
    • 进入根目录: cd ViewLogGtk
    • 创建空白主程序文件: touch main.cpp
    • 创建目录 core: mkdir core
    • 创建目录 widgets: mkdir widgets
    • 进入目录 core: cd core
    • 创建目录 core 下的空白程序文件:
    touch Util.h
    touch Util.cpp
    touch LogRecord.h
    touch LogRecord.cpp
    touch LogContent.h
    touch LogContent.cpp
    • 进入目录 widgets: cd …/widgets
    • 创建目录 widgets 下的空白程序文件:
    touch widget.h
    touch widget.cpp
    当前目录./ widgets/
    main.cpp
    Util.h, Util.cpp
    core/ LogRecord.h, LogRecord.cpp
    widget.h, widget.cpp
    LogContent.h, LogContent.cpp• 目录文件创建完毕。
  2. 编辑并实现代码文件:这里仅给出关键部分的代码。
    Util.h 定义了类 Util:
    类 Util 中只定义了一个静态的方法: queryFileCount()用来查询指定目录下指
    定类型的文件个数。其中方法的第一个参数为指定查询文件的目录,第二个参数
    为指定文件名中包含的字符串。对于本例,第一参数固定的传入字符串
    “/var/log/”,第二个参数固定的传入字符串“message”,即查询目录/var/log/下
    文件名中包含 message 的文件。
    LogRecord.h
    1 #ifndef LOGRECORD_H
    2 #define LOGRECORD_H
    3 #include
    4 using namespace std;
    5 class LogRecord
    6 {
    7 private:
    8 string contents;
    9 public:
    10 LogRecord(char* contents);
    11 ~LogRecord(void);
    12 string getContents(void);
    13 LogRecord(string contents);
    14 };
    15 #endif
    类 LogRecord 代表日志中每一行。构造函数 LogRecord(char*)和 LogRecord
    (string)分别提供了利用 char类型和 string 类型形成日志行记录的两种方法。
    其中私有字符串成员 contents 储存日志记录的一行字符串,公有方法 getContents
    ()对外提供了一个获得字符串记录内容的接口。
    LogContent.h
    1 #ifndef LOGCONTENT_H
    2 #define LOGCONTENT_H
    3 #include
    4 #include
    5 #include"LogRecord.h"
    Util.h
    1 #ifndef UTIL_H
    2 #define UTIL_H
    3 class Util{
    4 public:
    5 static int queryFileCount(const char
    dir,const char* keyword);
    6 };
    7 #endif6 using namespace std;
    7 class LogContent
    8 {
    9 private:
    10 string contains; //记录中需要出现的关键字
    11 string notcontains; //记录中不能出现的关键字
    12 string file_name; //记录文件名
    13 list records; //符合过滤条件的记录集合
    14 public:
    15 LogContent(void);
    16 ~LogContent(void);
    17 void setFilter(string contains, string not_contains); //设置过
    滤条件
    18 void set_file_name(string file_name); //设置检索的日志记
    录文件名
    19 int fill_records(void); //根据文件、过滤条件填充记录集合
    20 private:
    21 int filter_record(LogRecord record); //判断记录行是否满足
    过滤条件
    22 public:
    23 list get_records(void); //得到记录集合
    24 };
    25 #endif
    类 LogContent 是检索日志文件、过滤日志行记录的主要场所。它利用
    setFilter(string,string),设置日志行记录中必须出现的关键字和必须不出现的关键
    字,利用 set_file_name(string)设置日志文件的文件名称。检索关键字和日志
    文件设置完毕后,就可以利用 fill_records()检索日志文件并填充日志行记录集
    合。填充完毕,可以使用方法 get_records()得到检索的结果。
    widget.h
    1 #ifndef WIDGET_H
    2 #define WIDGET_H
    3 #include
    4 #include
    5 #include <gtk/gtk.h>
    6 #include “LogContent.h”
    7 using namespace std;
    8 //fields
    9 string file_name; //日志文件名
    10 string kw_contains; //日志过滤条件中,需要包含的关键字
    11 string kw_not_contains; //日志过滤条件中,不需要包含的关键字
    12 int file_num; //指定目录下,特定文件的个数
    13 GtkWidget* window; //主界面窗口
    14 GtkWidget* lbl_file_num; //文件个数标签
    15 GtkWidget* btn_choose_file; //选择文件按钮16 GtkWidget* btn_view; //查看日志文件按钮
    17 GtkWidget* ety_contains; //包含关键字输入文本框
    18 GtkWidget* ety_not_contains; //不包含关键字输入文本框
    19 GtkWidget* scroll_win; //用于显示日志的滚动窗口
    20 GtkWidget* text; //显示日志多行文本框
    21 GtkWidget* table; //用于摆放界面控件的表格布局
    22 GtkWidget* dialog; //用于选择文件的对话框
    23 GtkTextBuffer* buf; //显示日志的文字缓冲区
    24 LogContent logContent; //完成文件检索、过滤填充的对象
    25 //functions
    26 void layout_ctrls(); //放置界面控件
    27 void onChooseFile(GtkWidget* sender,gpointer data); //点击选择文件
    时的动作
    28 void onView(GtkWidget* sender,gpointer data); //点击查看日志文件
    按钮的动作
    29 void onFileSelect(GtkWidget* widget,GtkFileSelection* fs);
    //在对话框中选择文件的动作
    30 void dispLogNum(int num); //显示日志文件个数
    31 void connectHandlers(); //关联界面控件和相应的事件处理程序
    32 #endif
    33 #ifndef WIDGET_H
    widget.h 声明了与程序界面直接相关的变量和方法,其中的方法的实现部分即
    函数体在 widget.cpp 中。两个文件相结合,完成了程序界面的生成、界面控件元
    素和事件处理函数的关联、接收用户输入并反馈处理结果的功能,是用户之间交
    互的部分。
    main.cpp
    1 #include “widget.h”
    2 #include “widget.cpp”
    3 int main(int argc, char* argv[]){
    4 gtk_set_locale();
    5 gtk_init(&argc,&argv);
    6 layout_ctrls(); //调用布局界面的方法,形成并显示图形用户界

    7 gtk_main();
    8 return 0;
    9 }
    main.cpp 功能代码都很简单,除了常规的初始化程序环境的功能外,唯一特殊
    的就是调用 widget.h 中定义的 layout_ctrls()方法,形成并显示界面。
  3. 编译程序:
    程序包含文件较多,可以采取分步编译的方式。由于代码中用到了类、 string
    等概念,是 C++风格的代码,因此,编译时采用 g++命令,而不是 gcc 命令。 g++
    命令和 gcc 命令使用方法参数信息都相同, g++用于 C++风格代码而 gcc 常用于
    c 风格的代码。
    假设当前工作目录为项目的根目录即 ViewLogGtk 目录下,首先编译 core 目录下文件:
    • 编译类 Util: g++ -c core/Util.cpp
    • 编译类 LogRecord: g++ -c core/LogRecord.cpp
    • 编译类 LogContent: g++ -c core/LogContent.cpp
    然后,编译 main.cpp 和 widgets 目录下的文件:
    • g++ -c main.cpp -Icore -Iwidgets pkg-config gtk+-2.0 --cflags --libs
    由于 widges 目录下的文件涉及到了 Gtk+的界面部分,因此,在编译时需要加入
    pkg-config ....参数。
    最后,将得到的目标文件编译为统一的可执行文件,并将可执行文件命名为
    ViewLog,命令:
    • g++ LogRecord.o LogContent.o Util.o main.o -o ViewLog
    -Icore -Iwidgets pkg-config gtk+-2.0 --cflags --libs
  4. 运行程序:
    • ./ViewLog
    四、实验结果
    1、按照步骤编译源文件
    注意: pkg-config gtk±2.0 –cflags –libs 两边的引号是键盘左上角的引号(和~一起的那个引
    号)。
    2、运行程序实验 7 Qt 编程和 qmake 工具的使用
    一、实验目的
    练习 Qt 编程和 qmake 工具的使用。
    需要的知识: C/C++语言的基本技能、第 14 章 Qt 图形程序设计。
    二、实验内容
    日志的查询统计是系统管理的一项重要任务。 Linux 系统的日志以文本文件
    的形式存在,使用 Qt 编写一个图形界面的系统日志管理器(针对日志文集
    /var/log/messages* ), 它 可 以 列 出 系 统 当 前 的 日 志 个 数 ( /var/log/message,
    /var/log/message.1, /var/log/message.2,……共多少个),对指定日志文件可以查询
    哪些条目含有(include)指定信息(比如含有“succeed”的条目),哪些不含有
    (exclude)指定信息。
    图 6-1 是一个示例程序的效果图:
     数字 1 是一个 Qt 的 LCD 数码显示控件;
     显示 succeed 的单行文本框用来输入包含的指定信息;
     显示 kernel 的单行文本框用来输入不包含的指定信息;
     点击标签为“/var/log/messages”的按钮后,可以选择日志文件;
     点击标签为“View”的按钮后,可以根据输入的条件查看日志内容;
     下面的多行文本框用来显示日志内容。
    图 6-1 程序效果图
    程序的目录结构如图 6-2 所示:
    当前目录./
    widgets/
    main.cpp
    Util.h, Util.cpp
    core/ LogRecord.h, LogRecord.cpp
    QtLogViewer.h,
    QtLogViewer.cpp
    LogContent.h, LogContent.cpp图 6-2 程序目录结构图
    其中:
     目录 core 及其中文件的功能和实验 5 相同;
     目录 widgets 及其中的文件功能和实验 5 相同,由于本实验不采用 Gtk+界
    面工具包,而是采用 Qt 界面工具包。因此, widgets 目录下两个文件共同定
    义了一个类 QtLogViewer,该类派生字 QWidget 类,完成图形界面构成、交
    互的功能;
     文件 main.cpp:为程序的主函数所在文件,通过对 QtLogViewer 的调用,完
    成程序的启动、交互和退出。
    三、实现步骤
  5. 根据目录结构创建目录和文件:
    为了方便组织程序代码,可以将本实验的代码放到单独的目录下。因此,先
    建立一个目录作为项目的根目录,本例中根目录命名为: ViewLogQt。
    • 在当前工作目录下,建立项目根目录: mkdir ViewLogQt
    • 进入根目录: cd ViewLogQt
    • 创建空白主程序文件: touch main.cpp
    • 创建目录 core: mkdir core
    • 创建目录 widgets: mkdir widgets
    • 进入目录 core: cd core
    • 创建目录 core 下的空白程序文件:
    touch Util.h
    touch Util.cpp
    touch LogRecord.h
    touch LogRecord.cpp
    touch LogContent.h
    touch LogContent.cpp
    • 进入目录 widgets: cd …/widgets
    • 创建目录 widgets 下的空白程序文件:
    touch QtLogViewer.h
    touch QtLogViewer.cpp
    • 目录文件创建完毕。
  6. 编辑文件:
    这里仅给出关键部分的代码。也可以参考实验 5 的代码。
    QtLogViewer.h
    1 #ifndef QTLOGVIEWER_H
    2 #define QTLOGVIEWER_H
    3 #include <qlcdnumber.h>
    4 #include <qlabel.h>
    5 #include <qlineedit.h>
    6 #include <qtextedit.h>
    7 #include <qpushbutton.h>
    8 #include <qlayout.h>
    9 #include 10 #include “LogContent.h”
    11 using namespace std;
    12 class QtLogViewer:public QWidget{
    13 Q_OBJECT
    14 private:
    15 string file_name; //记录文件名
    16 string kw_contains; //日志过滤条件中,需要包含的关键字
    17 string kw_not_contains; //日志过滤条件中,不需要包含的关
    键字
    18 int file_num; //指定目录中,特定日志文件的个数
    19 QLCDNumber* lcd_file_num; //用于显示文件个数的数码显示

    20 QLabel* lbl_file_name;
    21 QLineEdit* le_contains; //包含关键字输入文本框
    22 QLineEdit* le_not_contains; //不包含关键字输入文本框
    23 QTextEdit* te_logs; //显示日志的多行文本框
    24 QPushButton* btn_choose_file; //选择文件按钮
    25 QPushButton* btn_view; //查看日志按钮
    26 QGridLayout* grid; //摆放界面控件的表格布局
    27 LogContent log; //检索、过滤并填充日志记录集合的对象
    28 public:
    29 QtLogViewer( QWidget *parent=0, const char *name=0 );
    30 public slots: //定义的信号槽
    31 void onChooseFile(); //选择文件按钮的事件处理函数
    32 void onView(); //查看日志条目按钮的事件处理函数
    33 private:
    34 void init(); //初始化界面和关联事件处理函数
    35 int getFilesNumer(); //查询日志文件个数
    36 string getFileName(); //检索日志文件名称
    37 };
    38 #endif
    QtLogViewer 派生自 QWidget,集成了程序中所有的界面控件,并完成界面
    事件及其处理函数之间的关联。通过对 LogContent 的引用(引入 log 对象)组合
    进来了记录检索、过滤和填充的功能。
    main.cpp
    1 #include <qapplication.h>
    2 #include “QtLogViewer.h”
    3 int main(int argc, char *argv[]){
    4 QApplication app(argc, argv);
    5 QtLogViewer viewer;
    6 app.setMainWidget(&viewer);
    7 viewer.show();
    8 return app.exec();
    9 }main.cpp 功能代码都很简单,除了常规的初始化程序环境的功能外,唯一特
    殊的就是将QtLogViewer设为程序的主界面,利用QtLogViewer形成并显示界面。
  7. 编译文件:
    利用 qmake 功能编译程序,首先创建 qmake 的工程文件,这里将工程文件命
    名为 LogViewer.pro:
     生成项目文件,在项目根目录(ViewLogQt)下运行:
    qmake -project -o LogViewer.pro
     生成 Makefile: qmake -o Makefile LogViewer.pro
     生成程序: make
  8. 运行程序:
     ./LogViewer
    提示:由于目录 core 下的程序代码采用标准 C++规范而没有用到 Gtk+或者 Qt
    的扩展功能。因此,可以重用实验 5 中 core 目录下的代码,即,本实验 core 目
    录下的代码和实验 5 中 core 目录下的代码相同。
    (
    source /opt/FriendlyARM/arm-qtopia/qtopia-2.2.0-FriendlyARM/setQpeEnv
    qmake -project -o LogViewer.pro
    qmake -spec
    /opt/FriendlyARM/arm-qtopia/qtopia-2.2.0-FriendlyARM/qtopia/mkspecs/linux-g++
    -o Makefile LogViewer.pro
    )
    四、实验结果
    程序运行一下 嵌入式实验一样 和实验五类似 不要做界面 直接运行代码即可
    单引号注意一下输入

猜你喜欢

转载自blog.csdn.net/weixin_44736005/article/details/106222057