linux学习记录

一、异常
(1)异常类型
注意:linux中断是异常的一种,异常包含中断,有软件中断(swi svc指令触发)、快速中断、外部中断三种与中断有关的异常。
异常类型
(2)IRQ中断流程
1.cpu每个指令周期检查是否发送异常;
2.若发现异常,保存CPSR(程序状态寄存器),将当前处理模式设置为ARM、IRQ模式,将被打断应用程序地址的下一个指令保存到LR寄存器,例如当前程序地址为0x6000008,LR为0x6000004,即下一个指令的地址;
3.PC指针指向0x18,程序到0x18取出指令,这个指令跳转到中断异常处理函数;
4.中断异常处理函数中,分三步处理;
①保护现场:寄存器、CPU状态入栈
②根据中断号,跳转到对应的中断处理函数中执行
③执行完毕恢复现场:寄存器、CPU状态出栈
5.恢复现场后,PC指向原来中断的地方,重新回到断点执行程序;
(3)中断处理函数原则
中断处理函数是一个特俗的函数,可以从参数、返回值、函数内部实现回答
1.参数和返回值:绝对不能传入参数,不能返回值;
2.内部实现:
快进快出原则,中断处理函数的任务要尽可能快速完成,以免堵塞其他中断;
②根据①原则,中断处理函数不能休眠,如调用sleep;
③中断处理函数不能调用不可重入函数(记为fun),以免主函数mian正在执行;fun,中断又来执行fun,导致fun内部的全局变量和静态变量的结果出乎意料。
(4)不可重入函数的特点
1.使用了全局变量和静态变量
2.返回了全局变量和静态变量
3.调用了不可重入函数,这点难以防范,因为很多时候并不清除调用的第三方函数内部如何实现,是否可重入;
4.使用了标准IO函数,如调用了scanf,printf;
5.使用了malloc,free函数
二、c线程池
(1)线程池简介
线程的创建和销毁是资源密集型的操作,设计操作系统的系统调度和资源分配,频繁的创建和销毁线程会造成资源浪费,并使得系统响应速度降低。线程池通过预先创建线程,并在后续重复利用这些线程,有效避免了在任务执行过程中频繁创建和销毁线程,从而降低资源消耗,提高系统的响应速度;
线程池作用:
1.使得线程可重复利用,降低频繁创建和销毁线程造成的资源消耗,提高系统响应速度;
2.避免线程过多导致系统资源耗尽;
3.线程池可以动态调整线程数量,适应不同的工作负荷;
(2)c语言实现线程池
数据:定义一个TheradPool结构体,结构体内主要包括一个线程队列、一个任务队列、互斥锁和条件变量以及一些用于记录任务和线程数量的变量;
初始化:首先创建线程,将线程放入线程队列,分配内存空间,初始化条件变量和互斥锁;
管理者线程:线程池主要实现工作(消费者)线程和一个管理者线程,管理则线程用于管理线程的创建和销毁,管理者检测到运行过程中线程的数量不足时将创建新的线程,若是检测到有大量空闲的线程,则销毁一部分线程,将资源归还给操作系统。
工作线程:工作线程数量数量有多个,每个工作线程首先会尝试获取线程池锁,等待任务队列非空的条件变量,等到了条件变量则从任务队列中取出一个任务执行,并向生产者发出任务队列空闲的条件变量。对外提供addTask函数,用于将任务添加到现场池,若是线程池中有可用的线程资源,直接取出一个线程执行任务;若是没有可用的线程资源则阻塞,等待工作线程发空闲信号。

#ifndef _THREAD_POOL_H__
#define _THREAD_POOL_H__

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#define ADD_NUMBER 2

// 任务结构体
typedef struct Task {
  void (*function)(void* arg);
  void* arg;
} Task;

// 线程池结构体
typedef struct ThreadPool {
  // 任务队列
  Task* tq;
  int taskCapacity;  // 容量
  int taskNum;      // 当前任务个数
  int taskFront;     // 队头 -> 取数据
  int taskRear;      // 队尾 -> 放数据

  pthread_t mt;        // 管理者线程ID
  pthread_t* wt;       // 工作的线程ID
  int minNum;                 // 最小线程数量
  int maxNum;                 // 最大线程数量
  int busyNum;                // 忙的线程的个数
  int liveNum;                // 存活的线程的个数
  int exitNum;                // 要销毁的线程个数
  pthread_mutex_t mutexPool;  // 锁整个的线程池
  pthread_mutex_t mutexBusy;  // 锁busyNum变量
  pthread_cond_t notFull;     // 任务队列是不是满了
  pthread_cond_t notEmpty;    // 任务队列是不是空了

  int shutdown;  // 是不是要销毁线程池, 销毁为1, 不销毁为0
} ThreadPool;

typedef struct Arg {
  int num;
  int id;
} Arg;

int max(int a, int b);
int min(int a, int b);

// 创建线程池并初始化
ThreadPool* threadPoolCreate(int min, int max, int taskSize);

// 销毁线程池
int threadPoolDestroy(ThreadPool* pool);

