使用Linux实现二值信号灯控制方案 (二值信号灯 linux)

介绍

在现代城市的道路交通系统中,信号灯是不可缺少的一部分。信号灯的作用是控制交通流量和行车安全,使道路的通行更加有序和高效。随着计算机技术的不断发展,利用计算机控制信号灯已经成为现代交通系统的一个趋势。

Linux是一种开源的操作系统,具备高度模块化、灵活性强、易于拓展的优点。今天我们将介绍如何使用Linux实现二值信号灯的控制方案。

方案设计

信号灯的控制需要实现通断控制、时序控制、状态监测等功能。我们需要选择适合此项目的Linux系统,并且选择合适的嵌入式设备,同时我们还需要编写驱动程序,与Linux系统进行通信,完成信号灯的控制。具体方案设计如下:

1. 选择适合此项目的Linux系统

我们可以选择使用嵌入式Linux系统,如Buildroot、OpenEmbedded和Yocto等,也可以选择使用桌面Linux系统,在其上安装交叉编译器,将应用程序移植到嵌入式Linux系统中。考虑到物联网的发展趋势,我们在本项目中选择使用Yocto系统作为开发环境。

2. 选择合适的嵌入式设备

信号灯控制需要稳定可靠的嵌入式设备,我们要根据实际需求选择相应的硬件平台。在本项目中,我们选择采用树莓派(Raspberry Pi)嵌入式平台,它是一种低成本、高性能的开发板,可运行多种Linux操作系统。

3. 编写驱动程序

驱动程序是连接硬件和软件的桥梁,它能够将嵌入式设备的底层操作接露给Linux操作系统,使得应用程序可以方便地调用设备的功能。在本项目中,我们需要编写GPIO驱动程序,GPIO是通用输入输出接口,它可以将树莓派的引脚配置为输入或输出。通过编写GPIO驱动程序,我们可以控制树莓派上的GPIO引脚,从而控制信号灯的工作状态。

如何实现Linux的二值信号灯控制?

步骤1:安装Linux操作系统

我们可以通过大量的资源来获取Linux操作系统,在之前的介绍中,我们使用的是嵌入式Yocto系统。在嵌入式Linux系统的集成中,通常要包含设备驱动程序,所以我们不需要额外安装GPIO驱动程序。

步骤2:连接信号灯模块与树莓派

在树莓派上连接信号灯模块,其中包括了一个双色灯和两个单色灯,当值为0和1时,不同颜色的灯会发生变化。因此,我们需要连接3个GPIO引脚,一个用于控制双色灯,两个用于控制单色灯。

步骤3:GPIO驱动程序的编写

在Linux中,GPIO驱动程序通常是由内核提供的模块形式,我们只需要在此基础上稍微修改即可。首先需要设置GPIO引脚的模式,我们把它们配置为输出模式,然后在应用程序中使用API接口来进行控制。

在GPIO的控制过程中,需要注意一些细节问题,如需要使用Linux的gpiochip结构体来协助控制。在应用程序中,可以使用ioctl、文件系统等方式来进行GPIO操作,最终实现对信号灯的控制。

步骤4:应用程序的编写

应用程序为控制信号灯的关键,通过应用程序我们可以控制GPIO口和信号灯之间的相互关系。在应用程序的编写中,使用道路规则进行控制,符合交通场景下的需求,通过调用GPIO驱动程序来操作信号灯。

在本文中,我们介绍了如何使用Linux实现二值信号灯的控制方案。这涉及到了Linux系统的选择、硬件平台的选择、驱动程序的编写、应用程序的编写等方面的问题。通过本文的介绍,我们可以了解到Linux在物联网控制方案中的优势和应用,同时也学习到了如何将Linux的特性应用到物联网控制方案中。

相关问题拓展阅读:

Linux下线程同步的几种方法

Linux系统中,实现线程同步的方式大致分为六种,包括:互斥锁、自旋锁、信号量、条件变量、读写锁、屏障。其最常用的线程同步方式胡胡就是互斥锁、自旋锁、信号量。

1、互斥锁

互斥锁本质就是一个特殊的全局变量,拥有lock和unlock两种状态,unlock的互斥锁可以由某个线程获得,当互斥锁由某个线程持有后,这个互斥锁会锁上变成lock状态,此后只有该线程有权力打开该锁,其他想要获得该互斥锁的线程都会阻塞,直到互斥锁被解锁。

互斥锁的类型:

