天天看点

Linux线程同步与进程通信

线程同步与进程通信

线程同步

互斥量

  • 临界资源:在一段时间内只允许一个任务(线程或进程)访问资源。任务之间采用互斥的方式访问临界资源
  • 互斥量:

    pthread_mutex_t mutex;

    初始化、加锁、解锁、销毁
    • 加锁:

      int pthread_mutex_lock(); int pthread_mutex_trylock();

      在访问临界资源前,对互斥量进行加锁。

      trylock()

      未加锁则加锁,加锁后不重复加锁。
    • 解锁:

      int pthread_mutex_unlock();

      访问完成后对互斥量解锁
    • 销毁:

      int pthread_mutex_destory();

      互斥量使用完毕后对互斥量进行销毁,释放资源。

条件变量

  • 任务同步:多个线程都要访问临界资源又要相互合作,一个任务的执行依赖与另一个任务的执行情况。
  • 条件变量:

    pthread_cond_t cond;

    和互斥量一起使用,允许线程以互斥的方式阻塞等待条件的发生即为同步关系
    • 触发条件变量的线程:互斥量加锁>>某一操作>>触发条件变量>>互斥量解锁
    • 等待条件变量的线程:互斥量加锁>>等待条件变量>>某一操作>>互斥量解锁
    • 销毁:

      pthread_cond_destory();

      条件变量不再使用后进行销毁,释放空间
    • 等待条件变量:

      pthread_cond_wait();

      使得调用进程进入阻塞状态,直到条件触发
    • 触发条件变量:

      pthread_cond_signal();/pthread_cond_broadcast();

      可触发并唤醒等待条件变量的线程
  • 条件变量与互斥量要配合使用
    • 条件变量的使用场景伴随着临界资源的使用
    • 在调用

      pthread_cond_wait();

      前需要使互斥量处于加锁状态,通过原子操作将调用线程放到该条件变量等待线程队列(临界资源)中
      //等待条件变量的操作
      pthread_mutex_lock();
      pthread_cond_wait();
      pthread_mutex_unlock();
                 
  • 条件变量的使用

读写锁

  • 读写关系:读写互斥、写写互斥、读读不互斥
  • 读写锁:

    pthread_rwlock_t;

    • 加读锁:

      阻塞加锁pthread_rwlock_rdlock();/非阻塞加锁pthread_rwlock_tryrdlock();/限时加读锁pthread_rwlock_timedrdlock();

    • 加写锁:

      阻塞加锁pthread_rwlock_wrlock();/非阻塞加锁pthread_rwlock_trywrlock();/限时加读锁pthread_rwlock_timedwrlock();

    • 解锁:统一解锁函数,最近配对原则解锁

      pthread_rwlock_unlock();

进程通信

  • 管道和命名管道:文件的形式实现不同进程共享资源
  • XSI IPC机制:LINUX系统使用的进程通信机制
    功能 信号量 共享内存 消息队列
    创建或打开一个IPC对象,获得对IPC机制的访问权 semget shmget msgget
    IPC操作:信号量操作;连接/释放共享内存;发送/接收消息 semop shmat/shmdt msgsnd/msgrcv
    IPC控制:获得/修改IPC对象状态,“删除”IPC对象 semctl shmctl msgctl

消息队列

  • 消息队列:消息的链表,有写或者读权限的进程可以按照一定的规则添加或者读取消息
  • 消息队列的操作

    sys/types.h sys/ipc.h sys/msg.h

    • 打开或创建消息队列对象:

      int msgget();

    • 从消息队列接收消息:

      int msgrcv();

    • 向消息队列发送消息:

      int msgsnd();

    • 消息队列控制操作:

      int msgctl();

