Linux编程基础 5.3:信号量

4 信号量

  • 信号量是专门用来解决进程同步与互斥问题的一种通信机制,它与信号无关;
  • 不同于管道、FIFO以及消息队列,一般不用来传输数据;
  • 信号量包括:表示资源数量的非负整型变量、修改信号量的原子操作P和V、该信号量下等待资源的进程队列。

使用信号量进行通信时,通常需要如下步骤:

  • 创建信号量/信号量集或者获取系统中已有的信号量/信号量集;
  • 初始化信号量:早期信号量通常初始化为1,但有些进程一次需要多个同类的临界资源或多个不同类且唯一的临界资源,因此可能需要初始化信号量集;
  • 信号量的P、V操作:根据进程请求修改信号量的数量,P操作使信号量-1,V操作使信号量+1;
  • 从系统中删除不需要的信号量。

Linux内核提供了三个系统调用:

  • semget
  • semctl
  • semop

4.1 semget函数

#include <sys/sem.h>int semget(key_t key, int nsems, int semflg);

功能:创建一个新的信号量集或获取一个已经存在的信号量集。

参数说明

  • key:传入参数,信号量的键值,通常为一个整数;
  • nsems:创建的信号量数目;
  • semflg:标志位,同open和msgget函数的标志位功能相似,用来设置权限
    – 权限位可与IPC_CREAT及IPC_EXCL发生位或;
    – IPC_PRIVATE:表示该信号量为当前进程的私有信号量。

返回值说明

  • 成功:返回信号量的标识符;
  • 不成功:返回-1并设置errno,常见errno值如下:
    – EACCES:表示进程无访问权限;
    – ENOENT:表示传入的键值不存在;
    – EINVAL:表示nsems小于0或信号量已达上限;
    – EEXIST:当semflg设置为IPC_CREAT和IPC_EXCL时,该信号量已存在。

4.2 semctl函数

#include <sys/sem.h>int semctl(int semid, int semnum, int cmd, ...);

功能:对信号量或信号量集进行控制。

参数说明

  • semid:信号量标识符,通常为semget函数的返回值;
  • semnum:信号量在信号量集中的编号,该参数在使用信号量集时才会使用,通常设置为0,表示取第一个信号;
  • cmd:对信号量进行操作,常用的设置为SETVAL和IPC_RMID:
    – SETVAL:表示semctl函数的功能为初始化信号量的值,信号量的值通过可选参数传入,信号量在使用前应先对其值进行设置;
    – IPC_RMID:表示semctl函数的功能为删除指定信号量。信号量的删除应由其所有者或创建者进行,没有被删除的信号量将会一直存在于系统中。
  • 最后一个参数是可选参数,依赖于参数cmd,使用该参数时,用户必须在程序自定义一个如下所示的共用体:
union semun{int val;				//cmd = SETVAL, 用于指定信号量值struct semid_ds *buf;	//cmd = IPC_STAT或IPC_SET, 该项才生效unsigned short *array;	//cmd = GETALL或SETALL, 该项才生效struct seminfo *_buf;	//cmd = IPC_INFO, 该项才生效
};
//semid_ds是一个由内核维护的记录信号量属性信息的结构体
struct semid_ds{struct ipc_perm sem_perm;	//所有者和标识权限time_t sem_otime;			//最后操作时间time_t sem_ctime;			//最后更改时间unsigned short sem_nsems;	//信号集中的信号数量
};

返回值说明

  • 成功:根据参数cmd的取值返回相应信息,通常为一个非负整数;
  • 不成功:返回-1并设置errno。

4.3 semop函数

#include <sys/sem.h>int semop(int semid, struct sembuf *sops, unsigned nsops);

功能:改变信号量的值。

参数说明

  • semid:信号量标识符,通常为semget函数的返回值;
  • sops:一个struct sembuf类型的数组指针,该数组中的每个元素设置了要对信号量集的哪个信号做哪种操作;
struct sembuf{short sem_num;			//信号量在信号量集中的编号short sem_op;			//信号量操作short sem_flag;			//标志位
};

– sem_op = -1:表示P操作;
– sem_op = +1:表示V操作;
– 通常设置sem_flag = SEM_UNDO:若进程退出前没有删除信号量,则信号量将会由系统自动释放。

  • nsops:表示参数sops所指数组中元素的个数。

返回值说明

  • 成功:返回0;
  • 不成功:返回-1并设置errno。

【案例1】使用信号量实现父子进程同步,防止父子进程抢夺CPU。

