赞
踩
高内聚低耦合的含义是什么?
Bootloader的含义及作用:
使用交叉编译器之前还需要安装一下其它的库
处理器指令执行过程还是太抽象了,请给一种比较通俗的解释:、
处理器指令执行过程(通俗可以理解为让指令与数据结合,发生化学反应的过程)包括:
取值 从IR中取出指令
译码 解释指令
执行 读取数据
存储 保存执行结果
可以保存部分程序和数据的所谓“外部介质”是什么。
“接口”是什么?接口除了有编程级的接口外,还有什么别的级别吗?
什么是OEM和OEM板机厂商?
什么是“UNIX”哲学?
“基于Flash”的文件系统中FLash是什么?
已知fs代表文件系统,那么procfs,devfs,susfs的英文全称及其含义是什么?
Linux模块机制有点难,试着用更形象简单的语言解释他
linux中“镜像”的含义是什么?
linux中uid和suid的英文全称及其含义:
文件操作函数实例:
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/ types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #define BUFFER SIZE 128 //每次读写缓存 大小,影响运行效率
- #define SRC_FILE_NAME "src_file.txt" //源文件名
- #define DEST_FILE_NAME "dest_file.txt" //目标文件名
- #define OFFSET 0 //文件指针偏移量
-
- int main (){
- int sre_file, dest_file;
- unsigned char src_ buff[BUFFER_SIZE] ;
- unsigned char dest_ buff[BUFFER_ SIZE];
- int reaL_ read_ len = 0;
- char str[BUFFER_ SIZE] = "this is a testabout\nopen()\nclose()\nwrite()\nread()\nlseek()\nend of the file\n"; //创建源文件
- src_ file=open(SRC_ FILE_NAME,O_RDWR|0_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_ IROTH);
- if(sre_ file<0)
- {
- printf ("create file error!!!\n") ;
- exit(1);
- }
- //向源文件中写数据
- write(src_file, str, sizeof(str));
- //创建目的文件
- dest_ file=open(DEST_FILE_NAME,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (dest_ file<0)
- {
- printf("open file error!!!\n");
- exit (1) ;
- }
- 1seek (sre_ file, OFFSET, SEEK_SET) ;//将源文件的读写指针移到起始位置
- while(real_read_len=read(sre_file,src_buff,sizeof (src_buff)))>0)
- {
- printf("src_file:%s",src_buff);
- write(dest_file,src_buff, real_rea_len);
- 1seek (dest_file, OFFSET, SEK_ SET);//将目的文件的读写指针移到起始位置
- }
- while((real_ read_len=read (dest file, dest_ buff,sizeof (dest_buff))>0);//读取目的文件的内容
- printf("dest_file:%s",dest_ buff);
- close(src_file);
- close (dest_file) ;
- return 0;
- }
- #include <linux/init.h> //所有模块都会需要这个头文件
- #include <linux/module.h> //下面的宏需要
-
- MODULE_LICENSE("Dual BSD/GPL"); //告诉内核,该模块采用自由许可证
- static int __init print_init(void)//模块被袁载到内核时调用
- {
- printk(KERIN_ALERT"init successfully\n"); //函数printk在umn内核中定义,功能和标准C库中的函数printf类似
- return 0;
- }
-
- static void __exit print_exit(void)//模块被移除出内核时调用
- {
- printk (KERN ALERT "exit successfully\n");
- return 0;
- }
-
- module_init (print_init);//模块加载函数
- module_exit(print_exit);//模块 卸载函数
-
内存分配与重定位分别是什么含义?
“接口”的本质是不是就是函数?
Linux中协议栈指的是什么?
任务号与排期、下发任务之间有什么关系?排期的过程是什么?
任务下发和展示如何区分线程级和进程级等?
Linux调度器是如何以模块方式提供的呢?
TLB中如果没有虚拟地址的入口时,“转换表遍历硬件”是什么样的操作?“从存放在内存的转换表中获得转换和访问器权限”又是什么操作?
什么是底层的Buddy算法?
Linux中的mount操作是什么?
消息队列标识符和IPC标识符分别是什么?
锁机制是什么?为什么说信号量可以被用作一个锁机制?
在完成任务前,先来理一下基本的原理及其代码
fork工作原理实例:
- #include <sys/types.h>
- #include <unistd.h>
- #include <std1o.h>
- Hinclude <stdlib.h>
- int main (vo1d){
- p1d_ t pid;
- char message ;
- int n;
- pid=fork();
- if (pid < 0)
- {
- perror ("fork falled");
- exit(1);
- }
- if(pid==0)
- {
- printf("This 1s the child process. My PID is; d. My PPID 18: %d.\n",getpid(),getppid()) ;
- }
- else
- {
- printf ("This is the parent process. My PID is 8d. \n", getpid());
- }
- return 0;
- }
已知:
vfork工作原理实例
- #include<stdio.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include<stdlib.h>
- struct sched_param param; //调度策略参数
- int main()
- {
- pid_t pid;
- int cnt = 0;
- pid = vfork();
- if(pid > 0)
- {
- while(1)
- {
- printf("this is father pid = %d\n",getpid());
- sleep(2);
- }
- }
- else if(pid == 0)
- {
- if (sched_setscheduler(getpid(), SCHED_FIFO, ¶m) == -1)
- {
- perror("sched_setscheduler");
- exit(EXIT_FAILURE);
- }
- while(1)
- {
- printf("this is person pid,person pid=%d\n",getpid());
- sleep(2);
- cnt++;
- if(cnt == 3)
- {
- exit(0);
- }
- }
- }
- return 0;
- }
exec函数族成员工作原理应用实例:
- //exec让当前进程执行指定的程序,通常fork之后在子进程中使用。exec 函数一旦调用成功即执行新的程序,不返回。只有失败才返回 -1。
- //资源调度就结果来说,算是“换核不换壳”
- int execl(const char *path, const char *arg, ...);
- int execlp(const char *file, const char *arg, ...);
- int execle(const char *path, const char *arg, ...,char *const envp[]);
- int execv(const char *path, char *const argv[]);
- int execvp(const char *file, char *const argv[]);
- int execve(const char *path, char *const argv[], char *const envp[]);
- //6个函数都是在exec的基础上加上几个字母作为函数名。添加字母和对应含义如下:
- l (list) 命令行参数列表
- p (path) 搜素file时使用path变量
- v (vector) 使用命令行参数数组
- e (environment) 使用环境变量数组,不使用进程原有的环境变量,设置新加载程序运行的环境变量
我们结合上述三个工作原理实例,完成下面“进程指定执行程序的实例”
首先创建一个所谓”要执行的程序“,即一个.cpp文件,然后使用gcc编译器编译这个源程序文件,并取名为multiexec
- // multiexec.cpp
- #include<iostream>
- #include<cstdio>
- int main(int argc, char* argv[])
- {
- std::cout << __FILE__ << " " << __func__ << " " << __LINE__ << std::endl;
- if(argc > 1)
- {
- for(int i = 0; i < argc; ++i)
- {
- printf("argv[%d]: %s\n", i, argv[i]);
- }
- }
- return 0;
- }
要执行的程序multiexec创建好后,就要再写一个程序,使进程指定multiexec进行执行操作
- // multiprocess.cpp
- #include<iostream>
- #include<unistd.h>
- #include<cstdio>
- int main()
- {
- pid_t pid = fork();
- if( 0 == pid )
- {
- std::cout << "hi, I'am a child process and the process id is " << getpid()<<std::endl;
- execl("./multiexec","multiexec","arg1","arg2",NULL); //执行全新代码
- }
- else if(pid > 0)
- {
- std::cout << "hi, I'am a parent process and the process id is " << getpid() <<std::endl;
- }
- return 0;
- }
进程相关的基础代码就到这里了,下面开始进入线程的基础代码实例
pthread的基础API:
- /**************初始化与销毁线程对象***********/
- (1)初始化一个线程对象的属性
- 函数原型:
- int pthread_attr_init(pthread_attr_t *attr);
- 若函数调用成功返回0,否则返回对应的错误代码。
- attr:指向一个线程属性的指针
- (2)销毁一个线程属性对象
- pthread_attr_destroy()函数销毁。
- 函数原型:
- int pthread_attr_destroy(pthread_attr_t *attr);
- 若函数调用成功返回0,否则返回对应的错误代码。
- attr:指向一个线程属性的指针
-
-
- /*****************创建与销毁线程****************/
- pthread_create()函数是用于创建一个线程的,创建线程实际上就是确定调
- 用该线程函数的入口点。
- 函数原型:
- int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void
- *(*start_routine) (void *), void *arg);
- 参数说明:
- 1) pthread_t *thread:pthread_t是一种用于表示线程的数据类型,每一个pthread_t类型的变量都可以表示一个线程。
- 2) const pthread_attr_t *attr:用于手动设置新建线程的属性,例如线程的调用策略、线程所能使用的栈内存的大小等。
- 3) void *(*start_routine) (void *):以函数指针的方式指明新建线程需要执行的函数。
- 4)void *arg:指定传递给start_routine()函数的实参,当不需要传递任何数据时,将arg赋值为NULL即可。
-
- void pthread_exit(void *retval);
- //retval:如果retval不为空,则会将线程的退出值保存到retval中,如果不关心线程的退出值,形参为NULL即可
-
- pthread_join()
- //数以阻塞的方式等待thread指定的线程结束,并且获取它的退出值
-
-
- /***************线程调度相关的API,看不懂就不必看了**************/
- policy:可选值为线程的三种调度策略,SCHED_OTHER、SCHED_FIFO、SCHED_RR。
- POSIX标准指定了三种调度策略:
- ➢ 分时调度策略 (SCHED_OTHER)。
- ➢ 实时调度策略,先进先出方式调度(SCHED_FIFO)。
- ➢ 实时调度策略 ,时间片轮转方式调度(SCHED_RR)。
pthread工作原理实例:
- void *test_thread(void *arg) /* 线程执行代码 */
- {
- int num = (unsigned long long)arg;
- printf("arg is %d\n", num);
- while(1) {
- ……
- if( ?? )
- pthread_exit(NULL);
- }
- }
-
-
- int main(void)
- {
- pthread_t thread;
- void *thread_return;
- int arg = 520; //这个arg的含义以后会详细解释
- int res;
- //尝试创建一个线程
- printf("start create thread\n");
- res = pthread_create(&thread, NULL,test_thread, (void*)(unsigned long long)(arg));
- if(res != 0)
- {
- printf("create thread fail\n");
- exit(res);
- }
- printf("create treads success\n");
-
- //以阻塞的方式等待刚创建的线程退出
- printf("waiting for threads to finish...\n");
- res = pthread_join(thread,&thread_return);
- if(res != 0)
- {
- printf("thread exit fail\n");
- exit(res);
- }
- printf("thread exit ok!\n");
- return 0;
- }
现在我们正式开始任务的完成:
1.导入所有的库函数。这里导入的库函数一定是过多的,但做实验的话还是通用性比较重要,以后也直接将所有库函数导入完就好了。
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
-
- #include<pthread.h>
-
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sched.h>
2.定义创建进程的函数。我们在创建进程时不调用set_realtime_scheduler()
函数。这样,新创建的进程将继承父进程的调度策略,即默认的非实时策略。而设置调度策略为实施策略的函数我将在下一步讲解
- /************ 创建非实时进程********************/
- void create_nonrealtime_processes() {
- pid_t pid = fork();
- if (pid == 0) {
- printf("pid:%d ",getpid()); //打印自己的pid
- printf("Create the first non-real-time process successfully\n");
- exit(0);
- }
- else if (pid > 0) {
- pid = fork();
- if (pid == 0) {
- printf("Create the second non-real-time process successfully\n");
- exit(0);
- }
- }
- }
3.因为要创建实时进程,所以要定义函数来设置进程调度策略为实时策略
- /**********************创建real-time进程*******************/
- //创建进程
- void create_processes() {
- pid_t pid;
- int i;
-
- for (i = 0; i < 2; i++) //循环两次,表示每个进程创建两个子进程
- {
- pid = fork();
- if (pid == 0) {
- // 子进程
- printf("pid1:%d \n",getpid()); //打印自己的pid
- //create_threads(); //创建线程的函数
- printf("create real-time process successfully\n");
- break;
- } else if (pid < 0) {
- // 出错处理
- perror("fork() error");
- break;
- }
- }
- }
-
- // 设置进程调度策略为实时策略
- void set_realtime_scheduler() {
- struct sched_param param;
- param.sched_priority = 99; // 设置实时优先级,取值范围为1-99
-
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
- perror("sched_setscheduler() error");
- }
- }
-
- // 创建实时进程
- void create_realtime_processes() {
- // 创建实时进程
- set_realtime_scheduler();
- create_processes();
-
- exit(0);
- }
在主函数中,我写到:
- // 创建非实时进程
- create_nonrealtime_processes();
-
- // 创建实时进程
- set_realtime_scheduler();
- create_realtime_processes();
在测试创建进程的任务中,先将所有create_thread相关代码注释掉,得到测试结果。可见,两个实时进程和非实时进程创建成功。测试代码为“work2_1.cpp”
4.定义创建线程的函数
- // 创建线程
- void create_threads() {
- pthread_t threads[2];
- int i;
- for (i = 0; i < 2; i++) {
- pthread_create(&threads[i], NULL, thread_function, NULL);
- //这句语句中的thread_function是一个函数,相当于创建这个线程时所要实现的功能。这个函数会在下面定义
- }
- }
5.定义线程的功能函数
首先是任务可中断睡眠。"任务可中断睡眠"指进程进入睡眠状态,可以被外部中断唤醒。在 Linux 系统中,可以使用 sleep
或 usleep
系统调用来实现可中断睡眠。例如,sleep(5)
表示进程睡眠 5 秒钟。如果在睡眠期间接收到信号,进程会被唤醒。
- // 线程函数
- void* thread_function1(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after sleep”
- //任务可中断睡眠
- sleep(1);
- printf("Task is awake after a second sleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
然后是任务不可中断睡眠。"任务不可中断睡眠"指进程进入睡眠状态,无法被外部中断唤醒。在 Linux 系统中,可以使用 pause
系统调用来实现不可中断睡眠。进程会一直睡眠,直到接收到一个信号。通常,用于等待信号的时候会使用 pause
函数。
- // 线程函数
- void* thread_function2(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after usleep”
- //任务不可中断睡眠
- usleep(10);
- pause();
-
- printf("Task is awake after usleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
最后是暂停。"任务暂停"指进程的执行被暂停,直到接收到某个特定的信号。在 Linux 系统中,可以使用 pause
或 sigsuspend
系统调用来实现任务暂停。调用这些函数后,进程将一直等待,直到接收到一个指定的信号才会继续执行。
- // 线程函数
- void* thread_function3(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他暂停后打印“Task is awake after pause”
- //暂停
- pause();
- printf("Task is awake after pause");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
下面就休眠来做个实验,创建以上两个线程后,分别让“可中断线程”休眠1秒和5秒,观察终端的输出结果:
休眠1秒时,tid1比tid2先创建,且不可中断睡眠并没有执行在pause之后的printf语句
休眠5秒时,tid2比tid1先创建
从线程创建的个数可以看到,创建线程的过程被pause中断了,需要在外部中断或其他方式将其唤醒。测试代码为work2_2.cpp
6.任务要求用一个进程来测试进程退出过程,我将在主函数的最开头写入如下代码
- // 设置一个进程来测试进程退出过程
- pid_t pid = fork();
- if (pid == 0)
- {
- // 子进程
- while (1) {
- // 进程中执行的任务,这里我什么也不执行
- // TODO: 在这里进行进程退出过程的测试
-
- // 退出进程并输出消息
- printf("Process exits successfully\n");
- exit(0);
- }
- }
以上代码的含义是:子进程将无限循环执行任务,然后调用exit(0)
函数退出进程,并在退出处输出"Process exits successfully"
的消息。注意在使用gcc编译包换线程相关内容的c++代码时,请务必加上“-lpthread”,以下为测试成功截图,测试代码为“work2.cpp”:
最后的最后,我将所有测试代码的源码放在这里
- //work2.cpp
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
-
- #include<pthread.h>
-
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sched.h>
-
- void create_threads();
- void create_process();
- void set_realtime_scheduler();
- void* thread_function1(void* arg);
- void* thread_function2(void* arg);
- void* thread_function3(void* arg);
-
-
-
-
- // 创建线程
- void create_threads() {
- pthread_t threads[2];
- int i;
- for (i = 0; i <= 2; i++) {
- if(i==0)
- pthread_create(&threads[i], NULL, thread_function1, NULL);
- else if(i==1)
- pthread_create(&threads[i], NULL, thread_function2, NULL);
- else
- pthread_create(&threads[i], NULL, thread_function3, NULL);
- //这句语句中的thread_function是函数,相当于创建这个线程时所要实现的功能。这个函数会在下面定义
- }
- }
-
- // 线程函数
- void* thread_function1(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after sleep”
- //任务可中断睡眠
- sleep(1);
- printf("Task is awake after a second sleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function2(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after usleep”
- //任务不可中断睡眠
- usleep(10000);
-
- printf("Task is awake after usleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function3(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他暂停后打印“Task is awake after pause”
- //暂停
- pause();
- printf("Task is awake after pause");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- //创建进程
- void create_processes() {
- pid_t pid;
- int i;
-
- for (i = 0; i < 2; i++) //循环两次,表示每个进程创建两个子进程
- {
- pid = fork();
- if (pid == 0) {
- // 子进程
- printf("pid1:%d\n ",getpid()); //打印自己的pid
- //create_threads(); //创建线程的函数
- break;
- } else if (pid < 0) {
- // 出错处理
- perror("fork() error");
- printf("\n");
- break;
- }
- }
- }
-
- // 设置进程调度策略为实时策略
- void set_realtime_scheduler() {
- struct sched_param param;
- param.sched_priority = 99; // 设置实时优先级,取值范围为1-99
-
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
- perror("sched_setscheduler() error");
- }
- }
-
-
-
- int main() {
-
- // 设置一个进程来测试进程退出过程
- pid_t pid = fork();
- if (pid == 0)
- {
- // 子进程
- while (1) {
- // 进程中执行的任务,这里我什么也不执行
- // TODO: 在这里进行进程退出过程的测试
-
- // 退出进程并输出消息
- printf("Process exits successfully\n");
- exit(0);
- }
- }
-
- }
- //work2_1.cpp
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
-
- #include<pthread.h>
-
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sched.h>
-
- void create_nonrealtime_processes();
- void create_realtime_processes();
- void create_threads();
- void create_process();
- void set_realtime_scheduler();
- void* thread_function1(void* arg);
- void* thread_function2(void* arg);
- void* thread_function3(void* arg);
- /************ 创建非实时进程********************/
- void create_nonrealtime_processes() {
- pid_t pid = fork();
- if (pid == 0) {
- printf("pid:%d \n",getpid()); //打印自己的pid
- printf("Create the first non-real-time process successfully\n");
- exit(0);
- }
- else if (pid > 0) {
- pid = fork();
- if (pid == 0) {
- printf("Create the second non-real-time process successfully\n");
- exit(0);
- }
- }
- }
-
- /**********************创建real-time进程*******************/
- void create_processes() {
- pid_t pid;
- int i;
-
- for (i = 0; i < 2; i++) //循环两次,表示每个进程创建两个子进程
- {
- pid = fork();
- if (pid == 0) {
- // 子进程
- printf("pid1:%d \n",getpid()); //打印自己的pid
- create_threads(); //创建线程的函数
- printf("create real-time process successfully\n");
- break;
- } else if (pid < 0) {
- // 出错处理
- perror("fork() error");
- break;
- }
- }
- }
- // 设置进程调度策略为实时策略
- void set_realtime_scheduler() {
- struct sched_param param;
- param.sched_priority = 99; // 设置实时优先级,取值范围为1-99
-
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
- perror("sched_setscheduler() error");
- }
- }
- // 创建实时进程
- void create_realtime_processes() {
- // 创建实时进程
- set_realtime_scheduler();
- create_processes();
-
- exit(0);
- }
-
-
-
- /********************thread relevant*********************/
- // 创建线程
- void create_threads() {
- pthread_t threads[2];
- int i;
- for (i = 0; i <= 2; i++) {
- if(i==0)
- pthread_create(&threads[i], NULL, thread_function1, NULL);
- else if(i==1)
- pthread_create(&threads[i], NULL, thread_function2, NULL);
- else
- pthread_create(&threads[i], NULL, thread_function3, NULL);
- //这句语句中的thread_function是函数,相当于创建这个线程时所要实现的功能。这个函数会在下面定义
- }
- }
-
- // 线程函数
- void* thread_function1(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after sleep”
- //任务可中断睡眠
- sleep(1);
- printf("Task is awake after a second sleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function2(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after usleep”
- //任务不可中断睡眠
- usleep(10000);
-
- printf("Task is awake after usleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function3(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他暂停后打印“Task is awake after pause”
- //暂停
- pause();
- printf("Task is awake after pause");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
- int main() {
- // 创建非实时进程
- create_nonrealtime_processes();
-
- // 创建实时进程
- set_realtime_scheduler();
- create_realtime_processes();
-
- return 0;
- }
-
- //work2_2.cpp
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
-
- #include<pthread.h>
-
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <sched.h>
-
- void create_realtime_processes();
- void create_threads();
- void create_process();
- void set_realtime_scheduler();
- void* thread_function1(void* arg);
- void* thread_function2(void* arg);
- void* thread_function3(void* arg);
-
- /**********************创建real-time进程*******************/
- void create_processes() {
- pid_t pid;
- int i;
-
- for (i = 0; i < 2; i++) //循环两次,表示每个进程创建两个子进程
- {
- pid = fork();
- if (pid == 0) {
- // 子进程
- printf("pid1:%d \n",getpid()); //打印自己的pid
- create_threads(); //创建线程的函数
- printf("create real-time process successfully\n");
- break;
- } else if (pid < 0) {
- // 出错处理
- perror("fork() error");
- break;
- }
- }
- }
- // 设置进程调度策略为实时策略
- void set_realtime_scheduler() {
- struct sched_param param;
- param.sched_priority = 99; // 设置实时优先级,取值范围为1-99
-
- if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
- perror("sched_setscheduler() error");
- }
- }
- // 创建实时进程
- void create_realtime_processes() {
- // 创建实时进程
- set_realtime_scheduler();
- create_processes();
-
- exit(0);
- }
-
-
-
- /********************thread relevant*********************/
- // 创建线程
- void create_threads() {
- pthread_t threads[2];
- int i;
- for (i = 0; i <= 2; i++) {
- if(i==0)
- pthread_create(&threads[i], NULL, thread_function1, NULL);
- else if(i==1)
- pthread_create(&threads[i], NULL, thread_function2, NULL);
- else
- pthread_create(&threads[i], NULL, thread_function3, NULL);
- //这句语句中的thread_function是函数,相当于创建这个线程时所要实现的功能。这个函数会在下面定义
- }
- }
-
- // 线程函数
- void* thread_function1(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after sleep”
- //任务可中断睡眠
- sleep(1);
- printf("Task is awake after a second sleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function2(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他休眠一秒后打印“Task is awake after usleep”
- //任务不可中断睡眠
- usleep(10);
-
- printf("Task is awake after usleep");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
-
- // 线程函数
- void* thread_function3(void* arg) {
- // 线程中只执行一次的任务,这里我让他打印自己的tid
- printf("pthread_tid = %lu\n",(unsigned long)pthread_self());
-
- while (1) {
- // 线程中可以循环执行的任务,这里我让他暂停后打印“Task is awake after pause”
- //暂停
- pause();
- printf("Task is awake after pause");
- // TODO: 在这里调用系统服务进行测试
- }
- return NULL;
- }
- int main() {
- while(1){
- create_realtime_processes();
- }
-
- return 0;
- }
完成这个任务前,先来看一下一些常用的函数:
建立管道的标准函数
- //建立管道,该函数在数组上填上两个新的文件描述符后返回0,失败返回-1。
- int pipe(int file_descriptor[2]);
- eg.int fd[2];int result = pipe(fd);
- //通过使用底层的read和write调用来访问数据。向file_descriptor[1]写数据,从file_descriptor[0]中读数据。写入与读取的顺序原则是先进先出。
建立命名管道的标准函数
- int mkfifo(const char *filename,mode_t mode);
- //建立一个名字为filename的命名管道,参数mode为该文件的权限(mode%~umask),若成功则返回0,否则返回-1,错误原因存于errno中。
- 例如:mkfifo( "/tmp/cmd_pipe", S_IFIFO | 0666 );
- 具体操作方法只要创建了一个命名管道然后就可以使用open、read、write等
- 系统调用来操作。创建可以手工创建或者程序中创建。
- int mknod(const char *path, mode_t mode, dev_t dev);
- //第一个参数表示你要创建的文件的名称,第二个参数表示文件类型,第三个参数表示该文件对应的设备文件的设备号。只有当文件类型为 S_IFCHR 或 S_IFBLK 的时候该文件才有设备号,创建普通文件时传入0即可
进程对信号响应的多个接口函数
- void (*signal(int sig,void (*func)(int)))(int);
- //用于截取系统信号,第一个参数为信号,第二个参数为对此信号挂接用户自己的处理函数指针。返回值为以前信号处理程序的指针。
- eg.int ret = signal(SIGSTOP, sig_handle);
- int kill(pid_tpid,int sig);
- //kill函数向进程号为pid的进程发送信号,信号值为sig。当pid为0时,向当前系统的所有进程发送信号sig。
- int raise(int sig);//向当前进程中自举一个信号sig, 即向当前进程发送信号。
- unsigned int alarm(unsigned int seconds); //alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。如果参数seconds为0,则之前设置的闹钟会被取消,并将剩下的时间返回。使用alarm函数的时候要注意alarm函数的覆盖性,即在一个进程中采用一次alarm函数则该进程之前的alarm函数将失效。
- int pause(void);
- //使调用进程(或线程)睡眠状态,直到接收到信号,要么终止,或导致它调用一个信号捕获函数
实现消息队列的多个接口函数
- 在命名管道中,发送数据用write,接收数据用read,则在消息队列中,
- 发送数据用msgsnd,接收数据用msgrcv。
- ➢ msgget() 创建或打开消息队列函数,这里创建的消息队列的数量会受到系统消息队列数量的限制;
- ➢ msgsnd()添加消息函数,它把消息添加到已打开的消息队列末尾;
- ➢ msgrcv()读取消息函数,它把消息从消息队列中取走,与FIFO不同的是,这里可以取走指定的某一条消息;
- ➢ msgctl()控制消息队列函数,它可以完成多项功能
实现信号量的多个接口函数
- sem_open(); //有名信号量
- sem_init(); //无名信号量(基于内存的信号量)
-
- sem_wait();
- sem_trywait();
- sem_post();
- sem_getvalue();
-
- sem_close();
- sem_unlink();
-
- sem_destroy();
- ➢ int semget(key_t key, int num_sems, int sem_flags);
- //semget函数用于创建一个新的信号量集合,或者访问一个现有的集合(不同进程只要key值相同即可访问同一信号量集合)。第一个参数key是ftok生成的键值,第二个参数num_sems可以指定在新的集合应该创建的信号量的数目,第三个参数sem_flags是打开信号量的方式。
- //ftok是什么?
- ➢ int semop(int sem_id, struct sembuf *sem_ops, size_tnum_sem_ops);
- //semop函数用于改变信号量的值。第二个参数是要在信号集合上执行操作的一个数组,第三个参数是该数组操作的个数 。
- ➢ int semctl(int sem_id, int sem_num, int command,...);
- //semctl函数用于信号量集合执行控制操作,初始化信号量的值,删除一个信号量等。 类似于调用msgctl(), msgctl()是用于消息队列上的操作。第一个参数是指定的信号量集合(semget的返回值),第二个参数是要执行操作的信号量在集合中的索引值(例如集合中第一个信号量下标为0),第三个command参数代表要在集合上执行的命令。
实现内存映射的多个接口函数
- void *mmap(void*start,size_t length,int prot,int flags,int fd,off_t offset);
- //mmap函数将一个文件或者其它对象映射进内存。 第一个参数为映射区的开始地址,设置为0表示由系统决定映射区的起始地址;第二个参数为映射的长度;第三个参数为期望的内存保护标志;第四个参数是指定映射对象的类型;第五个参数为文件描述符(指明要映射的文件);第六个参数是被映射对象内容的起点。成功返回被映射区的指针,失败返回MAP_FAILED[其值为(void *)-1]。
- int munmap(void* start, size_t length);
- //munmap函数用来取消参数start所指的映射内存起始地址,参数length则是欲取消的内存大小。如果解除映射成功则返回0,否则返回-1,错误原因存于errno中错误代码EINVAL。
- int msync(void *addr,size_t len,int flags);
- //msync函数实现磁盘文件内容和共享内存取内容一致,即同步。第一个参数为文件映射到进程空间的地址,第二个参数为映射空间的大小,第三个参数为刷新的参数设置
套接字连接过程所使用的函数
- /**************************以下是一个客户端向服务端发送数据的一般流程********************/
- /**************server服务端的一般流程***************/
- //创建socket套接字
- int socket(int domain, int type, int protocal);
- //绑定端口号
- int bind(int socket, const struct sockaddr *address, size_taddress_len);
- //监听该端口号
- int listen(int socket, int backlog);
- //接收来自客户端的连接请求
- int accept(int socket, struct sockaddr *address, size_t *address_len);
- //从socket中读取数据
- int recv();
- //关闭套接字
- close();
-
- /**************client客户端的一般流程***************/
- //创建socket套接字
- int socket(int domain, int type, int protocal);
- //连接指定计算机端口
- int connect(int socket,const struct sockaddr *addrsss, size_t address_len);
- //向socket中写入数据
- int send();
- //创建socket套接字
- int socket(int domain, int type, int protocal);
导入库函数
- #include<stdio.h>
- #include<stdlib.h>
- #include<assert.h>
- #include<string.h>
- #include<unistd.h>
- #include<pthread.h>
声明所有线程函数
- //declare call thread function
- void *fun1(void *);
- void *fun2(void *);
- void *fun3(void *);
- void *fun4(void *);
- void *fun5(void *);
- void *fun6(void *);
3个进程,每个进程中包含2个线程
- pid_t pid1,pid2,pid3;
- int ret;
- pid1=fork();
- printf("pid1:%d ",getpid());
- printf("the return value:%d\n",pid1);
- pthread_t tid1,tid2;
- ret = pthread_create(&tid1,NULL,fun1,NULL);
- ret = pthread_create(&tid2,NULL,fun2,NULL);
-
- pid2=fork();
- printf("pid2:%d ",getpid());
- printf("the return value:%d\n",pid2);
- pthread_t tid3,tid4;
- ret = pthread_create(&tid3,NULL,fun3,NULL);
- ret = pthread_create(&tid4,NULL,fun4,NULL);
-
- pid3=fork();
- printf("pid3:%d ",getpid());
- printf("the return value:%d\n",pid3);
- pthread_t tid5,tid6;
- ret = pthread_create(&tid5,NULL,fun5,NULL);
- ret = pthread_create(&tid6,NULL,fun6,NULL);
每个线程函数的内部如下:
- void *fun1(void *arg)
- {
- printf("pthread_tid1 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
-
- void *fun2(void *arg)
- {
- printf("pthread_tid2 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
-
- void *fun3(void *arg)
- {
- printf("pthread_tid3 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
-
- void *fun4(void *arg)
- {
- printf("pthread_tid4 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
-
- void *fun5(void *arg)
- {
- printf("pthread_tid5 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
-
- void *fun6(void *arg)
- {
- printf("pthread_tid6 = %lu\n",(unsigned long)pthread_self());
- pthread_exit(NULL);
- }
/proc是什么样的一个文件系统?什么是文件系统?
一个明显的优势在于platform机制将设备本身的资源注册及进核,由内核统一管理。在驱动程序中使用这些资源是通过platform device提供的标准接口进行申请并使用。
首先我们先了解一下基本例程
字符设备驱动模块加载和卸载函数模板
- strruct xxx_dev_t{
- struct cdev cdev;
- ```
- }xxx dev
- /*设备驱动模块加载函数*/
- static int_init xxx_ init(void)
- {
- ```
- cdev_init(&XXX_ dev.cdev,&xxx_fops) ;
- xxx_dev.cdev.owner=THIS_MODULE;
- /*获取字符设备号*/
- if(xxx_ major){
- register_chrdev_gion(xxx_dev_no,1,DEV_NAME);
- }
- else{
- alloc_chrdev_region(&xxx_dev_no,0,1,DEV_NAME);
- }
- ret=cdev_add(&xxx_dev.cede,xxx_dev_no,1); //注册设备
- }
-
- /*设备驱动模块卸载函数*/
- static void exit xxx_exit(void)
- {
- unregister_chrdev_region(xxx_ dev_no,1);
- /*释放占用的设备号*/
- cdev_del(&xxx_dev.cdev); /* 注销设备* 7
- ```
- return 0;
- }
字符设备驱动程序read(),write(),ioctl()函数模板
- /*读设备函数*/
- static ssize_t xxx_ read (struct file *fi1p, char __user *buf,size_ t count, loff_ t *f_pos)
- {
- copy_to_user (buf,...,...);
- ...
- return 0;
-
- }
-
- /*写设备函数*/
- static ssize_t led_write(struct file *filp,const char__user *buf,size_t count,1off t*f_pos)
- {
- ...
- copy_from_user(..., buf, ... );
- return 0;
- }
-
- /*I/O操作函数*/
- int xxx_ioctl(struct inode *inode, struct file *filp,unsigned int cmd,unsigned long arg)
- {
- ...
- switch(cmd){
- case xxx_CDM1:
- ...
- break;
- case xxx_CMD2:
- ...
- break;
- default:
- /*不能支持的命令*/
- return -ENOTTY;
- }
- return 0;
- }
字符设备驱动程序file_operations结构体赋值操作模板
略
接下来再来看看教材中“GPIO设备驱动程序实例——LED驱动程序”
无操作系统时的LED驱动
- #define reg_gpio_ctrl (*(volatile int *)(ToVirtual(GPIO_REG_CTRL)))
- #define reg_gpio_data (*(volatile int *)(ToVirtual(GPIO_REG_DATA)))
-
- /* 初始化 LED */
- void LightInit(void)
- {
- reg_gpio_ctrl |= (1 << n); /* 设置 GPIO 为输出 */
- }
-
- void Light0n(void) /*点亮LED*/
- {
- reg_gpio_data |= (1 << n); /* 在 GPIO 上输出高电平 */
- }
-
- /* 熄灭 LED */
- void Lightoff(void)
- {
- reg_gpio_data &= ~(1 << n); /* 在 GPIO 上输出低电平 */
- }
led_switch()函数
- void led_switch(u8 sta)
- {
- u32 val =0;
- if(sta == LEDON) {
- val = readl(GPIO1_DR); /*读取该寄存器值*/
- val &=~(1<<3); /*对LED对应引脚值设置为0*/
- writel(val, GPIO1_DR); /*打开LED*/
- }
- else if(sta == LEDOFF) {
- val = readl(GPIO1_DR);
- val|=(1<<3); /*对LED对应引脚值设置为1*/
- writel(val, GPIO1_DR); /*关闭LED*/
- }
- }
led_open()函数
- static int led_open(struct inode *inode, struct file *filp)
- {
- filp->private_data = &dtsled; /* 设置私有数据 */
- return 0;
- }
led_read()函数
- static ssize_t led_read(struct file *filp, char __user *buf,size_t cnt,loff_t *offt)
- {
- return 0;
- }
led_write()函数
- static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt,loff_t *offt)
- {
- int retvalue;
- unsigned char databuf[1];
- unsigned char ledstat;
- retvalue = copy_from_user(databuf, buf, cnt);
- if(retvalue < 0) {
- printk("kernel write failed!\r\n");
- return -EFAULT;
- }
- ledstat = databuf[0]; /* 获取状态值 */
- if(ledstat == LEDON) {
- led_switch(LEDON); /* 打开LED灯 */
- } else if(ledstat == LEDOFF) {
- led_switch(LEDOFF); /* 关闭LED灯 */
- }
- return 0;
- }
led_ioctl()函数
- int led_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
- {
- struct light_dev *dev = filp->private_data;
- switch (cmd) {
- case LEDON:
- led_switch(LEDON); /* 打开LED灯 */
- break;
- case LEDOFF:
- led_switch(LEDOFF); /* 关闭LED灯 */
- break;
- default:
- /* 不能支持的命令 */
- return -ENOTTY;
- }
- return 0;
- }
led_release()函数
- static ssize_t led_release(struct inode *inode,struct file *filp)
- {
- return 0;
- }
led_init()函数
- static int __init led_init(void)
- {
- u32 val = 0;
- int ret;
- u32 regdata[14];
- const char *str;
- struct property *proper;
- /************ 1. 获取设备树中的属性数据 ************/
- /* (1)获取设备节点:alphaled */
- dtsled.nd = of_find_node_by_path("/alphaled");
- if(dtsled.nd == NULL) {
- printk("alphaled node can not found!\r\n");
- return -EINVAL;
- }
- else {
- printk("alphaled node has been found!\r\n");
- }
-
- /* (2)获取compatible属性内容 */
- proper = of_find_property(dtsled.nd,"compatible", NULL);
- if(proper == NULL) {
- printk("compatible property find
- failed\r\n");
- }
- else {
- printk("compatible = %s\r\n",(char*)proper->value);
- }
-
- /* (3)获取status属性内容 */
- ret = of_property_read_string(dtsled.nd,"status", &str);
- if(ret < 0){
- printk("status read failed!\r\n");
- }
- else {
- printk("status = %s\r\n",str);
- }
-
- /* (4)获取reg属性内容 */
- ret = of_property_read_u32_array(dtsled.nd, "reg", regdata, 10);
- if(ret < 0) {
- printk("reg property read failed!\r\n");
- }
- else {
- u8 i = 0;
- printk("reg data:\r\n");
- for(i = 0; i < 10; i++)
- printk("%#X ", regdata[i]);
- printk("\r\n");
- }
-
- /*********** 2. 初始化LED ************/
- #if 0 //这句话的含义有点没搞懂,是不是C++区别于C的某个语法呢?
- /* (1)寄存器地址映射 */
- IMX6U_CCM_CCGR1 = ioremap(regdata[0], regdata[1]);
- SW_MUX_GPIO1_IO03 = ioremap(regdata[2], regdata[3]);
- SW_PAD_GPIO1_IO03 = ioremap(regdata[4], regdata[5]);
- GPIO1_DR = ioremap(regdata[6], regdata[7]);
- GPIO1_GDIR = ioremap(regdata[8], regdata[9]);
- #else
- IMX6U_CCM_CCGR1 = of_iomap(dtsled.nd, 0);
- SW_MUX_GPIO1_IO03 = of_iomap(dtsled.nd, 1);
- SW_PAD_GPIO1_IO03 = of_iomap(dtsled.nd, 2);
- GPIO1_DR = of_iomap(dtsled.nd, 3);
- GPIO1_GDIR = of_iomap(dtsled.nd, 4);
- #endif
-
- * (2)使能GPIO1时钟 */
- val = readl(IMX6U_CCM_CCGR1);
- val &= ~(3 << 26); /* 清楚以前的设置 */
- val |= (3 << 26); /* 设置新值 */
- writel(val, IMX6U_CCM_CCGR1);
- /* (3)设置GPIO1_IO03的复用功能GPIO功
- 能,最后设置I/O属性。*/
- writel(5, SW_MUX_GPIO1_IO03);
- /* 寄存器SW_PAD_GPIO1_IO03设置IO属性
- */
- writel(0x10B0, SW_PAD_GPIO1_IO03);
- /* (4)设置GPIO1_IO03为输出功能 */
- val = readl(GPIO1_GDIR);
- val &= ~(1 << 3); /* 清除以前的设置 */
- val |= (1 << 3); /* 设置为输出 */
- writel(val, GPIO1_GDIR);
- /* (5)默认关闭LED */
- val = readl(GPIO1_DR);
- val |= (1 << 3);
- writel(val, GPIO1_DR);
-
- /**************3.注册字符设备驱动 ***************/
- /*(1)创建设备号 */
- if (dtsled.major) { /* 静态设置设备号 */
- dtsled.devid = MKDEV(dtsled.major, 0);
- register_chrdev_region(dtsled.devid,
- DTSLED_CNT, DTSLED_NAME);
- }
- else { /* 没有定义设备号,动态申请设置设备号 */
- alloc_chrdev_region(&dtsled.devid, 0,
- DTSLED_CNT, DTSLED_NAME); /* 申请设备号 */
- dtsled.major = MAJOR(dtsled.devid); /* 获取分配号的主设备号 */
- dtsled.minor = MINOR(dtsled.devid); /* 获取分配号的次设备号 */
- }
- printk("dtsled major=%d,minor=%d\r\n",dtsled.major,dtsled.minor);
-
- /* (2)初始化cdev */
- dtsled.cdev.owner = THIS_MODULE;
- cdev_init(&dtsled.cdev, &dtsled_fops);
-
- /* (3)添加一个cdev */
- cdev_add(&dtsled.cdev, dtsled.devid,DTSLED_CNT);
-
- /*(4)创建类*/
- dtsled.class = class_create(THIS_MODULE, DTSLED_NAME);
- if (IS_ERR(dtsled.class)){
- return PTR_ERR(dtsled.class);
- }
-
- /*(5)创建类设备*/
- dtsled.device = device_create(dtsled.class, NULL, dtsled.devid, NULL,DTSLED NAME);
- if (IS_ERR(dtsled.device)){
- return PTR_ERR(dtsled.device);
- }
- return 0;
- }
驱动卸载函数led_exit()
- static void __exit led_exit(void)
- {
- /* 取消映射 */
- iounmap(IMX6U_CCM_CCGR1);
- iounmap(SW_MUX_GPIO1_IO03);
- iounmap(SW_PAD_GPIO1_IO03);
- iounmap(GPIO1_DR);
- iounmap(GPIO1_GDIR);
-
- /* 注销字符设备驱动 */
- cdev_del(&dtsled.cdev);/* 删除cdev */
- unregister_chrdev_region(dtsled.devid, DTSLED_CNT);/*注销设备号*/
- device_destroy(dtsled.class, dtsled.devid);
- class_destroy(dtsled.class);
- }
以上led_init()和led_exit函数使用模块化调用的方法
- module_init(led_init);
- module_exit(led_exit);
流程图
导入必要的库
- #include <linux/types.h>
- #include <linux/kernel.h>
- #include <linux/delay.h>
- #include <linux/ide.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <linux/gpio.h>
- #include <asm/mach/map.h>
- #include <asm/uaccess.h>
- #include <asm/io.h>
分配主设备号和设备名字,并定义开灯和关灯两种操作
- #define LED_MAJOR 200 /* 主设备号 */
- #define LED_NAME "led" /* 设备名字 */
-
- #define LEDOFF 0 /* 关灯 */
- #define LEDON 1 /* 开灯 */
根据项目要求构造fileoperation结构体
- /*文件操作集合的绑定,也就是设备操作函数*/
- static struct file_operations led_fops = {
- .owner = THIS_MODULE,
- .write = led_write,
- .open = led_open,
- .read = led_read,
- //PS:"ioctl" can both read and write,but I don't want to use it.
- };
定义寄存器名字
- /* 寄存器物理地址 */
- #define CCM_CCGR1_BASE (0X020C406C)
- #define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
- #define SW_PAD_GPIO1_IO03_BASE (0X020E02F4)
- #define GPIO1_DR_BASE (0X0209C000)
- #define GPIO1_GDIR_BASE (0X0209C004)
-
- /* 映射后的寄存器虚拟地址指针 */
- static void __iomem *IMX6U_CCM_CCGR1;
- static void __iomem *SW_MUX_GPIO1_IO03;
- static void __iomem *SW_PAD_GPIO1_IO03;
- static void __iomem *GPIO1_DR;
- static void __iomem *GPIO1_GDIR;
在入口函数中用iommap将名字映射为地址
- /* 1、寄存器地址映射 */
- IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
- SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
- SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
- GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
- GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);
在入口函数中利用定义的寄存器名做“使能”“配置input/output”和“控制高低电平”的任务。且由于我们使用“mknod /dev/led c 200 0”手动创建设备节点文件,因此省略了使用class_create和device_create函数智能创建设备号的过程
- int retvalue = 0;
- u32 val = 0;
-
- /* 2、使能 GPIO1 时钟 */
- val = readl(IMX6U_CCM_CCGR1);
- val &= ~(3 << 26); /* 清除以前的设置 */
- val |= (3 << 26); /* 设置新值 */
- writel(val, IMX6U_CCM_CCGR1);
-
- /* 3、设置 GPIO1_IO03 的复用功能,将其复用为 GPIO1_IO03,最后设置 IO 属性。 */
- writel(5, SW_MUX_GPIO1_IO03);
-
- /*寄存器 SW_PAD_GPIO1_IO03 设置 IO 属性
- bit 16:0 HYS 关闭
- bit [15:14]: 00 默认下拉
- bit [13]: 0 kepper 功能
- bit [12]: 1 pull/keeper 使能
- bit [11]: 0 关闭开路输出
- bit [7:6]: 10 速度 100Mhz
- bit [5:3]: 110 R0/6 驱动能力
- bit [0]: 0 低转换率 */
- writel(0x10B0, SW_PAD_GPIO1_IO03);
-
- /* 4、设置 GPIO1_IO03 为输出功能 */
- val = readl(GPIO1_GDIR);
- val &= ~(1 << 3); /* 清除以前的设置 */
- val |= (1 << 3); /* 设置为输出 */
- writel(val, GPIO1_GDIR);
-
- /* 5、默认关闭 LED */
- val = readl(GPIO1_DR);
- val |= (1 << 3);
- writel(val, GPIO1_DR);
-
- /* 6、注册字符设备驱动 */
- retvalue = register_chrdev(LED_MAJOR, LED_NAME, &led_fops);
- if(retvalue < 0){
- printk("register chrdev failed!\r\n");
- return -EIO;
- }
在出口函数中取消映射
- /* 驱动出口函数 */
- static void __exit led_exit(void)
- {
- /* 取消映射 */
- iounmap(IMX6U_CCM_CCGR1);
- iounmap(SW_MUX_GPIO1_IO03);
- iounmap(SW_PAD_GPIO1_IO03);
- iounmap(GPIO1_DR);
- iounmap(GPIO1_GDIR);
- }
编写led_write相关的函数
编写led_read相关的函数
整合上述内容,得到Linux字符设备的驱动架构图
流程图
根据项目要求构造fileoperation结构体
定义入口、出口函数,并设计程序创建class、设备并销毁它们
定义寄存器名字
在入口函数中用iommap将名字映射为地址
利用定义的寄存器名做“使能”“配置input/output”和“控制高低电平”的任务
编写led_write相关的函数
编写led_read相关的函数
整合上述内容,得到Linux字符设备的驱动架构图
CRC是个什么东西?makecrc()函数是怎么做初始化的?
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。