北京电子科技学院(BESTI)
实验报告
课程:信息安全系统设计基础 班级:1353
姓名:芦畅 傅冬菁
学号:20135308 20135311
成绩: 指导教师:娄家鹏 实验日期:2015.11.10
实验密级: 预习程度: 实验时间:15:30~18:00
仪器组次: 必修/选修: 实验序号:2
实验名称:实验二:固件设计
实验目的与要求:
1.掌握程序的烧写方法;
2.能够实现Bootloader;
3.实现密码学中常见算法的固化。
实验仪器:
名称 |
型号 |
数量 |
嵌入式开发平台 |
UP-NETARM2410-CL |
1 |
PC机 |
DELL |
1 |
实验内容、步骤与体会:
1.开发环境的配置同实验一。
2.将实验代码拷贝到共享文件夹中。
3.在虚拟机中编译代码。
输入命令:armv4l-unknown-linux-gcc pthread.c -o pthread -lpthread
对于多线程相关的代码,编译时需要加-lpthread的库。
编译成功。
4.下载调试
在超级终端中运行可执行文件pthread,可得实验结果如下所示。
运行可执行文件term。
执行term时出现下面的错误:
/dev/ttyS0: No such file or directory
可以通过建立一个连接来解决。
在超级终端中进入/dev文件夹中。
输入命令“ln –sf /dev/tts/0 /dev/ttyS0”
因为在 Linux 下串口文件位于/dev 下,一般在老版本的内核中串口一为/dev/ttyS0 ,串口二为 /dev/ttyS1, 在我们的开发板中串口设备位于/dev/tts/下,因为开发板中没有ttyS0这个设备,所以我们要建立一个连接。
实验分析:
(一)进程相关函数
线程在运行过程中需要和其他线程进行交互,在资源不能满足时,需要暂时挂起以等待其他线程正在使用的资源。这种机制称为线程之间同步。线程同步的方法主要有互斥锁、条件变量和信号量。
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);
判断2个线程ID是否指向同一线程:pthread_equal 头文件:#include<pthread.h> 函数原型:int pthread_equal(pthread_t thread1,pthread_t thread2);
用于保证init_routine(自己创建的线程)线程函数在进程中仅调用一次,无法再此调用:pthread_once 头文件:#include<pthread.h> 函数原型:int pthread_once(pthread_once_t *once_control,void(*init_routine)(void));
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 *));
为一个键设置使用数据: 头文件:#include<pthread.h> 函数原型:int pthread_setspecific(pthread_key_t key,(const void *pointer));
读取一个键的私有数据: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.异步信号
信号量本质上市一个非负的整数计数器,可以控制对公共资源的访问,其数据类型为sem_t,对应的头文件为semaphore.h
用来向特定线程发送信号: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 "); 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 "); 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); put(&buffer, n); } put(&buffer, OVER); printf("producer stopped! "); return NULL; } /*--------------------------------------------------------*/ void * consumer(void * data) { int d; while (1) { d = get(&buffer); if (d == OVER ) break; printf(" %d-->get ", d); } printf("consumer stopped! "); 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; }
串口通信分析
fd=open("/dev/ttyS1",O_NOCTTY|O_RDWR|O_NONBLOCK); if( fd < 0) { perror("Unable open /dev/ttyS0 "); return 1; }
这是文件I/O的常用函数,open函数,open函数用来打开一个设备,他返回的是一个整型变量,如果这个值等于-1,说明打开文件出现错误,如果为大于0的值,那么这个值代表的就是文件描述符。
这个事常用的一种用法fd是设备描述符,linux在操作硬件设备时,屏蔽了硬件的基本细节,只把硬件当做文件来进行操作,而所有的操作都是以open函数来开始,它用来获取fd,然后后期的其他操作全部控制fd来完成对硬件设备的实际操作。
O_RDWR 读写方式打开;
O_NOCTTY 不允许进程管理串口(不太理解,一般都选上);
O_NDELAY 非阻塞(默认为阻塞,打开后也可以使用fcntl()重新设置)
常用串口操作
写入:n = write(fd, "linux", 5); n实际写入字节数; 读取:res = read(fd,buf,len); res 读取的字节数; 设置:fcntl(fd, F_SETFL, FNDELAY); //非阻塞 fcntl(fd, F_SETFL, 0); // 阻塞 关闭:close(fd);
串口设置
struct termios options; // 串口配置结构体 tcgetattr(fd,&options); //获取当前设置 bzero(&options,sizeof(options)); options.c_cflag |= B115200 | CLOCAL | CREAD; // 设置波特率,本地连接,接收使能 options.c_cflag &= ~CSIZE; //屏蔽数据位 options.c_cflag |= CS8; // 数据位为 8 ,CS7 for 7 options.c_cflag &= ~CSTOPB; // 一位停止位, 两位停止为 |= CSTOPB options.c_cflag &= ~PARENB; // 无校验 //options.c_cflag |= PARENB; //有校验 //options.c_cflag &= ~PARODD // 偶校验 //options.c_cflag |= PARODD // 奇校验 options.c_cc[VTIME] = 0; // 等待时间,单位百毫秒 (读)。后有详细说明 options.c_cc[VMIN] = 0; // 最小字节数 (读)。后有详细说明 tcflush(fd, TCIOFLUSH); // TCIFLUSH刷清输入队列。 TCOFLUSH刷清输出队列。 TCIOFLUSH刷清输入、输出队列。 tcsetattr(fd, TCSANOW, &options); // TCSANOW立即生效; TCSADRAIN:Wait until everything has been transmitted; TCSAFLUSH:Flush input and output buffers and make the change
1.打开串口函数open_port()中要实现的函数: (1)open("/dev/ttys0",O_RDWR | O_NOCTTY | O_NDELAY);/*打开串口0*/ (2)fcntl(fd,F_SETFL,0)/*恢复串口为阻塞状态*/ (3)isatty(STDIN_FILENO) /*测试是否为中断设备 非0即是中断设备*/ 2.配置串口参数函数set_opt()中要实现的函数: (1)保存原先有串口配置 tcgetattr(fd,&oldtio); (2)先将新串口配置清0 bzore(&newtio,sizeof(newito)); (3)激活选项CLOCAL和CREAD 并设置数据位大小 newtio.c_cflag |=CLOCAL | CREAD; newtio.c_cflag &= ~CSIZE; newtio.c_cflag |=CS8; (4)设置奇偶校验 奇校验: newtio.c_cflag |= PARENB; newtio.c_cflag |= PARODD; newtio.c_iflag |= (INPCK | ISTRIP); 偶校验: newtio.c_iflag |= (INPCK | ISTRIP); newtio.c_cflag |= PAREND; newtio.c_cflag &= ~PARODD; 无奇偶校验: newtio.c_cflag &= ~PARENB; (5) 设置停止位 newtio.c_cflag &= ~CSTOPB; /*停止位为1*/ newtio.c_cflag |= CSTOPB;/*停止位为0*/ (6)设置波特率: cfsetispeed(&newtio,B115200); cfsetospeed(&newtio,B115200); (7)设置等待时间和最小接受字符: newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; (8)处理为接收字符: tcflush(fd,TCIFLUSH); (9)激活新配置: tcsetattr(fd,TCSANOW,&newtio); 3.读写串口 write(fd,buff,8); read(fd,buff,8);
遇到的问题及解决:
实验二在实验一环境搭建下进行得很顺利,直到最后一步,一直不成功,应该是没有输入正确命令,要严格到每一个空格,大小写完全相同,后来尝试了几遍后终于成功了。
经过查阅资料,我明白:串行端口终端(Serial Port Terminal)是使用计算机串行端口连接的终端设备。计算机把每个串行端口都看作是一个字符设备。有段时间这些串行端口设备通常被称为终端设备,因为 那时它的最大用途就是用来连接终端。这些串行端口所对应的设备名称是/dev/tts/0(或/dev/ttyS0), /dev/tts/1(或/dev/ttyS1)等,设备号分别是(4,0), (4,1)等,分别对应于DOS系统下的COM1、COM2等。若要向一个端口发送数据,可以在命令行上把标准输出重定向到这些特殊文件名上即可。例如, 在命令行提示符下键入:echo test > /dev/ttyS1会把单词”test”发送到连接在ttyS1(COM2)端口的设备上。