#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
//自定义共用体
union semu{int val;struct semid_ds* buf;unsigned short* array;struct seminfo* _buf;
};
static int semId;
//设置信号量值
static int setSemValue() {union semu tempSemUnion;tempSemUnion.val = 1;if (semctl(semId, 0, SETVAL, tempSemUnion) == -1){return 0;}//of ifreturn 1;
}//of setSemValue
//p操作,获取信号量
static int semaphoreP() {struct sembuf tempSemBuf;tempSemBuf.sem_num = 0;tempSemBuf.sem_op = -1;tempSemBuf.sem_flg = SEM_UNDO;if (semop(semId, &tempSemBuf, 1) == -1){perror("sem_p err");return 0;}//of ifreturn 1;
}//of semaphoreP
//V操作,释放信号量
static int semaphoreV() {struct sembuf tempSemBuf;tempSemBuf.sem_num = 0;tempSemBuf.sem_op = 1;tempSemBuf.sem_flg = SEM_UNDO;if (semop(semId, &tempSemBuf, 1) == -1){perror("sem_v err");return 0;}//of ifreturn 1;
}//of semaphoreV
//删除信号量
static void delSemValue() {union semu tempSemUnion;if (semctl(semId, 0, IPC_RMID, tempSemUnion) == -1){perror("del err");}//of if
}//of delSemValue
int main() {int i;pid_t temPid;char tempChar = 'C';semId= semget((key_t)1000, 1, 0664 | IPC_CREAT);//创建信号量if (semId== -1){perror("sem_c err");exit(-1);}//of ifif (!setSemValue()){					//设置信号量值perror("init err");exit(-1);}//of iftemPid = fork();								//创建子进程if (temPid== -1){								//若创建失败delSemValue();						//删除信号量exit(-1);} else if (temPid == 0){     						//设置子进程打印的字符tempChar = 'Z';} else {										//设置父进程打印的字符tempChar = 'C';}//of ifsrand((unsigned int)getpid());			//设置随机数种子for (i = 0; i < 8; i++)	{						//循环打印字符semaphoreP();						//获取信号量printf("%c", tempChar);fflush(stdout);						//将字符打印到屏幕sleep(rand() % 4);						//沉睡printf("%c", tempChar);fflush(stdout);						//再次打印到屏幕sleep(1);semaphoreV();						//释放信号量}//of for iif (temPid > 0){wait(NULL);							//回收子进程delSemValue();						//删除信号量}//of ifprintf("\nprocess %d finished.\n", getpid());return 0;
}//of main

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/507417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谷歌浏览器外贸版_针对谷歌SEO,你有哪些值得推荐的工具、插件、网站、app,或者技巧分享?...

接触并了解谷歌SEO也有3年了&#xff0c;这3年来&#xff0c;一直钻研这块技术和工具&#xff0c;今天刚好看到这个问题&#xff0c;就分享下。目前谷歌SEO主要分3大块&#xff0c;站内SEO(On page seo&#xff0c;做好站内优化)&#xff0c;站外SEO(off page seo 主要是外链)&…

hadoopsdk使用_hadoop部署使用问题及解决

在cygwin环境中填写路径信息时务必注意将“\”替换为“/”。hadoop文件夹名称不能包含“-”&#xff0c;比如“hadoop-2.4.0”会出错。在cygwin环境中尽管“C:\abc”和“/cygdrive/c/abc”都可以被正确识别&#xff0c;但某些软件会将前者识别为相对路径&#xff0c;从而出现错…

Linux编程基础 5.4:共享内存

5 共享内存 共享内存允许两个或多个进程访问给定的同一块存储区域。它是效率最高的一种进程通信方式&#xff0c;节省了不同进程间多次读写的时间&#xff1b;在写进程的操作尚未完成时&#xff0c;不应有进程从共享内存中读取数据。共享内存自身不限制对共享内存的读写次序&a…

升级浏览器_微软IE11浏览器 最后的升级机会

微软IE10浏览器将很快退出支持&#xff0c;许多Windows用户将没有任何安全或非安全更新&#xff0c;免费或付费辅助支持选项或在线技术内容等。幸运的是&#xff0c;微软正在为Windows用户提供升级到IE11的最后机会&#xff0c;目前IE11仅次于Firefox浏览器&#xff0c;这是互联…

jq如何获取选中option的值_【分享】如何获取变量token的值

一.什么是token客户端使用用户名跟密码请求登录服务端收到请求&#xff0c;去验证用户名与密码验证成功后&#xff0c;服务端会签发一个 Token&#xff0c;再把这个 Token 发送给客户端客户端收到 Token 以后可以把它存储起来&#xff0c;比如放在 Cookie 里或者 LocalStorage …

Linux编程基础 6.1:线程操作

1 线程操作 创建线程 挂起线程 终止线程 其它操作 1.1 创建线程 #include <pthread.h>int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); 功能&#xff1a;创建线程&#xff1b;线程调用pthread_crea…

灯效控制器和rgb控制器_更具个性的RGB风扇,机箱里的魔术师,九州风神MF120GT开箱...

写在前面不知道在2020年还有多少人会自己装机&#xff0c;相信喜欢个性的DIY玩家还是会陆陆续续跳进这个不小的坑。装机的乐趣在哪里&#xff0c;除了几大硬件&#xff0c;显然个性的灯效才是装机的灵魂。在光污染的道路上&#xff0c;普通的光环&#xff0c;光面等风扇&#x…

华为交换机ssh思科交换机_华为交换机SSH(stelnet)配置实例教程