// 给线程池添加任务
void addTask(ThreadPool* pool, void (*func)(void*), void* arg);

// 获取线程池中工作的线程的个数
int threadPoolBusyNum(ThreadPool* pool);

// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool* pool);

//
// 工作的线程(消费者线程)任务函数
void* worker(void* arg);
// 管理者线程任务函数
void* manager(void* arg);
// 单个线程退出
void threadExit(ThreadPool* pool);

#endif  // _THREADPOOL_H

#include <thread_pool.h>
// 创建线程池并初始化
ThreadPool* threadPoolCreate(int min, int max, int taskSize) {
  ThreadPool *pool = (ThreadPool *)malloc(sizeof(ThreadPool));
  pool->wt = (pthread_t *)malloc(max * sizeof(pthread_t));
  memset(pool->wt, 0, max * sizeof(pthread_t));

  pool->taskFront = 0;
  pool->taskRear = 0;
  pool->taskNum = 0;
  pool->taskCapacity = taskSize;

  pool->minNum = min;
  pool->maxNum = max;
  pool->liveNum = min;
  pool->exitNum = 0;

  pthread_mutex_init(&pool->mutexBusy, NULL);
  pthread_mutex_init(&pool->mutexPool, NULL);
  pthread_cond_init(&pool->notFull, NULL);
  pthread_cond_init(&pool->notEmpty, NULL);
  pool->tq = (Task *)malloc(sizeof(Task) * taskSize);
  pthread_create(&pool->mt, NULL, manager, pool);
  for(int i = 0; i < min; i++) {
    pthread_create(&pool->wt[i], NULL, worker, pool);
  }
  return pool;
}

// 销毁线程池
int threadPoolDestroy(ThreadPool* pool) {
  printf("destory:%d\r\n", pool->liveNum);
  pool->shutdown = 1;
  pthread_join(pool->mt, NULL);
  for(int i = 0; i < pool->liveNum; i++) {
    // pthread_join(pool->wt[i], NULL);
    pthread_cond_signal(&pool->notEmpty);
  }
  free(pool->wt);
  free(pool->tq);
  pthread_mutex_destroy(&pool->mutexPool);
  pthread_mutex_destroy(&pool->mutexBusy);
  pthread_cond_destroy(&pool->notEmpty);
  pthread_cond_destroy(&pool->notFull);
  free(pool);
  return 0;
}

// 获取线程池中工作的线程的个数
int threadPoolBusyNum(ThreadPool* pool) {
  int num = 0;
  pthread_mutex_lock(&pool->mutexBusy);
  num = pool->busyNum;
  pthread_mutex_unlock(&pool->mutexBusy);
  return num;
}

// 获取线程池中活着的线程的个数
int threadPoolAliveNum(ThreadPool* pool) {
  int num = 0;
  pthread_mutex_lock(&pool->mutexPool);
  num = pool->liveNum;
  pthread_mutex_unlock(&pool->mutexPool);
  return num;
}

// 给线程池添加任务
void addTask(ThreadPool* pool, void (*func)(void*), void* arg) {
  pthread_mutex_lock(&pool->mutexPool);
  if(pool->shutdown) {
    pthread_mutex_unlock(&pool->mutexPool);
    return ;
  }
  if(pool->taskNum == pool->taskCapacity) {
    // 队列已满,阻塞生产者线程
    pthread_cond_wait(&pool->notFull, &pool->mutexPool);
  }

  pool->tq[pool->taskRear].function = func;
  pool->tq[pool->taskRear].arg = arg;
  pool->taskRear = (pool->taskRear + 1) % pool->taskCapacity;
  pool->taskNum++;
  pthread_cond_signal(&pool->notEmpty);
  pthread_mutex_unlock(&pool->mutexPool);
}

// 工作的线程(消费者线程)任务函数
void* worker(void* arg) {
  ThreadPool *pool = (ThreadPool *)arg;
  while(1) {
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->taskNum == 0 && pool->shutdown == 0) {
      pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
      if(pool->exitNum > 0) {
        // printf("worker exit num: %d %lu\r\n", pool->exitNum, pthread_self());
        pool->exitNum--;
        if(pool->liveNum > pool->minNum) {
          pool->liveNum--;
          pthread_mutex_unlock(&pool->mutexPool);
          threadExit(pool);
        }
      }
    }
    // printf("running %lu\r\n", pthread_self());
    if(pool->shutdown) {
      // printf("shoutdown %lu\r\n", pthread_self());
      pthread_mutex_unlock(&pool->mutexPool);
      threadExit(pool);
    }
    Task t = pool->tq[pool->taskFront];
    pool->taskFront = (pool->taskFront + 1) % pool->taskCapacity;
    pool->taskNum--;
    pthread_cond_signal(&pool->notFull);
    pthread_mutex_unlock(&pool->mutexPool);

    pthread_mutex_lock(&pool->mutexBusy);
    pool->busyNum++;
    pthread_mutex_unlock(&pool->mutexBusy);
    printf("%lu start\r\n", pthread_self());
    t.function(t.arg);
    printf("%lu end\r\n", pthread_self());
    free(t.arg);
    t.arg = NULL;

    pthread_mutex_lock(&pool->mutexBusy);
    pool->busyNum--;
    pthread_mutex_unlock(&pool->mutexBusy);
  }
}