①普通锁:互斥锁默认类型。当一个线程对一个普通锁加锁以后,其余请求该锁的线程将形成一个等待队列,并在锁解锁后按照优先级获得它,这种锁类型保证了资源分配的公平性。一个线程如果对一个已经加锁的普通锁再次加锁,将引发死锁;对一个已经被其他线程加锁的普通锁解锁,或者对一个已经解锁的普通锁再次解锁,将导致不可预期的后果。

②检错锁:一个线程如果对一个已经加锁的检错锁再次加锁,则加锁操作返回EDEADLK;对一个已经被其他线程加锁的检错锁解锁或者对一个已经解锁做做袜的检错锁再次解锁,则解锁操作返回EPERM。

③嵌套锁:该锁允许一个线程在释放锁之前多次对它加锁而不发生死纯激锁;其他线程要获得这个锁,则当前锁的拥有者必须执行多次解锁操作;对一个已经被其他线程加锁的嵌套锁解锁,或者对一个已经解锁的嵌套锁再次解锁,则解锁操作返回EPERM。

④默认锁:一个线程如果对一个已经解锁的默认锁再次加锁,或者对一个已经被其他线程加锁的默认锁解锁,或者对一个解锁的默认锁解锁,将导致不可预期的后果;这种锁实现的时候可能被映射成上述三种锁之一。

2、自旋锁

自旋锁顾名思义就是一个死循环,不停的轮询,当一个线程未获得自旋锁时,不会像互斥锁一样进入阻塞休眠状态,而是不停的轮询获取锁,如果自旋锁能够很快被释放,那么性能就会很高,如果自旋锁长时间不能够被释放,甚至里面还有大量的IO阻塞,就会导致其他获取锁的线程一直空轮询,导致CPU使用率达到100%,特别CPU时间。

3、信号量

信号量是一个计数器,用于控制访问有限共享资源的线程数。

Linux 线程同步的三种方法

线程的更大特点是资源的共享性,但资源共享中的同步问题是多线程编程的难点。linux下提供了多种方式来处理线程同步,最常用的是互斥锁、条件变量和信号量。

一、互斥吵大锁(mutex)

通过锁机制实现线程间的同步。

初始化锁。在Linux下,线程的互斥量数据类型是pthread_mutex_t。在使用前,要对它进行初始化。

静态分配:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

动态分配:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex_attr_t *mutexattr);

加锁。对共享资源的访问,要对互斥量进行加锁,如果互斥量已经上了锁,调用线程会阻塞,直到互斥量被解锁。

int pthread_mutex_lock(pthread_mutex *mutex);

int pthread_mutex_trylock(pthread_mutex_t *mutex);

解锁。在完成了对共享资源的访问后,要对互斥量进行解锁。

int pthread_mutex_unlock(pthread_mutex_t *mutex);

销毁锁。锁在是使用完成后,需要进行销毁以释放资源。

int pthread_mutex_destroy(pthread_mutex *mutex);

view plain copy

#include

#include

#include

#include

#include “iostream”

using namespace std;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int tmp;

void* thread(void *arg)