Stelnet(安全telnet)登陆也成为shh(sercure shell,安全外壳)登陆-----------必要配置---------------1、先根据加密算法生成秘钥对,用于传输数据时加密保护&#xff0c;保存在交换机中但不保存在配置文件中[Huawei]rsa local-key-pair create或[Huawei]dsa local-key-pair crea…

Linux编程基础 6.2:线程同步

2 线程同步 线程同步中的“同步”与生活中大家认知的“同步”略有不同&#xff0c;“同”不指同时&#xff0c;其主旨在于协同步调&#xff0c;按预定的先后次序执行线程&#xff1b;之所以需要实现线程同步&#xff0c;是因为若不对线程的执行次序加以控制&#xff0c;可能会…

电脑开两个微信_电脑怎么登录两个微信

1/4下载并安装微信电脑客户端&#xff0c;保证这台电脑没有登陆微信2/4就像正常打开微信一样&#xff0c;不过不是双击&#xff0c;而是快速连点四次3/4我们可以看到有两个微信登陆界面4/4用两个不同的账号进行扫码登陆即可

idea 查询项目代码行数_idea统计代码行数Statistic的步骤详解

idea统计代码行数Statistic的步骤详解idea统计代码行数可以用到插件&#xff1a;Statistic。步骤&#xff1a;File→Settings进入Plugins点击Marketplace搜索Statistic 安装蓝框标出的插件重启idea后就可以看到效果了(图是拿的别人的&#xff0c;基本就是这效果)如果没有下…

Linux编程基础 7.1:套接字通信流程及编程接口

1 socket通信流程 2 socket编程接口 Linux系统中常用的socket网络编程接口有&#xff1a; socket()bind()listen()accept()connect()send()recv()close()其中connect()与send()为客户端专用接口&#xff1b;bind()、listen()、accept()及recv()为服务器端专用接口&#xff1b…

cad2016中选择全图字体怎么操作_cad教程分享CAD中如何删除顽固图层?

Autocad教程公众号&#xff0c;专注于cad教程、cad教程视频的分享&#xff0c;欢迎关注&#xff0c;下载你所需的教程资源&#xff01;如你还未关注&#xff0c;请点击文章标题下方蓝色字体的"Autocad教程"进行关注。cad教程分享-CAD中如何删除顽固图层&#xff1f;方…

python开启多个端口服务_python bottle使用多个端口(多个进程)提高并发

我的程序是用python结合bottle框架写的&#xff0c;但bottle自带wsgi原本只是单进程单线程运行模式(Bottle 默认运行在内置的 wsgiref 服务器上面。这个单线程的 HTTP 服务器在开发的时候特别有用&#xff0c;但其性能低下&#xff0c;在服务器负载不断增加的时候也许会是性能瓶…

Linux编程基础 7.2:服务器和客户端编程案例

1 网络字节序 大端模式&#xff1a;若将数据的高字节保存在内存的低地址&#xff0c;将数据的低字节保存在内存的高地址&#xff1b; 小端模式&#xff1a;若将数据的高字节保存在内存的高地址&#xff0c;将数据的低字节保存在内存的低地址。 网络数据流&#xff1a;大端模式…

div 隐藏_div的position属性

如果你想把div放到合适的位置&#xff0c;请看看这篇文章。<!-- div的position属性--><html><style>.red{height:100px;background:red;}.green{height:100px;background:green;position:relative;left:50px;top:50px;}.black{height:100px;background:black…

苹果笔记本python怎么换行_python怎么换行,我的换行就是执行啊

展开全部首先运行终端或者cmd命令行(windows下)。e69da5e887aa62616964757a686964616f31333433646338执行python3.5的命令。然后输入如下图所示的内容。这种换行方法也可以在编辑器中进行&#xff0c;这里以vim为例&#xff0c;输入与上图类似的代码&#xff0c;保存为t.py脚本…

Linux编程基础 7.3:套接字本地通信

1 socket本地通信 socket原本是为网络通讯设计的&#xff0c;但后来在socket框架的基础上发展出了一种IPC&#xff08;进程通信&#xff09;机制&#xff0c;即UNIX Domain Socket&#xff0c;专门用来实现使用socket实现的本地进程通信。 本地通信的流程与使用的接口与基于TC…

乔布斯在斯坦福大学演讲稿英文_西方大文豪最爱的10个英文单词,写尽人世间细腻情感!...

从小浸染在汉语中的我们&#xff0c;常被汉字的意象美震撼到&#xff0c;一字就是一世界。汉字有种无与伦比的美丽&#xff0c;寥寥数字就能营造“只可意会不可言传”的意境&#xff0c;很多人感慨英文就是一串拉丁字母&#xff0c;无法传递细腻的情感。比如很多人说像「缘分」…

dorado 刷新_dorado7常用内容

tabControl").set("currentTab","tab2");// 通过tab的索引(index)属性切换view.get("#tabControl").set("currentIndex",1);//根据名字切换tabvar tab self.get("currentTab").get("name");if(tab!"ta…