// 单个线程退出
void threadExit(ThreadPool* pool) {
  pthread_t tid = pthread_self();
  for(int i = pool->maxNum - 1; i >= 0; i--) {
    if(tid == pool->wt[i]) {
      pool->wt[i] = 0;
      printf("threadExit() called, %ld exiting...\n", tid);
      break;
    }
  }
  pthread_exit(NULL);
}


int max(int a, int b) {return a > b ? a : b;}

int min(int a, int b) {return a < b ? a : b;}


// 管理者线程任务函数
void* manager(void* arg) {
  ThreadPool * pool = (ThreadPool *)arg;
  while(pool->shutdown == 0) {
    sleep(3);
    pthread_mutex_lock(&pool->mutexBusy);
    int busyNum = pool->busyNum;
    pthread_mutex_unlock(&pool->mutexBusy);

    pthread_mutex_lock(&pool->mutexPool);
    int taskNum = pool->taskNum;
    int liveNum = pool->liveNum;
    int maxNum = pool->maxNum;
    printf("live num: %d -- create num : %d -- total num : %d\r\n", pool->liveNum, min(taskNum, maxNum) - liveNum, min(taskNum, maxNum));
    // 线程不足 创建线程 创建后使得所有任务都已一个线程,若是创建线程数量超出线程数量限制,则区线程最大数量-当前线程数
    for(int n = min(taskNum, maxNum) - liveNum; n > 0; n--) {
      pthread_create(&pool->wt[pool->liveNum++], NULL, worker, pool);
    }

    // 一半的线程空闲, 并且线程数量,则释放线程
    if(pool->busyNum * 2 < pool->liveNum) {
      pool->exitNum = pool->liveNum - max(pool->minNum, pool->busyNum);
      printf("live num : %d ---- exit num: %d\r\n", pool->liveNum, pool->exitNum);
      for(int i = pool->exitNum; i > 0; i--) {
        pthread_cond_signal(&pool->notEmpty);
      }
    }
    pthread_mutex_unlock(&pool->mutexPool);
  }
  return NULL;
}

void taskFunc(void* arg)
{
    int num = *(int*)arg;
    printf("thread %ld is working, number = %d\n",
        pthread_self(), num);
    sleep(1);
}

int main()
{
    // 创建线程池
    ThreadPool* pool = threadPoolCreate(3, 10, 100);
    for (int i = 0; i < 100; i++)
    {
        int* num = (int*)malloc(sizeof(int));
        *num = i + 100;
        addTask(pool, taskFunc, num);
    }

    sleep(30);

    threadPoolDestroy(pool);
    return 0;
}

3.bug分析
现象:
管理者线程销毁一个线程后程序卡死
分析:
管理则并不直接销毁线程,而是给工作着线程发生任务队列非空的条件变量,让工作者线程主动退出,工作者线程是在获取线程池锁后等待条件变量,但是在等待到条件变量,线程退出之前,没有释放线程池锁,导致其他工作者线程永远等待线程池锁释放,最终造成程序卡死。

void* worker(void* arg) {
  ThreadPool *pool = (ThreadPool *)arg;
  while(1) {
    pthread_mutex_lock(&pool->mutexPool);
    while(pool->taskNum == 0 && pool->shutdown == 0) {
      pthread_cond_wait(&pool->notEmpty, &pool->mutexPool);
      if(pool->exitNum > 0) {
        // printf("worker exit num: %d %lu\r\n", pool->exitNum, pthread_self());
        pool->exitNum--;
        if(pool->liveNum > pool->minNum) {
          pool->liveNum--;
          // 出现bug是因为没有这一句
          pthread_mutex_unlock(&pool->mutexPool);
          threadExit(pool);
        }
      }
    }
    // printf("running %lu\r\n", pthread_self());
    if(pool->shutdown) {
      // printf("shoutdown %lu\r\n", pthread_self());
      pthread_mutex_unlock(&pool->mutexPool);
      threadExit(pool);
    }
    Task t = pool->tq[pool->taskFront];
    pool->taskFront = (pool->taskFront + 1) % pool->taskCapacity;
    pool->taskNum--;
    pthread_cond_signal(&pool->notFull);
    pthread_mutex_unlock(&pool->mutexPool);

    pthread_mutex_lock(&pool->mutexBusy);
    pool->busyNum++;
    pthread_mutex_unlock(&pool->mutexBusy);
    printf("%lu start\r\n", pthread_self());
    t.function(t.arg);
    printf("%lu end\r\n", pthread_self());
    free(t.arg);
    t.arg = NULL;

    pthread_mutex_lock(&pool->mutexBusy);
    pool->busyNum--;
    pthread_mutex_unlock(&pool->mutexBusy);
  }
}

解决:
在线程退出前释放锁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值