信息安全系统设计基础 实验二 固件设计 20135321余佳源 20135323符运锦

北京电子科技学院(BESTI)

实验报告

课程:信息安全系统设计基础 班级:1353

姓名:余佳源 符运锦

学号:20135321 20135323

成绩: 指导教师:娄家鹏 实验日期:2015.11.10

实验密级: 预习程度: 实验时间:15:30~18:00

仪器组次: 必修/选修: 实验序号:2

实验名称:固件设计

实验目的与要求:

  1. 掌握程序的烧写方法
  2. 能够实现Bootloader
  3. 实现密码学中常见算法的固化。

内容:

  • 读懂 pthread.c 的源代码,熟悉几个重要的PTHREAD库函数的使用。
  • 掌握共享锁和信号量的使用方法。
  • 进入/arm2410cl/exp/basic/02_pthread 目录,运行make 产生pthread程序,使用NFS方式连接开发主机进行运行实验。

实验仪器:
名 称 型 号 数 量
PC Lenovo 1
Laptop Sony 1
虚拟Linux环境 Redhat 9.0 1
ARM开发板 UP-NETARM2410-CL 1


实验内容、步骤与体会:

  1. 开发环境的配置同实验一
  2. 将实验代码拷贝到共享文件夹中。
  3. 在虚拟机中编译代码,对于多线程相关的代码,编译时需要加-lpthread 的库。
  4. 下载调试,在超级终端中运行可执行文件pthread,运行可执行文件term。

配置实验环境无需太多赘述,同实验一。


将实验代码拷贝到共享文件夹中


实验原理及代码解析

  1. 原理背景
    本实验为著名的生产者-消费者问题模型的实现,主程序中分别启动生产者线程和消费者线程。生产者线程不断顺序地将0到1000的数字写入共享的循环缓冲区,同时消费者 线程不断地从共享的循环缓冲区读取数据。

  2. 线程相关函数

(1)创建进程

线程创建函数: pthread_create
头文件:#include < pthread.h >
函数原型:int pthread_create(pthread_t *thread,pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);


获得父进程 ID:pthread_self
头文件:#include<pthread.h>
函数原型:pthread_t pthread_self(void);


测试两个线程号是否相同:pthread_equal
头文件:#include<pthread.h>
函数原型:int pthread_equal(pthread_t thread1,pthread_t thread2);

(2)终止进程

线程退出:pthread_exit
头文件:#include<pthread.h>
函数原型:void pthread_exit(void *retval);

用于自动释放资源函数:pthread_cleanup_push(),pthread_cleanup_pop()
头文件:#include<pthread.h>
函数原型:#define pthread_cleanup_push(routine,arg)\
    {struct _pthread_cleanup_buffer buffer;\
     _pthread_cleanup_push(&buffer,(routine),(srg));
    #define pthread_cleanup_pop\
     _pthread_clean_pop(&buffer,(exeute));
    }

用来等待一个线程的结束。函数原型为: pthread_join
头文件:#include<pthread.h>
函数原型:int pthread_join(pthread_t th,void *thread_return);

(3)私用数据

创建一个键:pthread_key_create
头文件:#include<pthread.h>
函数原型:int pthread_key_create(pthread_key_t *key,void(*destr_function)(void *));

读取一个键的私有数据:pthread_getspecific
头文件:#include<pthread.h>
函数原型:void *pthread_getspecific(pthread_key_t key);

删除一个键:pthread_key_delete
头文件:#include<pthread.h>
函数原型:int pthread_key_delete(pthread_key_t key);

(4)互斥锁
线程在运行过程中需要使用共享资源时,要保证该线程独占该资源,之中机制称为互斥。
使用互斥锁来可实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。

初始化一个互斥锁:pthread_mutex_init
头文件:#include<pthread.h>
函数原型:int pthread_mutex_init(pthread_mutex_t *mutex,const     pthread_mutexattr_t *mutexattr);

注销一个互斥锁:pthread_mutex_destory
头文件:#include<pthread.h>
函数原型:int pthread_mutex_destroy(pthread_mutex_t *mutex);

加锁,如果不成功,阻塞等待:pthread_mutex_lock
头文件:#include<pthread.h>
函数原型:int pthread_mutex_lock(pthread_mutex_t *mutex);

解锁:pthread_mutex_unlock
头文件:#include<pthread.h>
函数原型:int pthread_mutex_unlock(pthread_mutex_t *mutex);

(5)条件变量
而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。

初始化条件变量:pthread_cond_init
头文件:#include<pthread.h>
函数原型:int pthread_cond_t cond=PTHREAD_COND_INITIALIZER;
      int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
      