{

cout

#include

#include “stdlib.h”

#include “unistd.h”

pthread_mutex_t mutex;

pthread_cond_t cond;

void hander(void *arg)

{

free(arg);

(void)pthread_mutex_unlock(&mutex);

}

void *thread1(void *arg)

{

pthread_cleanup_push(hander, &mutex);

while(1)

{

printf(“thread1 is running\n”);

pthread_mutex_lock(&mutex);

pthread_cond_wait(&cond, &mutex);

printf(“thread1 applied the condition\n”);

pthread_mutex_unlock(&mutex);

sleep(4);

}

pthread_cleanup_pop(0);

}

void *thread2(void *arg)

{

while(1)

{

printf(“thread2 is running\n”);

pthread_mutex_lock(&mutex);

pthread_cond_wait(&cond, &mutex);

printf(“thread2 applied the condition\n”);

pthread_mutex_unlock(&mutex);

sleep(1);

}

}

int main()

{

pthread_t thid1,thid2;

printf(“condition variable study!\n”);

pthread_mutex_init(&mutex, NULL);

pthread_cond_init(&cond, NULL);

pthread_create(&thid1, NULL, thread1, NULL);

pthread_create(&thid2, NULL, thread2, NULL);

sleep(1);

do

{

pthread_cond_signal(&cond);

}while(1);

sleep(20);

pthread_exit(0);

return 0;

}

view plain copy

#include

#include

#include “stdio.h”

#include “stdlib.h”

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

struct node

{

int n_number;

struct node *n_next;

}*head = NULL;

static void cleanup_handler(void *arg)

{

printf(“Cleanup handler of second thread./n”);

free(arg);

(void)pthread_mutex_unlock(&mtx);

}

static void *thread_func(void *arg)

{

struct node *p = NULL;

pthread_cleanup_push(cleanup_handler, p);

while (1)

{

//这个mutex主要是用来保证pthread_cond_wait的并发性

pthread_mutex_lock(&mtx);

while (head == NULL)

{

//这个while要特别说明一下,单个pthread_cond_wait功能很完善,为何

//这里要有一个while (head == NULL)呢?因为pthread_cond_wait里的线

//程可能会被意外唤醒,如果这个时候head != NULL,则不是我们想要的情况。

//这个时候,应该让线程继续进入pthread_cond_wait

// pthread_cond_wait会先解除之前的pthread_mutex_lock锁定的mtx,

//然后阻塞在等待对列里休眠,直到再次被唤醒(大多数情况下是等待的条件成立

//而被唤醒,唤醒后,该进程会先锁定先pthread_mutex_lock(&mtx);,再读取资源

//用这个流程是比较清楚的

pthread_cond_wait(&cond, &mtx);

p = head;

head = head->n_next;

printf(“Got %d from front of queue/n”, p->n_number);

free(p);

}

pthread_mutex_unlock(&mtx); //临界区数据操作完毕,释放互斥锁

}

pthread_cleanup_pop(0);

return 0;

}

int main(void)

{

pthread_t tid;

int i;

struct node *p;

//子线程会一直等待资源,类似生产者和消费者,但是这里的消费者可以是多个消费者,而

//不仅仅支持普通的单个消费者,这个模型虽然简单,但是很强大

pthread_create(&tid, NULL, thread_func, NULL);

sleep(1);

for (i = 0; i n_number = i;

pthread_mutex_lock(&mtx); //需要操作head这个临界资源,先加锁,

p->n_next = head;

head = p;

pthread_cond_signal(&cond);

pthread_mutex_unlock(&mtx); //解锁

sleep(1);

}

printf(“thread 1 wanna end the line.So cancel thread 2./n”);

//关于pthread_cancel,有一点额外的说明,它是从外部终止子线程,子线程会在最近的取消点,退出

//线程,而在我们的代码里,最近的取消点肯定就是pthread_cond_wait()了。

pthread_cancel(tid);

pthread_join(tid, NULL);

printf(“All done — exiting/n”);

return 0;

}

三、信号量(sem)

如同进程一样,线程也可以通过信号量来实现通信,虽然是轻量级的。信号量函数的名字都以”sem_”打头。线程使用的基本信号量函数有四个。

信号量初始化。

int sem_init (sem_t *sem , int pshared, unsigned int value);

这是对由sem指定的信号量进行初始化,设置好它的共享选项(linux 只支持为0,即表示它是当前进程的局部信号量),然后给它一个初始值VALUE。

等待信号量。给信号量减1,然后等待直到信号量的值大于0。

int sem_wait(sem_t *sem);

释放信号量。信号量值加1。并通知其他等待线程。

int sem_post(sem_t *sem);

销毁信号量。我们用完信号量后都它进行清理。归还占有的一切资源。

int sem_destroy(sem_t *sem);

view plain copy

#include

#include

#include

#include

#include

#include

#define return_if_fail(p) if((p) == 0){printf (“:func error!/n”, __func__);return;}

typedef struct _PrivInfo

{

sem_t s1;

sem_t s2;

time_t end_time;

}PrivInfo;

static void info_init (PrivInfo* thiz);

static void info_destroy (PrivInfo* thiz);

static void* pthread_func_1 (PrivInfo* thiz);

static void* pthread_func_2 (PrivInfo* thiz);

int main (int argc, char** argv)

{

pthread_t pt_1 = 0;

pthread_t pt_2 = 0;

int ret = 0;

PrivInfo* thiz = NULL;

thiz = (PrivInfo* )malloc (sizeof (PrivInfo));

if (thiz == NULL)

{

printf (“: Failed to malloc priv./n”);

return -1;

}

info_init (thiz);

ret = pthread_create (&pt_1, NULL, (void*)pthread_func_1, thiz);

if (ret != 0)

{

perror (“pthread_1_create:”);

}

ret = pthread_create (&pt_2, NULL, (void*)pthread_func_2, thiz);

if (ret != 0)

{

perror (“pthread_2_create:”);

}

pthread_join (pt_1, NULL);

pthread_join (pt_2, NULL);

info_destroy (thiz);

return 0;

}

static void info_init (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

thiz->end_time = time(NULL) + 10;

sem_init (&thiz->s1, 0, 1);

sem_init (&thiz->s2, 0, 0);

return;

}

static void info_destroy (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

sem_destroy (&thiz->s1);

sem_destroy (&thiz->s2);

free (thiz);

thiz = NULL;

return;

}

static void* pthread_func_1 (PrivInfo* thiz)

{

return_if_fail(thiz != NULL);

while (time(NULL) end_time)

{

sem_wait (&thiz->s2);

printf (“pthread1: pthread1 get the lock./n”);

sem_post (&thiz->s1);

printf (“pthread1: pthread1 unlock/n”);

sleep (1);

}

return;

}

static void* pthread_func_2 (PrivInfo* thiz)

{

return_if_fail (thiz != NULL);

while (time (NULL) end_time)

{

sem_wait (&thiz->s1);

printf (“pthread2: pthread2 get the unlock./n”);

sem_post (&thiz->s2);

printf (“pthread2: pthread2 unlock./n”);

sleep (1);

}

return;

}

2、LINUX软中断通信

我验证下阿…一不小心就fork多了..

刚开始我把kill的参数弄反了,信号和pid位置弄错了,调了半个小时,很郁闷..

你只是忽略了一点…,我也给忽略了。。。后来才想起来

你按下ctrl+C的时候,另外两个fork出来的进程,他们也会接到SIGINT。伍搭橡。。就退出了。。所以你要先在子进程里面忽略这个SIGINT信号,用枝或signal(SIGINT,SIG_IGN)就OK了….

程序如下…

有解释,你可以自己看腔旁看…

#include”stdio.h”

#include”unistd.h”

#include”signal.h”

#include”sys/types.h”

#include”stdlib.h”

int k=0;

pid_t child1=0,child2=0;

void func_main(int sig);

void func_child1(int sig);

void func_child2(int sig);

int main()

{

while((child1=fork())==-1);

if(child1==0)

{

printf(“child1 OK\n”);

signal(SIGINT,SIG_IGN);

signal(SIGUSR1,func_child1);

sleep(60);

}

else if(child1 >0)

{

while((child2=fork())==-1);

if(child2==0)

{

printf(“child 2 OK\n”);

signal(SIGINT,SIG_IGN);//你按下ctrl+C,子进程也会接受到ctrl的信号…所以,子进程忽略

//所提子进程要忽略掉这个SIGINT信号

signal(SIGUSR2,func_child2);

sleep(60); //这里为了验证,如果进程没退出,40妙之后自动会退出的

//不然就得手动在终端里面kill掉这个进程了…

//有时候成了僵尸进程需要kill -9 才能杀死

}

else if(child2 >0)

{

signal(SIGINT,func_main);

printf(“children forked OK…\n”);

wait(0);

printf(“child return…\n”);

sleep(100);

return 0;

}

}

}

void func_main(int sig)

{

k++;

printf(“to send signal\n”);

//printf(“child1=%d,child2=%d\n”,child1,child2);

//if(k==1)

kill(child1,SIGUSR1);

//if(k==2)加上这句,再按一次ctrl C,子进程2才会退出

就是你想要的效果了

kill(child2,SIGUSR2);

signal(SIGINT,SIG_DFL); //这里恢复ctrl+C的效果

//子进程退出之后,我们再按一次ctrl+C,当前的父进程就会像平常一样,退出。

}

void func_child1(int sig)

{

printf(“child1 is killed by parent!\n”);

exit(0);

}

void func_child2(int sig)

{

printf(“child2 is killed by parent!\n”);

exit(0);

}

我也是初学者,这里抄一段《Linux设备驱动程序》书上的给你:

Linux的中断宏观分为两种:软中断和硬中断。声明一橘启数下,这里的软和硬的意思是指和软件相关以及和硬件相关,而不是软件实现的中断或硬件实现的中断。软中断就是“信号机制”。软中断不是软件中断。Linux通过信号来产生对进程的各种中断操作,我们现在知道的信号共有31个,其具体内容这里略过。

一般来说,软中断是由内核机制的触发事件引起的(例如进程运行超时),但是不可忽视有大量的软中断也是由于和硬件有关的中断引起的,例如当打印机端口产生一个硬件中断时,会通知和硬件相关的硬中断,硬中断就会产生一个软中断并送到操作系统内核里,这样内核就会根据这个软中断唤醒睡眠在打印机任务队列中的处理进程。

硬中断就是通常意义上的“中断处理程序”,它是直接处理由硬件发过来的中断信号的。当硬中断收到它应当处理的中断信号以后,就回去自己驱动的设备上去看看设备的状态寄存器以了解发生了什么事情,并进行相应的操作。

对于软中断,我们不做讨论,那是进程调度里要考虑的事情。由于我们讨论的是设备驱动程序的中断问题,所以焦点集中在硬中断里。我们这里讨论的是硬中断,即和硬件相关的中断。

要中断,是因为外设需要通知操作系统她那里发生了一些事情,但是中断的功能仅仅是一个设备报警灯,当灯亮的时候中断处理程序只知道有事情发生了,但发生了什么事情还要亲自到设备那里去看才行。也就是说,当中断处理程序得知设备发生了一个中断的时候,它并不知道设备发生了什么事情,只有当它访问了设备上的一些状态寄存器以后,才能知道具体发生了什么,要怎么去处理。

设备通过中断线向中断控制器发送高电平告诉操作系统它产生了一个中断,而操作系统会从中断控制器的状态位知道是哪条中断线上产生了中断。PC机上使用的中断控制器是8259,这种控制器每一个可以管理8条中断线,当两个8259级联的时候共可以控制15条中断线。这里的中断线是实实在在的电路,他们通过硬件接口连接到CPU外的设备控制器上。

并不是每个设备都可以向中断线上发中断信号的,只有对某一条确定的中断线勇有了控制权,才可以向这条中断线上发送信号。由于计算机的外部设备越来越多,所以15条中断线已经不够用了,中断线是非常宝贵的资源。要使用中断线,就得进行中断线的申请,就是IRQ(Interrupt Requirement),我们也常把申请一条中断线成为申请一个IRQ或者是申请一个中圆首断号。

IRQ是非常宝贵的,所以我们建议只有当设备需要中断的时候才申请占用一个IRQ,或者是在申请IRQ时采用共享中断的方式,这样可以让更多的设备使用中断。无论对IRQ的使用方式是独占还是共享,申请IRQ的过程都是一样的,分为3步:

1.将所有的中断线探测一遍,看看哪些中断还没有被占用。从这些还没有被占用的中断中选一个作为该设备的IRQ。

2.通过中断申请函数申请选定的IRQ,这是要指定申请的方式是独占还是共享。

3.根据中断申请函数的返回值决定怎么做:如果成功了万事大吉,如果没成功则或者重新申请或者放弃申请并返回错误。

Linux中的中断处理程序很有特色,它的一个中断处理程序分为两个部分:上半部(top half)和下半部(bottom half)。之所以会有上半部和下半部之分,完全是考虑到中断处理的效率。

上半部的功能是“登记中断”。当一个中断发生时,他就把设备驱动程序中中断例程的下半部挂到该设备的下半部执行队列中去,然后就没事情了–等待新的中断的到来。这样旁薯一来,上半部执行的速度就会很快,他就可以接受更多她负责的设备产生的中断了。上半部之所以要快,是因为它是完全屏蔽中断的,如果她不执行完,其它的中断就不能被及时的处理,只能等到这个中断处理程序执行完毕以后。所以,要尽可能多得对设备产生的中断进行服务和处理,中断处理程序就一定要快。

但是,有些中断事件的处理是比较复杂的,所以中断处理程序必须多花一点时间才能够把事情做完。可怎么样化解在短时间内完成复杂处理的矛盾呢,这时候 Linux引入了下半部的概念。下半部和上半部更大的不同是下半部是可中断的,而上半部是不可中断的。下半部几乎做了中断处理程序所有的事情,因为上半部只是将下半部排到了他们所负责的设备的中断处理队列中去,然后就什么都不管了。下半部一般所负责的工作是察看设备以获得产生中断的事件信息,并根据这些信息(一般通过读设备上的寄存器得来)进行相应的处理。如果有些时间下半部不知道怎么去做,他就使用著名的鸵鸟算法来解决问题–说白了就是忽略这个事件。

由于下半部是可中断的,所以在它运行期间,如果其它的设备产生了中断,这个下半部可以暂时的中断掉,等到那个设备的上半部运行完了,再回头来运行它。但是有一点一定要注意,那就是如果一个设备中断处理程序正在运行,无论她是运行上半部还是运行下半部,只要中断处理程序还没有处理完毕,在这期间设备产生的新的中断都将被忽略掉。因为中断处理程序是不可重入的,同一个中断处理程序是不能并行的。

在Linux Kernel 2.0以前,中断分为快中断和慢中断(伪中断我们这里不谈),其中快中断的下半部也是不可中断的,这样可以保证它执行的快一点。但是由于现在硬件水平不断上升,快中断和慢中断的运行速度已经没有什么差别了,所以为了提高中断例程事务处理的效率,从Linux kernel 2.0以后,中断处理程序全部都是慢中断的形式了–他们的下半部是可以被中断的。

但是,在下半部中,你也可以进行中断屏蔽–如果某一段代码不能被中断的话。你可以使用cti、sti或者是save_flag、restore_flag来实现你的想法。

在处理中断的时候,中断控制器会屏蔽掉原先发送中断的那个设备,直到她发送的上一个中断被处理完了为止。因此如果发送中断的那个设备载中断处理期间又发送了一个中断,那么这个中断就被永远的丢失了。

之所以发生这种事情,是因为中断控制器并不能缓冲中断信息,所以当前一个中断没有处理完以前又有新的中断到达,他肯定会丢掉新的中断的。但是这种缺陷可以通过设置主处理器(CPU)上的“置中断标志位”(sti)来解决,因为主处理器具有缓冲中断的功能。如果使用了“置中断标志位”,那么在处理完中断以后使用sti函数就可以使先前被屏蔽的中断得到服务。

有时候需要屏蔽中断,可是为什么要将这个中断屏蔽掉呢?这并不是因为技术上实现不了同一中断例程的并行,而是出于管理上的考虑。之所以在中断处理的过程中要屏蔽同一IRQ来的新中断,是因为中断处理程序是不可重入的,所以不能并行执行同一个中断处理程序。在这里我们举一个例子,从这里子例中可以看出如果一个中断处理程序是可以并行的话,那么很有可能会发生驱动程序锁死的情况。当驱动程序锁死的时候,你的操作系统并不一定会崩溃,但是锁死的驱动程序所支持的那个设备是不能再使用了–设备驱动程序死了,设备也就死了。

A是一段代码,B是操作设备寄存器R1的代码,C是操作设备寄存器R2的代码。其中激发PS1的事件会使A1产生一个中断,然后B1去读R1中已有的数据,然后代码C1向R2中写数据。而激发PS2的事件会使A2产生一个中断,然后B2删除R1中的数据,然后C2读去R2中的数据。

如果PS1先产生,且当他执行到A1和B1之间的时候,如果PS2产生了,这是A2会产生一个中断,将PS2中断掉(挂到任务队列的尾部),然后删除了 R1的内容。当PS2运行到C2时,由于C1还没有向R2中写数据,所以C2将会在这里被挂起,PS2就睡眠在代码C2上,直到有数据可读的时候被信号唤醒。这是由于PS1中的B2原先要读的R1中的数据被PS2中的B2删除了,所以PS1页会睡眠在B1上,直到有数据可读的时候被信号唤醒。这样一来,唤醒PS1和PS2的事件就永远不会发生了,因此PS1和PS2之间就锁死了。

由于设备驱动程序要和设备的寄存器打交道,所以很难写出可以重入的代码来,因为设备寄存器就是全局变量。因此,最简洁的办法就是禁止同一设备的中断处理程序并行,即设备的中断处理程序是不可重入的。

有一点一定要清楚:在2.0版本以后的Linux kernel中,所有的上半部都是不可中断的(上半部的操作是原子性的);不同设备的下半部可以互相中断,但一个特定的下半部不能被它自己所中断(即同一个下半部不能并)。

由于中断处理程序要求不可重入,所以程序员也不必为编写可重入的代码而头痛了。编写可重入的设备驱动程序是可以的,编写可重入的中断处理程序是非常难得,几乎不可能。

我们都知道,一旦竞争条件出现了,就有可能会发生死锁的情况,严重时可能会将整个系统锁死。所以一定要避免竞争条件的出现。只要注意一点:绝大多数由于中断产生的竞争条件,都是在带有中断的

内核进程被睡眠造成的。所以在实现中断的时候,一定要相信谨慎的让进程睡眠,必要的时候可以使用cli、sti或者save_flag、restore_flag。

返回列表

上一篇:ppd linux

二值信号灯 linux的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于二值信号灯 linux,使用Linux实现二值信号灯控制方案,Linux下线程同步的几种方法的信息别忘了在本站进行查找喔。


数据运维技术 » 使用Linux实现二值信号灯控制方案 (二值信号灯 linux)