linux 中断 串口_linux串口中断 - CSDN
  • 网络上有一些关于linux 中断方式的文章,但是都只有接收程序,没有发送。我编写中断程序时候遇到一些问题。小结一下大概有如下几点: 收发都会产生SIGIO 信号 产生SIGIO 会中断sleep,造成sleep 定时不准。 比如 ...

    网络上有一些关于linux 中断方式的文章,但是都只有接收程序,没有发送。我编写中断程序时候遇到一些问题。小结一下大概有如下几点:

    1. 收发都会产生SIGIO 信号
    2. 产生SIGIO 会中断sleep,造成sleep 定时不准。

    比如 原先将send 放在了主程序的while语句中

    while(true)
    {
    send(buf,size)
    sleep(1);
    }

    发现这个时候定时不准。SIGIO 信号一来,sleep就迫不及待地醒了。于是,我放在了一个线程中。

    搞了差不多两天,总算调通了。不过,悲剧发生了,和同事交流时突然想起,为什么不开一个线程,阻塞方式等待接收数据呢?(主线程发送,子线程接收)那样就不要SIGIO了。感觉白忙活了。人生就是折腾。

    还是将代码post 出来。有时,网上的一段代码简直是程序员的亲人。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <fcntl.h>
    #include <sys/signal.h>
    #include <errno.h>
    #include <termios.h>
    #include <pthread.h>
    void signal_handler_IO (int status);   /* definition of signal handler */
    
    int n;
    int fd;
    int connected;
    struct termios termAttr;
    struct sigaction saio;
    char sbuff[64];
    char rbuff[64];
      int send_cnt;
     void* sender(void *)
     { int i;
         for (i=0;i<6;i++) sbuff[i]=0x30+i;sbuff[6]=0x0a;sbuff[7]=0x0d;
         send_cnt=8; 
         sleep(1);
         while(true)
         {   
             while(send_cnt>0)
             {
            int len= write(fd, &sbuff[8-send_cnt] ,1);
           if (len>0)
             send_cnt--;
             }
    	    sleep(1);
            send_cnt=8;
         }
     } 
    int main(int argc, char *argv[])
    {    int i;
    pthread_t ntid;
         fd = open("/dev/ttyACM4", O_RDWR| O_NOCTTY);// | O_NOCTTY | O_NDELAY
         if (fd == -1)
         {
            perror("open_port: Unable to open /dev/ttyO1\n");
            exit(1);
         }
    
         saio.sa_handler = signal_handler_IO;
         saio.sa_flags = 0;
         saio.sa_restorer = NULL; 
         sigaction(SIGIO,&saio,NULL);
    
         fcntl(fd, F_SETFL, FNDELAY);
         fcntl(fd, F_SETOWN, getpid());
         fcntl(fd, F_SETFL, O_NDELAY| O_ASYNC ); /**<<<<<<------This line made it work.**/
    
         tcgetattr(fd,&termAttr);
         //baudRate = B115200;          /* Not needed */
         cfsetispeed(&termAttr,B115200);
         cfsetospeed(&termAttr,B115200);
         termAttr.c_cflag &= ~PARENB;
         termAttr.c_cflag &= ~CSTOPB;
         termAttr.c_cflag &= ~CSIZE;
         termAttr.c_cflag |= CS8;
         termAttr.c_cflag |= (CLOCAL | CREAD);
         termAttr.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
         termAttr.c_iflag &= ~(IXON | IXOFF | IXANY);
         termAttr.c_oflag &= ~OPOST;
         tcsetattr(fd,TCSANOW,&termAttr);
         printf("serial configured....\n");
         
         pthread_create(&ntid,NULL,sender,NULL);
       
         while(true){
    	for (i=0;i<10;i++) sleep(10);
         }
    
         close(fd);
         exit(0);             
    }
    
    void signal_handler_IO (int status)
    {
        int nread = read(fd, rbuff, 64);
         if (nread>0)
         printf("%.*s",nread,rbuff);
      
    }

     

    展开全文
  • 网上大多数说的都是轮询的方式来读写串口数据...请问各位大侠怎样在linux下获取串口中断的标志,并跳转到相关的中断服务程序执行相关指令。(使用C语言) 网上使用的 signal handler是否就是用中断方式来读取串口数据?
  • 前言   cpu在执行程序时,如果有外部中断触发时,如定时器中断、串行总线中断等,cpu停止当前任务从而转去...  其实,在单片机逻辑编程中已经使用过Linux中断“上下部分”的思维,或者说,Linux中断“上下半...

    前言
      cpu在执行程序时,如果有外部中断触发时,如定时器中断、串行总线中断等,cpu停止当前任务从而转去响应中断处理。对于中断函数的处理,原则是尽快处理完事务并退出中断,这一点也比较好理解,尽快处理中断并返回,保证正常任务的执行,并q且能否响应其他事务的中断,保证实时性和并发性。

      其实,在单片机逻辑编程中已经使用过Linux中断“上下部分”的思维,或者说,Linux中断“上下半部”设计者灵感即源自最初的裸机编程。例如,在单片机裸机编程中,需要通过串口(UART)进行数据通信,对于接收数据情况下,在采用中断方式接收时,我们一般会先将数据存放在一个缓存(buf)中,中断函数只负责将数据存入缓存,而在主函数中获取缓存数据并处理。在这个过程中,中断处理部分即将数据存入缓存的动作为“中断上半部”,当然还包括从寄存器获取数据,改变寄存器状态等。“中断下半部”则是获取缓存数据并处理。

    RTOS中断处理
      从裸机编程上升到系统层面,单片机运行实时系统(RTOS)。RTOS一般以线程为调度任务,没有进程概念,但都会提供一套类似标准操作系统的 线程同步机制。此时,在设计中断程序时,可以更好实现“上半部”和“下半部”,特别是“下半部”。以RT-Thread为例,实现一个串口中断接收函数。上半部分负责将数据放入缓存,并通过信号量通知下半部分处理。下半部实现,可以创建一个处理线程,当获得上半部信号量时则调度线程进行处理数据,否则则挂起该线程,节约cpu资源。

    Linux中断
      不同于裸机编程,操作系统是多个进程和多个线程执行,宏观上达到并行运行的状态,外设中断则会打断内核中任务调度和运行,及屏蔽其外设的中断响应,如果中断函数耗时过长则使得系统实时性和并发性降低。中断原则是尽可能处理少的事务,而一些设备中往往需要处理大量的耗时事务。为了提高系统的实时性和并发性,Linux内核将中断处理程序分为上半部(top half)和下半部(bottom half)。上半部分任务比较少,处理一些寄存器操作、时间敏感任务,以及“登记中断”通知内核及时处理下半部的任务。下半部分,则负责处理中断任务中的大部分工作,如一个总线通信系统中数据处理部分。

    Linux中断上下部分区别:
    1)上半部由外设中断触发,下半部由上半部触发。
    2)上半部不会被其他中断打断,下半部是可以被打断的。
    3)上半部分处理任务要快,主要任务、耗时任务放在下半部。

      一般来说,在中断上半部执行完毕,下半部即在内核的调度下被执行,当然如果有其他更高优先级需处理的任务,会先处理该任务再调度处理下半部,或者在系统空闲时间进行处理。

    Linux中断设计
      对于Linux系统设备而言,一个完整的中断程序由上半部和下半部分共同构成,在编写设备驱动程序前,就需考虑好上半部和下半部的分配。很多时候上半部与下半部并没有严格的区分界限,主要由程序员根据实际设计,如某些外设中断可以没有下半部。关于上下半部的划分原则,就是主要事务、耗时事务划分在下半部处理。

    关于中断上下部分的设计,可以参考以下原则:
    1)与硬件相关的操作,如操作寄存器,必须放在上半部。
    2)对时间敏感、要求实时性的任务放在上半部。
    3)该任务不能被其他中断或者进程打断的放在上半部。
    4)实时性要求不高的任务、耗时任务放在下半部。

    Linux中断下半部实现
      Linux下半部实现,通过内核版本更新中实现下衍化,最原始的实现手段是BH(bottom half)后在2.5版本移除,在2.3版本引入软中断(softirq)和tasklet机制,在2.5版本引入工作队列(work queue在)。因此,目前使用方式是三种,软中断、tasklet机制、工作队列。

    展开全文
  • Linux下的串口编程,在嵌入式开发中占据着重要的地位,因为很多的嵌入式设备都是通过串口交换数据的。在没有操作系统的我们可以使用UART的中断来出来数据的接受和发送,而在Linux操作系统下,我们也可以使用软中断的...

    Linux下的串口编程,在嵌入式开发中占据着重要的地位,因为很多的嵌入式设备都是通过串口交换数据的。在没有操作系统的我们可以使用UART的中断来出来数据的接受和发送,而在Linux操作系统下,我们也可以使用软中断的方式来处理数据的接受和发送,这里主要使用的是信号SIGIO,也就是异步I/O。这里也可以使用select实现异步形式的通知。  这里可以参考《UNIX 环境高级编程》中的第14章 高级I/O和第18章的I/O终端,这两章描述了串口的编程和异步I/O方面的内容。还有一本书《linux serial programming how-to》,《Serial Programming Guide for POSIX Operating Systems》。这都是串口编程的必读和经典书籍。

    串口参数的设置一般包括波特率、起始位数量、数据位、停止位和流控协议。在接收端和发送端要配置成一样的参数设置。在Linux中,所有的设备文件一般都位于“/dev”下,其中串口一、串口二对应的设备名依次为“/dev/ttyS0”、"/dev/ttyS1"。这可以通过查看"/dev"下的文件加以确认。我的串口通信是开发板ARM9--mini2440发送数据,PC机通过串口接受数据。我的串口的参数设置为 115200,8,‘N’,1。也就是波特率是115200,8位数据位,无奇偶校验位,1位停止位。因为是用的开发板发送数据,所以要用到在minicom中运行发送的程序,不过在发送程序运行后,要立即关闭minicom,否则,接受程序不能接受到数据。这个是我使用中断时出现的问题,当我使用select是没有此问题,现在还不知道具体的原因是什么。

        串口编程中有一个最重要的结构体:

       

    struct termios
    {
    tcflag_t c_iflag; /* 输入选项标志 */
    tcflag_t c_oflag; /* 输出选项标志 */
    tcflag_t c_cflag; /* 控制选项标志 */
    tcflag_t c_lflag; /* 本地选项标志 */

    unsigned char c_line /*线控制*/
    cc_t c_cc[NCCS]; /* 控制特性 */
    }; 

     

        这个结构中最重要的是c_cflag。通过对它的赋值,用户可以设置波特率、字符大小、数据位、停止位、奇偶校验位和硬件流控等。其中的参数在网上和很多的书籍上都介绍的很详细了,这里主要介绍一下我在其中遇到的问题和解决的办法,以供学习。其中的c_line,在POSIX中的Linux中没有用到。

        在c_lflag中有这么一个参数ICANON,如若设置,则按规范模式工作,这使下列字符起作用:EOF、EOL EOL2、 ERASE、 KILL、 REPRINT 、STATUS、WERASE。输入字符被装配成行。如果不以规范模式工作,则读请求直接从输入队列取字符。在至少接收到MIN个字节或已超过TIME值之前,read将不返回。

        在规范模式很容易:系统每次返回一行。但在非规范模式下,系统怎样才能知道在什么时候将数据返回给我们呢?如果它一次返回一个字节,那么系统开销就很大。解决方法是:当已读了指定量的数据后,或者已经过了给定的时间后,即通知系统返回。这种技术使用了termios结构中c_cc数组的两个变量:MIN和TIME。C_cc数据中的这两个元素的下标名为VMIN和VTIME。MIN说明一个read返回前的最小字节数。TIME说明等待数据到达的分秒数。

    需要包括的头文件是:
    #include <stdio.h>
    #include <string.h>
    #include <sys/types.h>
    #include <errno.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <termios.h>
    #include <stdlib.h>
    #include "Set_Uart.c"

    其中的“Set_Uart.c”是我设置串口的模式和打开串口的文件。模式的设置就是按上面说的进行设置。

    下面是发送数据,写串口的程序,

     

    int main(void)
    {
      int fd;
      int nwrite,i;
      char buff[] = "Hello world!\n";
      fd = 0;
      /*打开串口*/
      if((fd = open_port(fd,0)) < 0) //在我的Set_Uart.c文件中定义。

      {
        perror("open_port error!\n");
        return (-1);
      }
      /*设置串口*/
      if((i = set_opt(fd,115200,8,'N',1)) < 0) //在Set_Uart.c文件中定义

      {
        perror("set_opt error!\n");
        return (-1);
      }
      printf("fd =%d\n",fd);
      /*向串口写入字符串*/
    sleep(10);  
      nwrite = write(fd,buff,strlen(buff));
    sleep(10);
      printf("nwrite = %d\n",nwrite);
      close(fd);
      return (1);
    }

       把该文件进行交叉编译后下载到开发板,进行运行./Write_Uart。

          下面是中断读取数据的main函数。Read_Uart_IRQ.c 当使用中断方式的读取数据时,要先运行开发板上的Write_Uart.c文件,然后,立即关闭,再在PC上运行读取数据的 Read_Uart_IRQ.c文件。所以,在Write_Uart.c中,在使用write()函数向UART写数据之间加入一小段的延时。这样便于关 闭minicom,如果在两台PC上进行测试的话,应该不存在此问题。

     

     

    int main(void)
    {
      int fd,res,i;
      struct sigaction saio; /*definition of signal axtion */
      char buf[255];
      fd_set rd;
      fd = 0;
      /*打开串口*/
      if((fd = open_port(fd,1))<0)
      {
        perror("open_port error!\n");
        return (-1);
      }
      /* install the signal handle before making the device asynchronous*/
      saio.sa_handler = signal_handler_IO;
      sigemptyset(&saio.sa_mask);
      //saio.sa_mask = 0; 必须用sigemptyset函数初始话act结构的sa_mask成员 

      saio.sa_flags = 0;
      saio.sa_restorer = NULL;
      sigaction(SIGIO,&saio,NULL);

      /* allow the process to recevie SIGIO*/
      fcntl(fd,F_SETOWN,getpid());
      /* Make the file descriptor asynchronous*/
      fcntl(fd,F_SETFL,FASYNC);
      
      /*设置串口*/
      if((i= set_opt(fd,115200,8,'N',1))<0)
      {
        perror("set_opt error!\n");
        return (-1);
      }
      /* loop while waiting for input,normally we would do something useful here*/
      while(STOP == FALSE)
      {
        usleep(100000);
        /* after receving SIGIO ,wait_flag = FALSE,input is availabe and can be read*/
        if(wait_flag == FALSE)
        {
              memset(buf,0,255);
          res = read(fd,buf,255);
           printf("nread=%d,%s\n",res,buf);
          if(res == 1)
            STOP = TRUE; /*stop loop if only a CR was input */
           wait_flag = TRUE; /*wait for new input*/
        }
      }
      close(fd);
      return 0; 
    }
    /******************************************
     信号处理函数,设备wait_flag=FASLE
     ******************************************************/
    void signal_handler_IO(int status)
    {
       printf("received SIGIO signale.\n");
      wait_flag = FALSE; 
    }

    下面是select方式的读取数据的main函数。Read_Uart.c

    int main(void)
    {
      int fd;
      int nread,nwrite,i;
      char buff[8];
      fd_set rd;
      fd = 0;
      /*打开串口*/
      if((fd = open_port(fd,1)) < 0)
      {
        perror("open_port error!\n");
        return ;
      }
      /*设置串口*/
      if((i= set_opt(fd,115200,8,'N',1)) < 0)
      {
        perror("set_opt error!\n");
        return (-1);
      }
      /*利用select函数来实现多个串口的读写*/
    while(1)
    {
      FD_ZERO(&rd);
      FD_SET(fd,&rd);
      while(FD_ISSET(fd,&rd))
      {
        if(select(fd+1,&rd,NULL,NULL,NULL) < 0)
          perror("select error!\n");
        else
        {
          while((nread = read(fd,buff,8))>0)
          {
            printf("nread = %d,%s\n",nread,buff);
         printf("nread = %d,%s\n",nread,buff);
          }
        }
      }

    close(fd);
        return ;
    }

    下面是运行的结果,PC机收到的开发板发送过来的数据。

    ./Read_Uart_IRQ 
    fcntl = 0
    isatty success !
    fd-open=3
    set 
    received SIGIO signale.
    nread=13,Hello 

    其中串口中的一些重要的设备如下;

     

    /*设置等待时间它最小接收字符*/
      newtio.c_cc[VTIME] = 1;
      newtio.c_cc[VMIN] = 0;
      newtio.c_lflag &= ~( ECHO | ECHOE | ISIG);
      newtio.c_lflag |=ICANON; //关闭ICANON标志就使终端处于非规范模式 现在处于打开 处于规范模式下
      newtio.c_oflag &= ~OPOST; //执行输出处理 现在就关闭状态
      newtio.c_iflag |= (IGNPAR | ICRNL); //忽略奇偶校验错误 将CR 映射成NL 

     

    2018/9/10添加:串口select接收线程实例

    void *Uart1_read_thread(void *param)
    {
    	int maxfd = 0;
    	fd_set read_fds;
    	struct timeval TimeoutVal;
    	int ret;
    	unsigned char RcvNum = 0;
    	unsigned char RcvC;
    	unsigned char RcvBuff[10];
    	while(1)
    	{
    		maxfd = 0;
    		FD_ZERO(&read_fds);
    		maxfd = Uart1_fd;
    		FD_SET(Uart1_fd, &read_fds);//Uart1_fd是open出来的串口描述符
    
    		TimeoutVal.tv_sec  = 2;
    		TimeoutVal.tv_usec = 0;
    		ret = select(maxfd+1, &read_fds, NULL, NULL, &TimeoutVal);
    
            if (ret < 0){
    //            printf("select failed! \n");
                //return -1;
            }else if (ret == 0){
    //            printf(" Chingan_Ptz_read time out! \n");
                continue;
            }else{
            	if (FD_ISSET(Uart1_fd, &read_fds)){
            		if(read(Uart1_fd, &RcvC, 1) == 1){
            			RcvBuff[RcvNum++] = RcvC;
            			if(RcvNum == 10){
            				RcvNum = 0;
            			}
            			RcvBuff[2]=RcvBuff[2];
            		}
            	}
            }
    	}

     

    展开全文
  • Linux中断机制分析

    2018-09-12 13:41:05
    上次更新博客到了linux内核中断子系统。这次总结一下软中断,也就是softirq。之后还会总结一些tasklet、工作队列机制。 1. 为什么要软中断  编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要...

    软中断分析

        最近工作繁忙,没有时间总结内核相关的一些东西。上次更新博客到了linux内核中断子系统。这次总结一下软中断,也就是softirq。之后还会总结一些tasklet、工作队列机制。

    1. 为什么要软中断

        编写驱动的时候,一个中断产生之后,内核在中断处理函数中可能需要完成很多工作。但是中断处理函数的处理是关闭了中断的。也就是说在响应中断时,系统不能再次响应外部的其它中断。这样的后果会造成有可能丢失外部中断。于是,linux内核设计出了一种架构,中断函数需要处理的任务分为两部分,一部分在中断处理函数中执行,这时系统关闭中断。另外一部分在软件中断中执行,这个时候开启中断,系统可以响应外部中断。

        关于软件中断的理论各种书籍都有介绍,不多叙述。而要真正体会软件中断的作用就必须从代码的角度来分析。我们做工作时候讲求的是professional,当一个人在某个领域一无所知的时候,我们称他为小白,偶,非苹果电脑。小白的脑子里充满了各种问题。慢慢的当这些疑惑解释完之后,小白就脱白了。此时,我们对这个领域的基本框架有了解,但这和professional还有一定的差距。再加以时日,逐渐融会贯通该领域才能达到专业的境界。

    2. 什么时候触发处理软件中断

        说了这么多废话,赶快步入正题。初识软中断,脑子里肯定有不少的疑问,首先就是软件中断在什么地方被触发处理?这个问题的答案就是:一个硬件中断处理完成之后。下面的函数在处理完硬件中断之后推出中断处理函数,在irq_exit中会触发软件中断的处理。

    
    asmlinkage void __exception asm_do_IRQ(unsigned int irq, structpt_regs *regs) 
    { 
        struct pt_regs *old_regs = set_irq_regs(regs); 
      
        irq_enter(); 
      
        /* 
         * Some hardware gives randomly wrong interrupts.  Rather 
         * than crashing, do something sensible. 
         */  
        if (irq >= NR_IRQS) 
            handle_bad_irq(irq, &bad_irq_desc); 
        else  
            generic_handle_irq(irq); 
      
        /* AT91 specific workaround */  
        irq_finish(irq); 
      
        irq_exit(); 
        set_irq_regs(old_regs); 
    }

        这里要注意,invoke_softirq必须满足两个条件才能被调用到,一个就是不是在硬件中断处理过程中或者在软件中断处理中,第二个就是必须有软件中断处于pending状态。第二个好理解,有软件中断产生才去处理,没有就不处理。第一个就不好理解了。

    /* 
    * Exit an interrupt context. Process softirqs if needed and possible:
    */  
    void irq_exit(void) 
    { 
        account_system_vtime(current); 
        trace_hardirq_exit(); 
        sub_preempt_count(IRQ_EXIT_OFFSET); 
        if (!in_interrupt() && local_softirq_pending()) 
            invoke_softirq(); 
      
    #ifdef CONFIG_NO_HZ 
        /* Make sure that timer wheel updates are propagated */  
        rcu_irq_exit(); 
        if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched()) 
            tick_nohz_stop_sched_tick(0); 
    #endif  
        preempt_enable_no_resched(); 
    }

    在linux系统的进程数据结构里,有这么一个数据结构

    #define preempt_count() (current_thread_info()->preempt_count),

    利用preempt_count可以表示是否处于中断处理或者软件中断处理过程中。

    
    #define PREEMPT_MASK    (__IRQ_MASK(PREEMPT_BITS) << PREEMPT_SHIFT) 
    #define SOFTIRQ_MASK    (__IRQ_MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT) 
    #define HARDIRQ_MASK    (__IRQ_MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT) 
      
    #define PREEMPT_OFFSET    (1UL << PREEMPT_SHIFT) 
    #define SOFTIRQ_OFFSET    (1UL << SOFTIRQ_SHIFT) 
    #define HARDIRQ_OFFSET    (1UL << HARDIRQ_SHIFT)

    sub_preempt_count(IRQ_EXIT_OFFSET);

    #define in_interrupt() (irq_count())

    #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))

                         wps_clip_image-31524 

        preempt_count的8~23位记录中断处理和软件中断处理过程的计数。如果有计数,表示系统在硬件中断或者软件中断处理过程中。系统这么设计是为了避免软件中断在中断嵌套中被调用,并且达到在单个CPU上软件中断不能被重入的目的。对于ARM架构的CPU不存在中断嵌套中调用软件中断的问题,因为ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的。只有在进入了软中断处理过程中之后才会开启硬件中断,如果在软件中断处理过程中有硬件中断嵌套,也不会再次调用软中断,because硬件中断是软件中断处理过程中再次进入的,此时preempt_count已经记录了软件中断!对于其它架构的CPU,有可能在触发调用软件中断前,也就是还在处理硬件中断的时候,就已经开启了硬件中断,可能会发生中断嵌套,在中断嵌套中是不允许调用软件中断处理的。Why?我的理解是,在发生中断嵌套的时候,表明这个时候是系统突发繁忙的时候,内核第一要务就是赶紧把中断中的事情处理完成,退出中断嵌套。避免多次嵌套,哪里有时间处理软件中断,所以把软件中断推迟到了所有中断处理完成的时候才能触发软件中断。

    3. 软件中断的处理过程

        之前我已经说到,软中断的一个很大的目的就是避免中断处理中,处理的操作过多而丢失中断。同时中断还需要考虑到一件事情就是中断处理过程过长就会影响系统响应时间。如果一个中断处理一秒钟,那你一定能感受到串口卡住的现象。从另外一方面说呢,我们又必须考虑中断处理的操作一定的优先度,毕竟是硬件触发的事务,关系到网络、块设备的效率问题。Linux内核就中断方面就必须考虑平衡这三个方面的问题。而下面我要分析的__do_softirq函数就恰似在这三者之间打太极,游刃有余,面面俱到!

     /* 
    * We restart softirq processing MAX_SOFTIRQ_RESTART times, 
    * and we fall back to softirqd after that. 
    * 
    * This number has been established via experimentation. 
    * The two things to balance is latency against fairness - 
    * we want to handle softirqs as soon as possible, but they 
    * should not be able to lock up the box. 
    */  
    #define MAX_SOFTIRQ_RESTART 10  
      
    asmlinkage void __do_softirq(void) 
    { 
        struct softirq_action *h; 
        __u32 pending; 
        int max_restart = MAX_SOFTIRQ_RESTART; 
        int cpu; 
      
        pending = local_softirq_pending(); 
        account_system_vtime(current); 
      
        __local_bh_disable((unsigned long)__builtin_return_address(0)); 
        trace_softirq_enter(); 
      
        cpu = smp_processor_id(); 
    restart: 
        /* Reset the pending bitmask before enabling irqs */  
        set_softirq_pending(0); 
      
        local_irq_enable(); 
      
        h = softirq_vec; 
      
        do  
        { 
            if (pending & 1) 
            { 
                int prev_count = preempt_count(); 
      
                h->action(h); 
      
                if (unlikely(prev_count != preempt_count())) 
                { 
                    printk(KERN_ERR "huh, entered softirq %td %p"  
                           "with preempt_count %08x,"  
                           " exited with %08x?\n", h - softirq_vec, 
                           h->action, prev_count, preempt_count()); 
                    preempt_count() = prev_count; 
                } 
      
                rcu_bh_qsctr_inc(cpu); 
            } 
            h++; 
            pending >>= 1; 
        } 
        while (pending); 
      
        local_irq_disable(); 
      
        pending = local_softirq_pending(); 
        if (pending && --max_restart) 
            goto restart; 
      
        if (pending) 
            wakeup_softirqd(); 
      
        trace_softirq_exit(); 
      
        account_system_vtime(current); 
        _local_bh_enable(); 
    }
    
    

     __do_softirq函数处理软件中断过程如下图流程分析

    4. 首先调用local_softirq_pending函数取得目前有哪些位存在软件中断

    5. 调用__local_bh_disable关闭软中断,其实就是设置正在处理软件中断标记,在同一个CPU上使得不能重入__do_softirq函数

    6. 重新设置软中断标记为0,set_softirq_pending重新设置软中断标记为0,这样在之后重新开启中断之后硬件中断中又可以设置软件中断位。

    7. 开启硬件中断

    8. 之后在一个循环中,遍历pending标志的每一位,如果这一位设置就会调用软件中断的处理函数。在这个过程中硬件中断是开启的,随时可以打断软件中断。这样保证硬件中断不会丢失。

    9. 之后关闭硬件中断,查看是否又有软件中断处于pending状态,如果是,并且在本次调用__do_softirq函数过程中没有累计重复进入软件中断处理的次数超过10次,就可以重新调用软件中断处理。如果超过了10次,就调用wakeup_softirqd();唤醒内核的一个进程来处理软件中断。设立10次的限制,也是为了避免影响系统响应时间。

                                                   wps_clip_image-25966

    4. 处理软中断内核线程

        之前我说到不能让CPU长时间来处理中断事务,这样会影响系统的响应时间,严重影响用户和系统之间的交互式体验。所以在之前的__do_softirq中最多将循环执行10次,那么当执行了10次仍然有软中断在pending状态,这个时候应该怎么处理呢?系统将唤醒一个软件中断处理的内核进程,在内核进程中处理pending中的软件中断。这里要注意,之前我们分析的触发软件中断的位置其实是中断上下文中,而在软中断的内核线程中实际已经是进程的上下文。

    这里说的软中断上下文指的就是系统为每个CPU建立的ksoftirqd进程。

     

        看完这个函数,我不得不佩服这个函数设计的精巧!而我更多的从中体会到其中蕴藏的一种做人的道理。那就是做人要霸道一点,太谦和太恭维不行,但是又不能横行霸道,原则的问题要公平讲理,一定的时候顾及别人的利益,好处不能一个人独吞。这就跟下面ksoftirqd处理过程一样,该狠的时候禁止抢占,其它进程别想调度到哦,但是自己占用CPU时间过长的话,也自觉的问一问是不是该释放CPU给其它进程了。

        下面我们就来分析一下这个处理过程怎么就体现了上面的这种说法呢?软中断的内核进程中主要有两个大循环,外层的循环处理有软件中断就处理,没有软件中断就休眠。内层的循环处理软件中断,并每循环一次都试探一次是否过长时间占据了CPU,需要调度释放CPU给其它进程。具体的操作在注释中做了解释。

    
    static int ksoftirqd(void *__bind_cpu) 
    { 
        set_current_state(TASK_INTERRUPTIBLE); 
      
        while (!kthread_should_stop()) 
        { 
            /*不管三七二十一首先禁止抢占,我掌握CPU,并全凭我自己掌握调度*/  
            preempt_disable(); 
            if (!local_softirq_pending()) 
            { 
                preempt_enable_no_resched(); 
                /*如果没有软中断在pending,那就让出CPU来吧*/  
                schedule(); 
               /*我被唤醒了,首先掌握CPU,不让自己被抢占,自己决定自己的是否要调度*/  
                preempt_disable(); 
            } 
      
            __set_current_state(TASK_RUNNING); 
      
            while (local_softirq_pending()) 
            { 
                /* Preempt disable stops cpu going offline. 
                   If already offline, we'll be on wrong CPU: 
                   don't process */  
                if (cpu_is_offline((long)__bind_cpu)) 
                    goto wait_to_die; 
                /*处理软中断*/  
                do_softirq(); 
                /*虽然我自己掌握是否要调度,虽然我可以一直不调度,但是我是 
                个正直的人,运行一段时间后我会看看是否需要调度,还其它进程运行*/  
                preempt_enable_no_resched(); 
                cond_resched(); 
                preempt_disable(); 
                rcu_qsctr_inc((long)__bind_cpu); 
            } 
            preempt_enable(); 
            set_current_state(TASK_INTERRUPTIBLE); 
        } 
        __set_current_state(TASK_RUNNING); 
        return 0; 
      
    wait_to_die: 
        preempt_enable(); 
        /* Wait for kthread_stop */  
        set_current_state(TASK_INTERRUPTIBLE); 
        while (!kthread_should_stop()) 
        { 
            schedule(); 
            set_current_state(TASK_INTERRUPTIBLE); 
        } 
        __set_current_state(TASK_RUNNING); 
        return 0; 
    } 
    

     

    文章内容转载自:http://blog.chinaunix.net/uid-28236237-id-3450751.html

     

     

    展开全文
  • linux 中断中断处理

    2018-07-30 17:06:12
    从物理学角度看,中断是一种电信号,中断是硬件发出,送入中断控制器的输入引脚中,中断控制器是个简单的电子芯片,其作用是将多路中断管线,采用复用技术只通过一个和处理器连接的管线与处理器通信。当接受到一个...
  • Linux中断屏蔽

    2018-11-13 20:03:14
    在单CPU范围内避免竞态的一种简单而有效的方法是在进入临界区之前屏蔽系统的中断,但是在驱动编程中不值得推荐,驱动通常需要考虑跨平台特点而不假定自己在单核上运行。 CPU一般都具备屏蔽中断和打开中断的功能,这...
  • 中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件...Linux中通常分为外部中断(又叫硬件中断)和内部中断(又叫异常)。
  • Linux串口编程的文章网上是满天飞,但大都是出自一篇文章,而且写的都是些基本的操作,像控制 RTS/CTS 等串口引脚状态,接收发送二进制数据等,都没有很好的说明,我在使用中遇到了些问题,写出来,希望能对大家...
  • 上一节我们学习了在linux中,中断的初始化流程以及调用流程。 本节我们对中断的框架进行说明。   开始之前我们先把前面说过的一个数据结构拿出来分析一下。   1.中断描述符 /** * struct irq_desc - ...
  • rom bios 时钟中断是08号中断linux 中怎样变成了0x20号中断? 8259A 是可编程中断控制器芯片,可管理8个中断源。通过级联可最多管理64个中断源 pc/at 使用了2片8259A, 占用地址0x20-0x3f 0xa0-0xbf 范围, 共可...
  • 新手,想知道ARM linux中是如何确定中断号的, 我手里有一块iTop4412的开发板,在申请按键的中断时都是这样写的 ret = request_irq(IRQ_EINT(9), interrupt_9, IRQ_TYPE_EDGE_RISING, "myinterrupt_9", (void*)&t)...
  • Linux中的中断处理   发布于2013-4-2   与Linux设备驱动中中断处理相关的首先是申请与释放IRQ的API request_irq()和free_irq(), request_irq()的原型为: int request...
  • linux 内核中用于申请中断的函数是 request_irq(),函数原型在Kernel/irq/manage.c 中定义: int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, c
  • linux的共享中断机制

    2015-07-22 13:36:28
    从个人的理解,Linux2.6内核对中断处理程序的现在的处理可以分为两种模式,一种就是上面说的老的模式(非共享中断线),一种属于使用共享中断线的新模式,从其使用的注册中断处理程序的函数中来分析,函数原型如下: ...
  • linux中断的小结

    2011-11-23 14:25:08
    中断和"信号"有些类似,同时,软中断又是和硬中断相对应的,"硬中断是外部设备对CPU的中断","软中断通常是硬中断服务程序对内核的中断","信号则是由内核(或其他进程)对某个进程的中断"(《Linux内核源代码情景...
  • 以前进行单片机单板开发的时候,会有外部中断,定时器中断以及串口中断,就是有一个事件触发(外部中断则是外部有按键触发(边缘触发和水平触发),串口中断则是串口有数据输入,定时器中断则是内部设置一个定时器,...
  • options.c_cc[VTIME] = 0; /* 设置超时0 seconds*/   options.c_cc[VMIN] = 13;... 这两句话决定了对串口读取的函数read()的一些功能。我将着重介绍一下他们对read()函数的影响。   其中cc_t
1 2 3 4 5 ... 20
收藏数 26,346
精华内容 10,538
关键字:

linux 中断 串口