基于条件变量阻塞,无条件等待:pthread_cond_wait
头文件:#include<pthread.h>
函数原型:int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

阻塞直到指定事件发生,计时等待:pthread_cond_timedwait
头文件:#include<pthread.h>
函数原型:int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex,const struct timespec *abstime);


解除特定线程的阻塞,存在多个等待线程时按入队顺序激活其中一个:pthread_cond_signal
头文件:#include<pthread.h>
函数原型:int pthread_cond_signal(pthread_cond_t *cond);


解除所有线程的阻塞:pthread_cond_broadcast
头文件:#include<pthread.h>
函数原型:int pthread_cond_broadcast(pthread_cond_t *cond);

清除条件变量:pthread_cond_destroy
头文件:#include<pthread.h>
函数原型:int pthread_cond_destroy(pthread_cond_t *cond);

(6)异步信号

用来向特定线程发送信号:pthread_kill
头文件#include<pthread.h>
函数原型:int pthread_kill(pthread_t threadid,int signo);

设置线程的信号屏蔽码:pthread_sigmask(但对不允许屏蔽的Cancel信号和不允许响应的Restart信号进行了保护)
头文件#include<pthread.h>
函数原型:int pthread_sigmask(int how,const sigset_t *newmask,sigset_t *oldmask);

阻塞线程:sigwait
头文件#include<pthread.h>
函数原型:int sigwait (const sigset_t *set,int *sig);

程序源代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "pthread.h"

#define BUFFER_SIZE 16

/* 设置一个整数的循环缓冲区。 */
struct prodcons {
  int buffer[BUFFER_SIZE];      /* 缓存区数组 */
  pthread_mutex_t lock;         /* 互斥锁:互斥保证缓冲区的互斥访问 */
  int readpos, writepos;        /* 读写的位置 */
  pthread_cond_t notempty;      /* 当缓冲区没有空信号*/
  pthread_cond_t notfull;       /* 当缓冲区没有满信号 */
};

/*--------------------------------------------------------*/
/* 初始化缓冲区 */
void init(struct prodcons * b)
{
  pthread_mutex_init(&b->lock, NULL);
  pthread_cond_init(&b->notempty, NULL);
  pthread_cond_init(&b->notfull, NULL);
  b->readpos = 0;
  b->writepos = 0;
}
/*--------------------------------------------------------*/
/*在存储缓冲区中写入整数*/
void put(struct prodcons * b, int data)
{
    pthread_mutex_lock(&b->lock);

      /* 等待缓冲区非满 */
     while ((b->writepos + 1) % BUFFER_SIZE == b->readpos) {
        printf("wait for not full\n");
        pthread_cond_wait(&b->notfull, &b->lock);
      }
  /* 写入数据,并提前写指针*/
      b->buffer[b->writepos] = data;
      b->writepos++; /* 缓冲区指针加1*/
if (b->writepos >= BUFFER_SIZE) b->writepos = 0;
  /* 信号缓冲区此时非空 */
      pthread_cond_signal(&b->notempty);/* 发缓冲区不空信号 */

    pthread_mutex_unlock(&b->lock);
}
/*--------------------------------------------------------*/
/* 读取并从缓冲区中删除一个整数 */
int get(struct prodcons * b)
{
      int data;
    pthread_mutex_lock(&b->lock);

     /* 等待缓冲非空 */
      while (b->writepos == b->readpos) {
        printf("wait for not empty\n");
        pthread_cond_wait(&b->notempty, &b->lock);
      }
      /*读取数据并提前读取指针*/
      data = b->buffer[b->readpos];
      b->readpos++;
/*读取指针加1*/
if (b->readpos >= BUFFER_SIZE) b->readpos = 0;
      /* 信号缓冲区现在非满*/
      pthread_cond_signal(&b->notfull);

      pthread_mutex_unlock(&b->lock);
      return data;
}
/*--------------------------------------------------------*/
#define OVER (-1)
struct prodcons buffer;
/*--------------------------------------------------------*/
void * producer(void * data)
{
      int n;
      for (n = 0; n < 1000; n++) {
        printf(" put-->%d\n", n);
        put(&buffer, n);
    }
  put(&buffer, OVER);
  printf("producer stopped!\n");
  return NULL;
}
/*--------------------------------------------------------*/
void * consumer(void * data)
{
  int d;
  while (1) {
    d = get(&buffer);
    if (d == OVER ) break;
    printf("              %d-->get\n", d);
  }
  printf("consumer stopped!\n");
  return NULL;
}
/*--------------------------------------------------------*/
int main(void)
{
      pthread_t th_a, th_b;
      void * retval;

      init(&buffer);
     pthread_create(&th_a, NULL, producer, 0);
      pthread_create(&th_b, NULL, consumer, 0);
  /*等待生产者和消费者的结束。*/
      pthread_join(th_a, &retval);
      pthread_join(th_b, &retval);

      return 0;
}

