[Linux]信號量

信號量是一個計數器,用于為多個進程提供對共享數據對象的訪問。

在信號量上只有三種操作可以進行,初始化、遞增和增加,這三種操作都是原子操作。遞減操作可以用于阻塞一個進程,增加操作用于解除阻塞一個進程。

為了獲得共享資源,需要測試信號量,若信號量為正,則進程可以使用該資源,這時信號量值減一。否則信號量值為0,進程進入休眠狀態。當進程不再使用由一個信號量控制的共享資源時,信號量值加一。如果有正在休眠的進程,則喚醒它們。常用的信號量的形式為二元信號量。即是原子性的。

信號量的功能:負責數據操作的互斥、同步等功能。本質上是一種數據操作鎖。

我們為什么要使用信號量呢?為了防止出現因多個程序同時訪問一個共享資源而引發的一系列問題,我們需要一種方法,它可以通過生成并使?用令牌來授權,在任一時刻只能有一個執行線程訪問代碼的臨界區域。臨界區域是指執行數據更新的代碼需要獨占式地執行。而信號量就可以提供這樣的一種訪問機制,讓一個臨界區同一時間只有一個線程在訪問它,也就是說信號量是用來協調進程對共享資源的訪問的。其中共享內存的使用就要用到信號量。

信號量只能進行兩種操作等待和發送信號,PV操作,即P(sv)和V(sv),P申請資源,則將可用資源數-1,V釋放資源,則將可用資源數+1。

內核為每個信號量集合維護著一個semid_ds結構:

struct semid_ds
{struct ipc_perm sem_perm;unsigned short sem_nsems; //該集合的信號量數目time_t sem_otime;time_t sem_ctime;
};

每個信號量都有一個無名的結構:

  unsigned short  semval;   /* semaphore value */unsigned short  semzcnt;  /* # waiting for zero */unsigned short  semncnt;  /* # waiting for increase */pid_t           sempid;   /* process that did last op */

當我們想使用信號量時,首先要通過調用函數semget來獲得一個信號量ID:

#include<sys/sem.h>
int semget(key_t key,int nsems,int flag);
//成功,返回信號量ID,出錯返回-1

semctl函數包含了多種信號量操作

int semctl(int semid,int semnum,int cmd,.../* union semun arg */);
//第四個參數是可選的,取決于請求的命令,如果使用該參數,則其類型是semun,它是多個命令特定的聯合。
union semun
{int val;       //for SETVAL;struct semid_ds *buf;     //for IPC_STAT and IPC_SET;unsigned short *array;    //for GETALL and SETALL
};

我們通常使用的:IPC_RMID:從系統中刪除該信號量集合

SETVAL:設置成員semnum的semval值,該值由arg.val指定

函數semop自動執行信號量集合上的操作數組。

int semop(int semid,struct sembuf semoparray[],size_t nops);
//成功,返回0,失敗,返回-1;semoparray是一個指針,指向由sembuf結構表示的信號量操作數組,nops規定該數組中操作的數量
struct sembuf
{unsigned short sem_num;   //0,1,2,3,....short sem_op;  //(負值,0,正值)-1,0,1short sem_flg;  //IPC_NOWAIT,SEM_UNDO
};

對集合中每個成員的操作由相應的sem_op值規定。此值可以為負值,0,正值。最易于處理的是為正值,說明需要釋放資源,則sem_op的值會加到信號量值上,如果指定了undo標志,則從此信號量調整之后的值上減去sem_op;如果sem_op是負值,說明需要申請資源,則信號量會減去sem_op的絕對值,如果指定undo標志,則sem_op的絕對值也加到信號量的調整值上。sem_op為0,表示調用進程希望等待到該信號量值為0。

對于信號量調整,如果在進程終止時,它占用了經由信號量分配的資源,那么就會成為一個問題。無論何時只要為信號量操作指定了SEM_UNDO標志,然后分配資源(sem_op< 0),那么內核就會記住該特定信號量,分配給調用進程多少資源。對每個操作都指定SEM_UNDO,以處理在未釋放資源條件下進程終止的情況。

信號量主要解決互斥與同步問題,下面舉個栗子:(實現父子進程輸出成對AA或BB)

