初探信号量

1.信号量

信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享。

2.信号量的工作原理

由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:

(1)P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行

(2)V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)

注:原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的

3.二元信号量

二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁),它只用两种状态:占用与非占用。所以它的引用计数为1。

4.进程如何获得共享资源

(1)测试控制该资源的信号量

(2)信号量的值为正,进程获得该资源的使用权,进程将信号量减1,表示它使用了一个资源单位

(3)若此时信号量的值为0,则进程进入挂起状态(进程状态改变),直到信号量的值大于0,若进程被唤醒则返回至第一步。

注:信号量通过同步与互斥保证访问资源的一致性。

5.一个demo

一个文件原来里面写了个1,然后有二十个进程,并发的对该文件取出该数,然后+1 写回

#define PROCNUM 20
#define FILENAME "log"
#define LINESIZE 1024

int semid;

void P(void)
{
    
    
   struct sembuf op;

	//对semid的数组下标为0 进行操作
	//每次操作资源总量-1
	//特殊要求没有
   op.sem_num = 0;
   op.sem_op = -1;
   op.sem_flg = 0;

   while(semop(semid, &op, 1) < 0)
   {
    
    
           if(EINTR != errno || EAGAIN != errno)
           {
    
    
                   perror("semop()");
                   exit(1);
           }
   }
}


void V(void)
{
    
    
   struct sembuf op;
	//对semid的下标为0的位置 进行操作
	//每次操作对资源总量+1
	//特殊要求没有
   op.sem_num = 0;
   op.sem_op = 1;
   op.sem_flg = 0;

   if(semop(semid, &op, 1) < 0)
   {
    
    
           perror("semop()");
           exit(1);
   }
}

void  addFun(void)
{
    
    
   FILE *fp  = fopen(FILENAME,"r+");
   char Buf[LINESIZE];

   if(NULL == fp)
   {
    
    
           perror("fopen()");
           exit(1);
   }

   P();
   fgets( Buf, LINESIZE, fp);
   fseek(fp, 0, SEEK_SET);
   sleep(1);
   fprintf(fp, "%d\n", atoi(Buf)+1);
   fflush(fp);
   V();

   fclose(fp);

}

int main()
{
    
    
   int i, pid;
   int res;

   //父子进程之间匿名
   semid = semget(IPC_PRIVATE, 1, 0600);
   if(semid < 0)
   {
    
    
           perror("semget()");
           exit(1);
   }

   //对数组下标为0的 设置资源总量为1
   res = semctl(semid, 0, SETVAL , 1);
   if(res < 0)
   {
    
    
           perror("semctl()");
           exit(1);
   }

   for(i = 0 ; i < PROCNUM; i++)
   {
    
    
           pid = fork();
           if(pid < 0)
           {
    
    
                   perror("fork()");
                   exit(1);
           }

           if(0 == pid)
           {
    
    
                   addFun();
                   exit(0);
           }

   }



   for(i = 0; i < PROCNUM; i++)
   {
    
    
           wait(NULL);
   }

   semctl(semid, 0, IPC_RMID);

   return 0;
}

猜你喜欢

转载自blog.csdn.net/ZZHinclude/article/details/119906665
今日推荐