串行口代码分析

头文件

#include <stdio.h> /*标准输入输出定义*/
#include <stdlib.h> /*标准函数库定义*/
#include <unistd.h> /*linux 标准函数定义*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> /*文件控制定义*/
#include <termios.h> /*PPSIX 终端控制定义*/
#include <errno.h> /*错误号定义*/
#include <pthread.h>  /*线程库定义*/

打开串口是通过标准的文件打开函数来实现的

int fd;
fd = open( "/dev/ttyS0", O_RDWR); /*以读写方式打开串口*/
if (-1 == fd)/* 不能打开串口一*/
{ 
    perror(" 提示错误!");
}

串口设置

最基本的设置串口包括波特率设置,效验位和停止位设置。串口的设置主要是设置struct termios结构体的各成员值。

- 波特率设置:
struct termios Opt;
tcgetattr(fd, &Opt);
cfsetispeed(&Opt,B19200); /*设置为 19200Bps*/
cfsetospeed(&Opt,B19200);
tcsetattr(fd,TCANOW,&Opt);

- 校验位和停止位的设置:
无效验 8 位
Option.c_cflag &= ~PARENB;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS8;
奇效验(Odd) 7 位
Option.c_cflag |= ~PARENB;Option.c_cflag &= ~PARODD;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS7;
偶效验(Even) 7 位
Option.c_cflag &= ~PARENB;Option.c_cflag |= ~PARODD;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= ~CSIZE;Option.c_cflag |= ~CS7;
Space 效验 7 位
Option.c_cflag &= ~PARENB;Option.c_cflag &= ~CSTOPB;Option.c_cflag &= &~CSIZE;Option.c_cflag |= CS8;

- 设置停止位:
1 位:options.c_cflag &= ~CSTOPB;
2 位:options.c_cflag |= CSTOPB;

注:如果不是开发终端,只是串口传输数据,而不需要串口来处理,那么使用原始模式(Raw Mode) 方式来通讯,设置方式如下:
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
options.c_oflag &= ~OPOST; /*Output*/

读写、关闭串口

设置好串口之后,读写串口就很容易了,把串口当作文件读写就可以了。

- 发送数据:
char buffer[1024];
int Length=1024;
int nByte;
nByte = write(fd, buffer ,Length)

- 读取串口数据:
使用文件操作read函数读取,如果设置为原始模式(Raw Mode)传输数据,那么read函数返回的字符数是实际串口收到的字符数。可以使用操作文件的函数来实现异步读取,如 fcntl,或者select等来操作。

char buff[1024];
int Len=1024;
int readByte = read(fd, buff, Len);

- 关闭串口就是关闭文件。
close(fd);


生产者写入共享的循环缓冲区函数 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);                     //释放互斥锁 } 


消费者读取共享的循环缓冲区函数 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,不空则跳出阻塞。否则无数据可读。    
    }    
    data = 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; 
    } 

在虚拟机中编译代码

进入 exp/basic/02_pthread 目录,使用 vi 编辑器或其他编辑器阅读理解源代码。运 行 make 产生 pthread 可执行文件


下载调试

到超级终端窗口,使用 NFS mount 开发主机的/arm2410cl 到/host 目录。
进入/host/exp/basic/02_pthread 目录,运行 pthread,观察运行结果的正确性。


实验成功截图

运行可执行文件 term


注意事项及遇到的问题:

在执行./term 时有可能出现下面的错误
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
/dev/ttyS0: No such file or directory

可以通过方法建立一个连接来解决。
在 Linux 下串口文件位于/dev 下,一般在老版本的内核中串口一为
/dev/ttyS0 ,串口二为 /dev/ttyS1, 在我们的开发板中串口设备位于
/dev/tts/下,因为开发板中没有ttyS0这个设备,所以我们要建立一个连接。
首先在超级终端中进入/dev文件夹中。输入命令“ln –sf /dev/tts/0 /dev/ttyS0”注意空格与字母l、数字0。


参考资料

参考资料1:Linux多进程实例与代码优化
参考资料2:Linux下的多线程机制--生产者和消费者实例
参考资料3:缓冲区实现(一)——循环缓冲区(操作固定长度数据)
参考资料4:由环形队列消锁引发的思考

posted @ 2015-11-22 22:12  20135321余佳源  阅读(223)  评论(0编辑  收藏  举报