三种专门用于线程同步的机制:POSIX信号量,互斥量和条件变量.
在Linux上信号量API有两组,一组是System V IPC信号量,即PV操作,另外就是POSIX信号量,POSIX信号量的名字都是以sem_开头.
phshared参数指定信号量的类型,若其值为0,就表示这个信号量是当前进程的局部信号量,否则该信号量可以在多个进程之间共享.value值指定信号量的初始值,一般与下面的sem_wait函数相对应.
其中比较重要的函数sem_wait函数会以原子操作的方式将信号量的值减一,如果信号量的值为零,则sem_wait将会阻塞,信号量的值可以在sem_init函数中的value初始化;sem_trywait函数是sem_wait的非阻塞版本;sem_post函数将以原子的操作对信号量加一,当信号量的值大于0时,其他正在调用sem_wait等待信号量的线程将被唤醒.
这些函数成功时返回0,失败则返回-1并设置errno.
生产者消费者模型:
生产者对应一个信号量:sem_t procer;
消费者对应一个信号量:sem_t customer;
sem_init(&procer,2)----生产者拥有资源,可以工作;
sem_init(&customer,0)----消费者没有资源,阻塞;
在访问公共资源前对互斥量设置(加锁),确保同一时间只有一个线程访问数据,在访问完成后再释放(解锁)互斥量.
互斥锁的运行方式:串行访问共享资源;
信号量的运行方式:并行访问共享资源;
互斥量用pthread_mutex_t数据类型表示,在使用互斥量之前,必须使用pthread_mutex_init函数对它进行初始化,注意,使用完毕后需调用pthread_mutex_destroy.
pthread_mutex_init用于初始化互斥锁,mutexattr用于指定互斥锁的属性,若为NULL,则表示默认属性。除了用这个函数初始化互斥所外,还可以用如下方式初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER。
pthread_mutex_destroy用于销毁互斥锁,以释放占用的内核资源,销毁一个已经加锁的互斥锁将导致不可预期的后果。
pthread_mutex_lock以原子操作给一个互斥锁加锁。如果目标互斥锁已经被加锁,则pthread_mutex_lock则被阻塞,直到该互斥锁占有者把它给解锁.
pthread_mutex_trylock和pthread_mutex_lock类似,不过它始终立即返回,而不论被操作的互斥锁是否加锁,是pthread_mutex_lock的非阻塞版本.当目标互斥锁未被加锁时,pthread_mutex_trylock进行加锁操作;否则将返回EBUSY错误码。注意:这里讨论的pthread_mutex_lock和pthread_mutex_trylock是针对普通锁而言的,对于其他类型的锁,这两个加锁函数会有不同的行为.
pthread_mutex_unlock以原子操作方式给一个互斥锁进行解锁操作。如果此时有其他线程正在等待这个互斥锁,则这些线程中的一个将获得它.
三个打印机轮流打印:
输出结果:
如果说互斥锁是用于同步线程对共享数据的访问的话,那么条件变量就是用于在线程之间同步共享数据的值.条件变量提供了一种线程之间通信的机制:当某个共享数据达到某个值时,唤醒等待这个共享数据的线程.
条件变量会在条件不满足的情况下阻塞线程.且条件变量和互斥量一起使用,允许线程以无竞争的方式等待特定的条件发生.
其中pthread_cond_broadcast函数以广播的形式唤醒所有等待目标条件变量的线程,pthread_cond_signal函数用于唤醒一个等待目标条件变量线程.但有时候我们可能需要唤醒一个固定的线程,可以通过间接的方法实现:定义一个能够唯一标识目标线程的全局变量,在唤醒等待条件变量的线程前先设置该变量为目标线程,然后采用广播的方式唤醒所有等待的线程,这些线程被唤醒之后都检查该变量以判断是否是自己.
采用条件变量+互斥锁实现生产者消费者模型:
运行结果:
阻塞队列+生产者消费者
运行结果:
‘贰’ linux系统上信号发送和信号接收讲解
用于进程间通信,通信机制由操作系统保证,比较稳定。
在linux中可以通过kill -l查看所有信号的类型。
kill -信号类型 进程ID
int kill(pid_t pid, int sig);
入参pid :
pid > 0: 发送信号给指定的进程。
pid = 0: 发送信号给 与调用kill函数进程属于同一进程组的所有进程。
pid < 0: 取|pid|发给对应进程组。
pid = -1:发送给进程有权限发送的系统中所有进程。
sig :信号类型。
返回值 :成功:0;失败:-1 (ID非法,信号非法,普通用户杀init进程等权级问题),设置errno
以OpenHarmony源码为例,应用ANR后,AbilityManagerService会通知应用mp堆栈信息,就是通过信号量做的。
头文件位置 :
include <signal.h>
函数解释 :
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
当接收到指定的信号signum时,就会跳转到参数handler指定的函数执行。其中handler的入参是信号值。
函数原型 :
signum参数指出要捕获的信号类型,act参数指定新的信号处理方式,oldact参数输出先前信号的处理方式(如果不为NULL的话)。
sigaction结构体
sa_handler 信号处理函数
sa_mask 在处理该信号时可以暂时将sa_mask 指定的信号集搁置
sa_flags 指定一组修改信号行为的标志。 它由以下零个或多个的按位或组成
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了 SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
sa_restorer 是一个替代的信号处理程序,当设置SA_SIGINFO时才会用它。
相关函数
int sigemptyset( sigset_t *set);
sigemptyset()用来将参数set信号集初始化并清空。
执行成功则返回0,如果有错误则返回-1。
完整示例
‘叁’ Linux 环境下的C语言, 关于 kill 发送信号和 signal() 函数, 具体问题在以下代码的注释处
pause()会令目前的进程暂停(进入睡眠状态), 直到被信号(signal)所中断。
当50信号触动了,pause将退出睡眠状态,执行printf和return
‘肆’ linux镄剆ignal
c璇瑷signal鍑芥暟镄勫簲鐢锛
signal鏄涓涓绯荤粺璋幂敤銆傛槸涓绉岖壒娈婄殑涓鏂锛屽綋镆愮岖壒瀹氱殑钬滆蒋浠朵腑鏂钬濆彂鐢熸椂銆傜敤浜庤皟鐢ㄧ殑绋嫔簭銆备腑鏂阃氩父鏄绋嫔簭杩愯屼腑鍑虹幇镄勭壒娈婃儏鍐碉纴濡傚紩鐢ㄧ壒娈婂唴瀛树腑镄勯潪娉曞湴鍧锛屾诞镣规暟琚0闄ゃ
signal()镄勫师鍨嬩负锛
#includesignal锛岿>
void(*signal(inthum锛寁oid(*func)(int)))(int)銆
signal涓镄刬nt_鏁板强鍏跺惈涔
SIGHUP1A缁堢鎸傝捣鎴栬呮带鍒惰繘绋嬬粓姝
SIGINT2A阌鐩树腑鏂锛埚侠reak阌琚鎸変笅锛
SIGQU99v3C阌鐩樼殑阃鍑洪敭琚鎸変笅
SIGILL4C闱炴硶鎸囦护
SIGABRT6C鐢盿bort(3)鍙戝嚭镄勯鍑烘寚浠
SIGFPE8C娴镣瑰纾甯
SIGKILL9AEFKill淇″彿
SIGSEGV11C镞犳晥镄勫唴瀛桦紩鐢
SIGPIPE13A绠¢亾镰磋:鍐欎竴涓娌℃湁璇籶ort镄勭¢亾
SIGALRM14A鐢盿larm(2)鍙戝嚭镄勪俊鍙
SIGTERM15A缁堟淇″彿
SIGUSR130,10,16A鐢ㄦ埛镊宸卞畾涔変俊鍙1
SIGUSR231,12,17A鐢ㄦ埛镊宸卞畾涔変俊鍙2
SIGCHLD20,17,18B瀛愯繘绋嬬粨𨱒熶俊鍙
SIGCONT19,18,25杩涚▼缁х画锛堟浘琚锅沧㈢殑杩涚▼锛
SIGSTOP17,19,23DEF缁堟㈣繘绋
SIGTSTP18,20,24D鎺у埗缁堢锛坱ty锛変笂鎸変笅锅沧㈤敭
SIGTTIN21,21,26D钖庡彴杩涚▼浼佸浘浠庢带鍒剁粓绔璇
SIGTTOU22,22,27D钖庡彴杩涚▼浼佸浘浠庢带鍒剁粓绔鍐
浠ヤ笅镄勪俊鍙锋病鍦≒OSIX.1涓鍒楀嚭锛岃屽湪SUSv2鍒楀嚭
SIGBUS10,7,10C镐荤嚎阌栾(阌栾镄勫唴瀛榑闂)
SIGPOLLASysV瀹氢箟镄凯ollable浜嬩欢锛屼笌SIGIO钖屼箟
SIGPROF27,27,29AProfiling瀹氭椂鍣ㄥ埌
SIGSYS12,-,12C镞犳晥镄勭郴缁熻皟鐢(SVID)
SIGTRAP5C璺熻釜/鏂镣规崟銮
SIGURG16,23,21BSocket鍑虹幇绱фユ浔浠(4.2BSD)
SIGVTALRM26,26,28A瀹为檯镞堕棿鎶ヨ︽椂阍熶俊鍙(4.2BSD)
SIGXCPU24,24,30C瓒呭嚭璁惧畾镄凛PU镞堕棿闄愬埗(4.2BSD)
SIGXFSZ25,25,31C瓒呭嚭璁惧畾镄勬枃浠堕檺鍒跺ぇ灏(4.2BSD)
锛埚逛簬SIGSYS銆係IGXCPU銆係IGXFSZ銆备互鍙婃煇浜涙満鍣ㄤ綋绯荤粨鏋勪笅镄凷IGBUS锛孡inux缂虹渷镄勫姩浣沧槸A(terminate)锛孲USv2鏄疌(terminateandmpcore)锛夈
浠ヤ笅鏄鍏朵粬镄勪竴浜涗俊鍙
淇″彿鍊煎勭悊锷ㄤ綔鍙戝嚭淇″彿镄勫师锲
SIGIOT6CIO鎹曡幏鎸囦护锛屼笌SIGABRT钖屼箟
SIGEMT7,-,7
SIGSTKFLT-,16,-A鍗忓勭悊鍣ㄥ爢镙堥敊璇
SIGIO23,29,22A镆怚/O镎崭綔濡备粖鑳藉熻繘琛屼简(4.2BSD)
SIGCLD-,-,18A涓岙IGCHLD钖屼箟
SIGPWR29,30,19A鐢垫簮鏁呴㱩(SystemV)
SIGINFO29,-,-A涓岙IGPWR钖屼箟
SIGLOST-,-,-A鏂囦欢阌佷涪澶
SIGWINCH28,28,20B绐椾綋澶у皬鏀瑰彉(4.3BSD,Sun)
SIGUNUSED-,31,-A链浣跨敤镄勪俊鍙(willbeSIGSYS)
Linux涓嫔备綍銮峰彇缃戝崱淇℃伅锛
镆ョ湅linux镄勭绣鍗′俊鎭姝ラゅ备笅锛氩伐鍏峰师鏂欙细linux镎崭綔绯荤粺鈶犲惎锷╨inux镎崭綔绯荤粺锛岃繘鍏ュ埌妗岄溃锛
鈶″惎锷ㄧ粓绔锛
鈶㈢粓绔杈揿叆锻戒护ifconfigeth0锛屽洖杞︼绂鈶linux镄勭绣鍗′俊鎭瑙h伙细
1.镆ョ湅缃戝崱鐢熶骇铡傚晢鍜屼俊鍙凤细镆ョ湅锘烘湰淇℃伅锛歭spci镆ョ湅璇︾粏淇℃伅锛歭spci-vvv#3涓灏忓啓镄剉镆ョ湅缃戝崱淇℃伅锛歭spci|grepEthernet锛
2.镆ョ湅缃戝崱椹卞姩锛氭煡鐪嬬绣鍗¢┍锷ㄤ俊鎭锛歭spci-vvv#镓惧埌缃戝崱璁惧囩殑璇︾粏淇℃伅锛屽寘𨰾缃戝崱椹卞姩#lsmod鍒楀嚭锷犺浇镄勬墍链夐┍锷锛屽寘𨰾缃戝崱椹卞姩锛
3.镆ョ湅缃戝崱椹卞姩鐗堟湰镆ョ湅妯″潡淇℃伅锛歮odifomolename>#鍏朵腑鍖呭惈version淇℃伅鎴#ethtool-idevicename>锛
4.镆ョ湅缃戠粶鎺ュ彛阒熷垪鏁版煡鐪嬬绣鍗℃帴鍙g殑涓鏂淇℃伅锛#cat/proc/interrupts|grepeth0鎴#ethtool-Seth0锛
5.镆ョ湅缃戝崱椹卞姩婧愮爜镄勭増链鍙疯В铡娅ntel缃戝崱椹卞姩婧愮爜锛屾墦寮瑙e帇缂╃洰褰曚笅镄*.spec鏂囦欢镆ョ湅椹卞姩镄勭増链锛
淇″彿he鏄鎸囦粈涔堬纻
淇″彿he鏄鎸囩殑鏄淇″彿镄勭瘒骞呰缉灏戯纴灏辨妸浠栧拰淇″彿閲忔斁鍦ㄤ竴璧蜂简淇″彿锛氾纸signal锛夋槸涓绉嶅勭悊寮傛ヤ簨浠剁殑鏂瑰纺銆备俊鍙锋椂姣旇缉澶嶆潅镄勯氢俊鏂瑰纺锛岀敤浜庨氱煡鎺ュ弹杩涚▼链夋煇绉崭簨浠跺彂鐢燂纴闄や简鐢ㄤ簬杩涚▼澶栵纴杩桦彲浠ュ彂阃佷俊鍙风粰杩涚▼链韬銆
linux闄や简鏀鎸乽nix镞╂湡镄勪俊鍙疯涔夊嚱鏁帮纴杩樻敮鎸佽涔夌﹀悎posix.1镙囧嗳镄勪俊鍙峰嚱鏁
涓轰粈涔坙inux閲岀殑淇″彿鍙戦佸嚱鏁板彨kill锻锛
鐖惰繘绋嬩细姣斿瓙杩涚▼鎻愭棭缁撴潫锛屽湪浣犲瓙杩涚▼鍙杙pid杩欎釜镞堕棿鐖惰繘绋嫔凡缁忕粨𨱒熶简锛岃繖涓镞跺欑浉褰扑簬瀛愯繘绋嫔彉鎴恴ombie锛屼细琚玦nit鏀跺吇锛堣繖涓鍦版柟鎴戜篃涓嶅お镍傦纴姝e父𨱒ヨ村簲璇ュ瓙杩涚▼镄刾pid鏄1锛岃存槑浣犵殑瀛愯繘绋嬭玦nit涔嫔栫殑杩涚▼鏀跺吇浜嗭级阒诲炵埗杩涚▼鎴栬厀hile1镄勪綔鐢ㄦ槸涓嶈╃埗杩涚▼缁撴潫锛屼互渚垮瓙杩涚▼鍙栧肩殑镞跺欑埗杩涚▼渚濈劧淇濈暀
linux淇″彿閲忕嚎绋嬭繘绋嫔尯鍒锛
淇″彿閲忓湪杩涚▼鏄浠ユ湁钖崭俊鍙烽噺杩涜岄氢俊镄勶纴鍦ㄧ嚎绋嬫槸浠ユ棤钖崭俊鍙疯繘琛岄氢俊镄勶纴锲犱负绾跨▼linux杩樻病链夊疄鐜拌繘绋嬮棿镄勯氢俊锛屾墍浠ュ湪sem_init镄勭浜屼釜鍙傛暟瑕佷负0锛岃屼笖鍦ㄥ氱嚎绋嬮棿镄勫悓姝ユ槸鍙浠ラ氲繃链夊悕淇″彿閲忎篃鍙阃氲繃镞犲悕淇″彿锛屼絾鏄涓鑸𨱍呭喌绾跨▼镄勫悓姝ユ槸镞犲悕淇″彿閲忥纴镞犲悕淇″彿閲忎娇鐢ㄧ亩鍗曪纴钥屼笖sem_t瀛桦偍鍦ㄨ繘绋嬬┖闂翠腑锛屾湁钖崭俊鍙烽噺蹇呴’LINUX鍐呮牳绠$悊锛岀敱鍐呮牳缁撴瀯structipc_ids瀛桦偍锛屾槸闅忓唴镙告寔缁镄勶纴绯荤粺鍏抽棴锛屼俊鍙烽噺鍒椤垹闄わ纴褰撶劧涔熷彲浠ユ樉绀哄垹闄わ纴阃氲繃绯荤粺璋幂敤鍒犻櫎锛
娑堟伅阒熷垪锛屼俊鍙烽噺锛屽唴瀛桦叡浜锛岃繖鍑犱釜閮芥槸涓镙风殑铡熺悊銆傦纴鍙涓嶈繃淇″彿閲忓垎涓烘湁钖崭笌镞犲悕
‘伍’ linux c signal函数
是第二次调用的时候输出的,第一次调用只是绑定了SIGUSR1的信号处理函数,不会进入该处理函数
为什么会有这样的输出呢?
signal函数是将信号与处理函数进行绑定,成功绑定则返回绑定之前的信号处理函数。那么来看看你的代码,第一次调用将sig_fun1绑定,无输出;第二次调用将sig_fun2绑定,也就是把sig_fun1替换下来,并且你还调用了它,参数为30,所以会有那样的输出。
该如何改呢?
其实你并没有涉及到linux的信号处理机制,光绑定是不够的,还需要发信号给它,才能真正进入信号处理过程。给你一个示例代码吧
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
void sig_fun2(int signo)
{
printf("in sig_fun2:%d\n", signo);
}
void sig_fun1(int signo)
{
printf("in sig_fun1:%d\n", signo);
}
int main()
{
unsigned long i;
if (signal(SIGUSR1, sig_fun1) == SIG_ERR)
{
printf("signal fun1 error\n");
exit(1);
}
sleep(15);
(signal(SIGUSR1, sig_fun2))(30);
sleep(15);
printf("done\n");
return 0;
}
/****************************C 代码完,下面是如何运行***************************/
首先编译,假设生成可执行程序为test
然后运行,我用的是后台运行: nohup ./test>output.txt &
注意,这种方法要将输出重定向到文件output.txt(名字无所谓),然后你会看到一个数字,就是pid进程号
最后,在15秒之内发送信号:kill -SIGUSR1 进程号
现在你就可以打开output.txt看输出结果了。如果用sleep的话会被打断,所以只有两个输出加上替换处理函数时的输出共3个,也可以换成 int n=15;while(n--)sleep(1);
-------------------------------------------------------------
怎么样,加分吧
-------------------------------------------------------------
1.我就是想问第二次绑定sig_fun2的时候,调用了第一次绑定的sig_fun1么?
调用了, (signal(SIGUSR1, sig_fun2))(30);就是这一句, signal(SIGUSR1, sig_fun2)是个函数指针,你这样写就是调用它了,但是这和信号处理没关系,写成signal(SIGUSR1, sig_fun2);就可以了
这就是你所说的成功则返回绑定之前的函数???
对
那当时绑定sig_fun1的时候,返回之前的处理函数是什么??
这个就是系统默认的了,比如SIGINT就是你ctrl+c取消程序执行发送的信号,它的处理函数就是结束程序的一系列动作,不过SIGUSR1是留给用户自定义的信号,系统默认应该是啥也不做的一个函数,例如void fun(int signo){},你也可以第一次绑定的时候就调用试试看对不对
2.还有我在看signal函数定义的时候,void(//...)(int) 最后传入的这个int整形参数就是我们自定义sig_fun()中所接收的30么??我看例子里面有的signal(SIGINT,myfunc);也没有带参数啊,搞不懂
是你理解错了,signal函数只是绑定,没涉及到调用绑定函数,不用带参数,信号处理函数不是像你这样调用的。callback回调你知道吧,就是先做好一个函数或过程放着,事件触发的时候才调用。那个30是你用普通函数调用的方式时的参数,跟信号处理一点关系也没有,你用60,70也没半毛钱关系。我猜你是想要调用信号处理函数,然后迷糊了,其实我上面说的“kill -SIGUSR1 进程号”就是触发程序调用该处理函数的信号,这和kill -9 杀死进程一个道理,只不过处理函数不同,结果不一样。ctrl+c也可以用信号的方式发送,kill -2 进程号,或者 kill -SIGINT 进程号
另外,站长团上有产品团购,便宜有保证