信号量

  • 互斥信号量:任务之间互斥访问临界资源 初始值为1
  • 计数信号量:任务之间竞争访问共享资源 初始值大于1,表示共享资源个数
  • 二值信号量:任务之间的同步机制 初始值为0
  • 信号量的数据结构
    struct s
    {
        int count; //资源计数
        Queue queue; //任务阻塞队列
    };
    
    int init(); // 初始化
    int P(); // P操作
    int V(); // V操作
               
  • P操作:分配资源
    --s.count;
    if(s.count < 0)
    {
    	调用进程进入s.queue;
    	阻塞调用进程;
    }
               
  • V操作:释放资源
    ++s.count;
    if(s.count <= 0 )
    {
    	从阻塞队列s.queue中取出一个进程P;
    	进程P进入就绪队列;
    }
               
  • 信号量集的操作

    sys/types.h sys/ipc.h sys/sem.h

    • 创建或者打开信号量集对象

      int semget();

      #include<stdio.h>
      #include<sys/sem.h>
      #define MYKEY 0x1a0a //定一个ipc的key值
      int main()
      {
      	int semid;
      	semid = semget(MYKEY,1,IPC_CREAT|IPC_R|IPC_W|IPC_M); //创建一个信号量集对象
      	printf("semid = %d\n",semid);
          return 0;
      }
                 
    • 信号量PV操作

      int semop();

      :根据参数

      short sem_op;

      判断执行的操作类型
      • sem_op > 0

        :信号量V操作,增加信号量的值
      • sem_op < 0

        :信号量P操作,减少信号量的值
      • sem_op = 0

        :对信号量的当前值是否为0的测试
      //实现P操作
      static int semaphore_v(void)
      {
      	struct sembuf sem_b;
      	sem_b.sem_num = 0; //信号量编号
      	sem_b.sem_op = -1; // P操作
      	sem_b.sem_fg = SEM_UNDO; //在进程没释放信号量而退出时,系统自动释放该进程中未释放的信号量
      	if(semop(sem_id, &sem_b,1) == -1)
      	{
      		fprintf(stderr,"semaphore_p failed\n");
      		return 0;
      	}
      	return 1;
      }
      
      //实现V操作
      static int semaphore_v(void)
      {
      	struct sembuf sem_b;
      	sem_b.sem_num = 0;
      	sem_b.sem_op = 1; // V操作
      	sem_b.sem_fg = SEM_UNDO;
      	if(semop(sem_id, &sem_b,1) == -1)
      	{
      		fprintf(stderr,"semaphore_v failed\n");
      		return 0;
      	}
      	return 1;
      }
                 
    • 信号量控制

      int semctl();

      int cmd;

      控制命令参数
      • 对信号量集的控制命令:

        IPC_RMID

        删除;

        IPC_SET

        设置参数;

        IPC_STAT

        获取参数;

        IPC_INFO

        获取系统信息
      • 对信号量的控制命令:

        SETVAL

        设置信号量的值;

        GETVAL

        获取信号量的值;

        GETPID

        获取信号量拥有者的PID值
      //信号量初始化
      int init_sem(int sem_id, int init_value)
      {
      	union semun_sem_union;
      	sem_union.val = init_value;
      	if(semctl(sem_id,0,SETVAL,sem_union) == -1)
      	{
      		perror("Initialize semaphore");
      		return -1;
      	}
      	return 0;
      }
      
      //从系统中删除信号量
      int del_sem(int sem_id)
      {
      	union semun sem_union;
      	if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
      	{
      		perror("Initialize semaphore");
      		return -1;
      	}
      }
                 

共享内存

  • 共享内存:不同进程可以将同一段内存空间链接到自己的地址空间中,类似物理空间中的全局变量
  • 共享内存的操作

    sys/types.h sys/ipc.h sys/shm.h

    • 创建或者打开共享内存对象

      int shmget();

    • 将共享内存链接到调用的进程的地址空间

      void *shmat();

    • 删除进程的共享内存链接

      int shmdt();

    • 对共享内存的操作

      int shmctl();

  • 共享内存应用–生产者/消费者问题
    //缓存池结构
    struct BufferPool
    {
    	char Buffer[5][100];
    	int index[5];
    };
               

继续阅读