//comm.h
#ifndef _COMM_H_
#define _COMM_H_#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>#define PATHNAME "."
#define PROJ_ID 0X6666
union semun {int  val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  };int CreateSem(int nums);
int  GetSem(int nums);
int DestroySem(int semid);
int initSem(int semid,int nums,int initval);
int SemP(int semid,int who);
int SemV(int semid,int who);#endif  //_COMM_H_
#include"comm.h"static int CommSemSet(int nsems,int flags)
{key_t key = ftok(PATHNAME,PROJ_ID);if(key < 0){//printf("%d\n", key);perror("ftok");return -1;}int semid = semget(key,nsems,flags);if(semid < 0){perror("semget");return -2;}return semid;
}int  CreateSemSet(int nums)
{return CommSemSet(nums,IPC_CREAT|IPC_EXCL|0666);
}int GetSem(int nums)
{return CommSemSet(nums,IPC_CREAT);
}
int DestroySem(int semid)
{if(semctl(semid,0,IPC_RMID) < 0){perror("semctl");return -1;}return 0;
}int initSem(int semid,int nums,int initval)
{union semun _un;_un.val =  initval;if(semctl(semid,nums,SETVAL,_un) < 0){perror("semctl");return -1;}return 0;
}static int CommPV(int semid,int who,int op)
{struct sembuf _sem;_sem.sem_num = who;_sem.sem_op = op;_sem.sem_flg = 0;if(semop(semid,&_sem,1) < 0){perror("semop");return -1;}   return 0;
}
int SemP(int semid,int who)
{return CommPV(semid,who,-1);
}
int SemV(int semid,int who)
{return CommPV(semid,who,1);
}
//sem.c
#include"comm.h"int main()
{int semid = CreateSemSet(1);initSem(semid,0,1); pid_t id = fork();if(id == 0)//child{int _semid = GetSem(0);while(1){SemP(_semid,0);printf("A");fflush(stdout);usleep(123456);printf("A");fflush(stdout);usleep(345678); SemV(_semid,0);}}else{while(1){SemP(semid,0);printf("B");fflush(stdout);usleep(234567);printf("B");fflush(stdout);usleep(456789);//  usleep(121212);SemV(semid,0);}wait(NULL);}DestroySem(semid);printf("sem quit!\n");return 0;
}
//Makefile
sem:sem.c comm.cgcc -o $@ $^
.PHONY:clean
clean:rm -f sem

運行結果:

這里寫圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/382699.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/382699.shtml
英文地址,請注明出處:http://en.pswp.cn/news/382699.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux VIM 程序中有游離的‘\357’ ‘\274’錯誤

gcc date.cpp -o date -lstdc date.cpp:18:20: 錯誤&#xff1a;程序中有游離的‘\357’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:21: 錯誤&#xff1a;程序中有游離的‘\274’date.Showdata()&#xfffd;&#xfffd;&#xfffd;^ date.cpp:18:22…

[Linux]關于SIGCHLD

之前我們就學過&#xff0c;關于wait和waitpid來處理僵尸進程&#xff0c;父進程等待子進程結束后自己才退出&#xff0c;這樣的方法有倆種方式&#xff0c;一種是父進程死死的等子進程退出&#xff0c;也就是使用阻塞的方式等待子進程退出&#xff0c;另一種方式是通過非阻塞的…

C語言思維導圖

本人能力有限&#xff0c;知識點難免概括不全&#xff0c;如有錯誤歡迎指正

轉載一篇關于curl的文章

轉載一篇關于curl的文章 http://www.360doc.com/content/16/0107/15/18578054_526158476.shtml

[Linux]vi/vim下添加多行注釋和取消注釋

添加注釋&#xff08;Centos&#xff09;&#xff1a; 在命令行模式下按ctrlV進入 visual block模式&#xff08;可視化模式&#xff09; 選中你需要注釋的行&#xff0c;再按大寫的I&#xff0c;輸入//&#xff0c;最后按倆下esc即可。 如果想讓前進tab個位&#xff0c;則可在…

pthread和互斥量條件變量函數意義速查表

數據類型 pthread_t 線程 互斥量和條件變量

[Linux]共享內存

共享內存是UNIX提供的進程間通信手段中速度最快的一種&#xff0c;也是最快的IPC形式。為什么是最快的呢&#xff0c;因為數據不需要在客戶進程和服務器進程之間復制&#xff0c;所以是最快的一種IPC。這是虛存中由多個進程共享的一個公共內存塊。 兩個不同進程A、B共享內存的…

僵尸進程的產生,危害和解決方案

概念 僵死狀態&#xff08;Zombies&#xff09;是一個比較特殊的狀態。 當進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產生僵尸進程。僵尸進程會以終止狀態保持在進程表中&#xff0c;并且會一直在等待父進程讀取退出狀態代碼。所以&#xff0c;只要子進程退出&…

CString string 轉換

https://www.cnblogs.com/HappyEDay/p/7016162.html

[Linux]gdb調試多進程多線程例程

gdb相信學linux的同學已經比較熟悉了吧&#xff0c;它是linux下代碼調試工具。我們在寫c語言&#xff0c;c的代碼時經常會用到&#xff0c;它有一些常用的調試命令: run&#xff08;r&#xff09;&#xff1a;運行程序&#xff0c;如果有斷點在下一個斷點處停止 start&#xf…

gdb調試常用命令速查(段錯誤調試)

編譯程序時需要加上-g&#xff0c;之后才能用gdb進行調試&#xff1a;gcc -g main.c -o main gdb中命令&#xff1a; 回車鍵&#xff1a;重復上一命令 &#xff08;gdb&#xff09;help&#xff1a;查看命令幫助&#xff0c;具體命令查詢在gdb中輸入help 命令,簡寫h &…

C語言字符串 小記

#include "stdafx.h" #include <iostream> #include <string.h> using namespace std;int _tmain(int argc, _TCHAR* argv[]) {char str1[] "12345"; // ""括起來的字符串 會在末尾增加 \0 cout << sizeof(str1) << en…

[Linux]守護進程(精靈進程)

一、守護進程是什么 守護進程是生存期很長的一種進程&#xff0c;可以說它是7*24小時工作的。&#xff08;什么是7*24&#xff0c;一周7天&#xff0c;每天24小時&#xff0c;這不就是一年365天一直在工作嘛&#xff0c;還搞的這么詼諧&#xff0c;哈哈&#xff09;。它們常常…

linux命令行界面下ctrl 常用組合鍵速查表

Ctrlz 暫停正在運行的程序 Ctrll 清屏 Ctrld 結束輸入或退出shell Ctrla 切換到命令行開始 Ctrle 切換到命令行末尾 Ctrlu 刪除光標前內容 Ctrlk 刪除光標后內容 Ctrlxu 撤銷操作

[Linux]運輸層的端口

既然提到端口&#xff0c;我們就來分析一下為什么要使用端口的緣由吧。我們首先要知道的是&#xff0c;運輸層有復用和分用的功能。應用層所有的應用進程都可以通過運輸層再傳送到IP層&#xff0c;這就是復用。運輸層從IP層收到數據后必須交付到指明的應用進程&#xff0c;這就…

淺談shell中的clear命令實現

NAME(名稱) clear - 清除終端屏幕 SYNOPSIS(總覽) clear DESCRIPTION(描述) clear可以在允許的情況下清屏. 它會在環境變量中查找終端的類型, 然后到terminfo數據庫中找出清屏的方法. 《man手冊》 #include <stdio.h>int clear_main(int argc, char **argv) {/* Th…

C++ 對引用的理解

引用可以看做是數據的一個別名&#xff0c;通過這個別名和原來的名字都能夠找到這份數據引用必須在定義的同時初始化&#xff0c;并且以后也要從一而終&#xff0c;不能再引用其它數據&#xff0c;這有點類似于常量&#xff08;const 變量&#xff09;。引用變量 里面 實際存儲…

[Linux]ARP協議

概念&#xff1a; 1. ARP協議(地址解析協議):由IP地址轉換為MAC地址的協議。IP地址&#xff1a;網絡號主機號。MAC地址&#xff1a;數據鏈路層的物理地址&#xff08;硬件地址&#xff09;。IP協議使用了ARP協議&#xff0c;因此被劃歸為網絡層&#xff0c;但其用途是從網絡層…

Makefile使用及多文件gdb 調試

文件內容 [koulocalhost makefile]$ cat 1.c #include "3.h" int main() {key_t key ftok(".",1);printf("%d\n",add(1,2));return 0; }[koulocalhost makefile]$ cat 2.c #include "3.h" int add(int a, int b) {return a b; } [k…

C++ 對引用的理解2

1.指針就是數據或代碼在內存中的地址&#xff0c;指針變量指向的就是內存中的數據或代碼。這里有一個關鍵詞需要強調&#xff0c;就是內存&#xff0c;指針只能指向內存&#xff0c;不能指向寄存器或者硬盤&#xff0c;因為寄存器和硬盤沒法尋址。 2.其實 C 代碼中的大部分內容…