UNIX&Linux操作系统编程-线程同步和进程间通信

上传人:M****1 文档编号:569739196 上传时间:2024-07-30 格式:PPT 页数:211 大小:3.10MB
返回 下载 相关 举报
UNIX&Linux操作系统编程-线程同步和进程间通信_第1页
第1页 / 共211页
UNIX&Linux操作系统编程-线程同步和进程间通信_第2页
第2页 / 共211页
UNIX&Linux操作系统编程-线程同步和进程间通信_第3页
第3页 / 共211页
UNIX&Linux操作系统编程-线程同步和进程间通信_第4页
第4页 / 共211页
UNIX&Linux操作系统编程-线程同步和进程间通信_第5页
第5页 / 共211页
点击查看更多>>
资源描述

《UNIX&Linux操作系统编程-线程同步和进程间通信》由会员分享,可在线阅读,更多相关《UNIX&Linux操作系统编程-线程同步和进程间通信(211页珍藏版)》请在金锄头文库上搜索。

1、第四讲:线程同步与进程间通信第四讲:线程同步与进程间通信线程同步与进程间通信线程同步与进程间通信n线程同步机制线程同步机制l互斥量互斥量l读写锁读写锁l条件变量条件变量n进程间通信机制进程间通信机制l管道管道lXSI IPC机制机制l信号信号2024年7月30日3互斥量互斥量n可以通过使用可以通过使用pthread的互斥接口保护数据,确保的互斥接口保护数据,确保同一时间里只有一个线程访问同一时间里只有一个线程访问共享资源或临界区共享资源或临界区域域n互斥量互斥量mutex,本质上就是一把锁,本质上就是一把锁l在访问共享在访问共享资源后临界区域资源后临界区域前,对互斥量进行加锁前,对互斥量进行加

2、锁l在访问完成后释放互斥量上的锁在访问完成后释放互斥量上的锁l对互斥量进行加锁后,任何其他试图再次对互斥量加对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞,直到锁被释放锁的线程将会被阻塞,直到锁被释放2024年7月30日4互斥量的初始化互斥量的初始化n静态初始化(静态分配的互斥量)静态初始化(静态分配的互斥量)lpthread_mutex_tmlock=PTHREAD_MUTEX_INITIALIZER;n动态初始化(动态分配的互斥量)动态初始化(动态分配的互斥量)l函数原型函数原型#includeint pthread_mutex_init(pthread_mutex_t

3、*mutex,const pthread_mutexattr_t *attr);l参数与返回值参数与返回值mutex:即互斥量,类型是:即互斥量,类型是pthread_mutex_t注意:注意:mutex必须指向有效的内存区域必须指向有效的内存区域attr:设置互斥量的属性,通常可采用默认属性,即可将:设置互斥量的属性,通常可采用默认属性,即可将attr设为设为NULL。成功返回成功返回0,出错返回错误码,出错返回错误码2024年7月30日5互斥量的销毁互斥量的销毁n互斥量在使用完毕后,必须要对互斥量进行销毁,互斥量在使用完毕后,必须要对互斥量进行销毁,以释放资源以释放资源n函数原型函数原型#

4、includeint pthread_mutex_destroy( pthread_mutex_t *mutex);n参数与返回值参数与返回值lmutex:即互斥量:即互斥量l成功返回成功返回0,出错返回错误码,出错返回错误码2024年7月30日6互斥量的加锁和解锁操作互斥量的加锁和解锁操作n在对共享资源访问之前和访问之后,需要对互斥量在对共享资源访问之前和访问之后,需要对互斥量进行加锁和解锁操作进行加锁和解锁操作n函数原型函数原型#includeint pthread_mutex_lock(pthread_mutex_t *mutex);Int pthread_mutex_unlock(pt

5、hread_mutex_t *mutex);2024年7月30日7尝试尝试加锁加锁n当使用当使用pthread_mutex_lock时,若已被加锁,时,若已被加锁,则调用线程将被阻塞。有没有办法让线程不阻塞,则调用线程将被阻塞。有没有办法让线程不阻塞,即实现非阻塞的语义即实现非阻塞的语义?n函数原型函数原型#includeint pthread_mutex_trylock(pthread_mutex_t *mutex);n调用该函数时,若互斥量未加锁,则锁住该互斥调用该函数时,若互斥量未加锁,则锁住该互斥量,返回量,返回0;若互斥量已加锁,则函数直接返回失;若互斥量已加锁,则函数直接返回失败,

6、即败,即EBUSY2024年7月30日8互斥量的操作顺序互斥量的操作顺序n定义一个互斥量定义一个互斥量pthread_mutex_tn调用调用pthread_mutex_init初始化互斥量初始化互斥量n调用调用pthread_mutex_lock或者或者pthread_mutex_trylock对互斥量进行加锁操作对互斥量进行加锁操作n调用调用pthread_mutex_unlock对互斥量解锁对互斥量解锁n调用调用pthread_mutex_destroy销毁互斥量销毁互斥量2024年7月30日9参考代码参考代码2024年7月30日10条件变量条件变量n现有一需求,线程现有一需求,线程A先

7、执行某操作先执行某操作(例如(例如对变量对变量的修改的修改)后,线程后,线程B才能执行另一操作才能执行另一操作(根据变根据变量的值进行判断量的值进行判断),该如何实现?,该如何实现?n条件变量与互斥量一起使用时,允许线程以无竞条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定条件的发生争的方式等待特定条件的发生n与互斥量类似,条件变量也需要初始化和销毁与互斥量类似,条件变量也需要初始化和销毁2024年7月30日11条件变量的初始化条件变量的初始化n静态初始化(静态分配的条件变量)静态初始化(静态分配的条件变量)lpthread_cond_tcv=PTHREAD_COND_INITIAL

8、IZER;n动态初始化(动态分配的条件变量)动态初始化(动态分配的条件变量)l函数原型函数原型#includeint pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);l参数和返回值参数和返回值cond:条件变量:条件变量attr:条件变量属性,若为:条件变量属性,若为NULL,则使用默认属性,则使用默认属性成功返回成功返回0,出错返回错误编号,出错返回错误编号2024年7月30日12条件变量的销毁条件变量的销毁n函数原型函数原型int pthread_cond_destroy(pthread_cond_t *

9、cond);n参数和返回值参数和返回值lcond:条件变量:条件变量l成功返回成功返回0,出错返回错误编号,出错返回错误编号2024年7月30日13等待条件的发生等待条件的发生npthread_cond_wait函数将使调用线程进入阻塞函数将使调用线程进入阻塞状态,直到条件为真状态,直到条件为真n函数原型函数原型#includeint pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);n参数与返回值参数与返回值lcond:条件变量:条件变量lmutex:互斥量:互斥量l成功返回成功返回0,否则返回错误编号,否则返回错

10、误编号 2024年7月30日14等待条件的发生等待条件的发生n为什么为什么pthread_cond_wait需要互斥量需要互斥量l在调用在调用pthread_cond_wait前,需要使互斥量处于锁前,需要使互斥量处于锁住状态住状态l这样这样pthread_cond_wait函数,可以以原子的方式,函数,可以以原子的方式,将调用线程放到等待条件的线程列表上将调用线程放到等待条件的线程列表上npthread_cond_wait函数的特殊操作(函数的特殊操作(为什么?为什么?)l在线程阻塞前,调用在线程阻塞前,调用pthread_mutex_unlockl在线程唤醒后,调用在线程唤醒后,调用pth

11、read_mutex_lock2024年7月30日15等待条件的发生等待条件的发生n等待线程的操作顺序等待线程的操作顺序l调用调用pthread_mutex_lockl调用调用pthread_cond_waitl调用调用pthread_mutex_unlock2024年7月30日16使等待的条件为真使等待的条件为真npthread_cond_signal和和pthread_cond_broadcast可以通知等待的线程,条件已经满足。可以通知等待的线程,条件已经满足。npthread_cond_signal唤醒某一个等待该条件的线程唤醒某一个等待该条件的线程npthread_cond_broa

12、dcast唤醒等待该条件的所有线唤醒等待该条件的所有线程程2024年7月30日17等待条件为真等待条件为真n函数原型函数原型#includeint pthread_cond_signal(pthread_cond_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);n参数与返回值参数与返回值lcond:条件变量:条件变量l成功返回成功返回0,否则返回错误编号,否则返回错误编号思考:思考:能否用一个条件变量和多个互斥量一起使用?能否用一个条件变量和多个互斥量一起使用?多个消费者线程阻塞在相同条件变量但不同互斥量上多个消费者线程阻塞在

13、相同条件变量但不同互斥量上?2024年7月30日18参考代码参考代码intmain()pthread_ttid1,tid2;count=0;pthread_mutex_init(&count_lock,NULL);pthread_cond_init(&count_ready,NULL);pthread_create(&tid1,NULL,decrement_count,NULL);sleep(1);pthread_create(&tid2,NULL,increment_count,NULL);pthread_join(tid2,NULL);printf(decrementquitn);pthr

14、ead_join(tid1,NULL);return0;void*decrement_count(void*arg)pthread_mutex_lock(&count_lock);printf(decrement:waitingn);/*等待满足条件,期间互斥量仍然可用*/pthread_cond_wait(&count_ready,&count_lock);count=count-1;printf(decrement:count=%dn,count);pthread_mutex_unlock(&count_lock);printf(“decrementquitn”);pthread_exit

15、(NULL);void*increment_count(void*arg)pthread_mutex_lock(&count_lock);printf(increment:runningn);count=count+1;/*通知线程条件已满足*/pthread_cond_signal(&count_ready);printf(increment:count=%dn,count);pthread_mutex_unlock(&count_lock);pthread_exit(NULL);2024/7/3019读写锁通信机制读写锁通信机制 n在对数据的读写应用中,更多的是读操作,而写操作在对数据的读

16、写应用中,更多的是读操作,而写操作较少,例如对数据库数据的读写应用。为了满足当前较少,例如对数据库数据的读写应用。为了满足当前能够允许多个读出,但只允许一个写入的需求,线程能够允许多个读出,但只允许一个写入的需求,线程提供了读写锁来实现。其基本原则如下:提供了读写锁来实现。其基本原则如下:l(1)如果有线程读数据,则允许其它线程执行读操作,但)如果有线程读数据,则允许其它线程执行读操作,但不允许写操作不允许写操作l(2)如果有线程写数据,则其它线程的读、写操作均不允)如果有线程写数据,则其它线程的读、写操作均不允许许n将锁分为读锁和写锁将锁分为读锁和写锁l(1)如果某线程申请了读锁,其它线程可

17、以再申请读锁,)如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁但不能申请写锁l(2)如果某线程申请了写锁,则其它线程不能申请读锁,)如果某线程申请了写锁,则其它线程不能申请读锁,也不能申请写锁也不能申请写锁2024/7/30人民邮电出版社出版杨宗德编著20读写锁基本操作读写锁基本操作 功能函数初始化读写锁pthread_rwlock_init阻塞申请读锁pthread_rwlock_rdlock非阻塞申请读锁pthread_rwlock_tryrdlock阻塞申请写锁pthread_rwlock_wrlock非阻塞申请写锁pthread_rwlock_trywrlock释放锁(无

18、论是读锁还是写锁)pthread_rwlock_unlock销毁读写锁pthread_rwlock_destroy2024/7/3021读写读写锁的初始化和销毁锁的初始化和销毁n定义读写锁对象的代码如下:pthread_rwlock_trwlock;nintpthread_rwlock_init(pthread_rwlock_t*_restrict_rwlock,_constpthread_rwlockattr_t*_restrict_attr);nexternintpthread_rwlock_destroy(pthread_rwlock_t*_rwlock);n返回值:成功返回0,否则返回

19、错误代码2024/7/3022读写读写锁的使用锁的使用n*读模式下加锁*/intpthread_rwlock_rdlock(pthread_rwlock_t*_rwlock);n/*非阻塞的读模式下加锁*/intpthread_rwlock_tryrdlock(pthread_rwlock_t*_rwlock);n/*限时等待的读模式加锁*/intpthread_rwlock_timedrdlock(pthread_rwlock_t*_restrict_rwlock,_conststructtimespec*_restrict_abstime);2024/7/3023读写读写锁的使用锁的使用n

20、/*写模式下加锁*/intpthread_rwlock_wrlock(pthread_rwlock_t*_rwlock);n/*非阻塞的写模式下加锁*/intpthread_rwlock_trywrlock(pthread_rwlock_t*_rwlock);n/*限时等待的写模式加锁*/intpthread_rwlock_timedwrlock(pthread_rwlock_t*_restrict_rwlock,_conststructtimespec*_restrict_abstime);n/*解锁*/intpthread_rwlock_unlock(pthread_rwlock_t*_r

21、wlock);2024/7/3024参考代码参考代码线程同步与进程间通信线程同步与进程间通信n线程同步机制线程同步机制l互斥量互斥量l读写锁读写锁l条件变量条件变量n进程间通信机制进程间通信机制l管道管道lXSI IPC机制机制l信号信号26特殊文件特殊文件-管道管道n最古老、最简单的最古老、最简单的UNIX进程间通信机制进程间通信机制n管道是一种特殊文件管道是一种特殊文件n管道的局限性管道的局限性l半双工,只能一个进程写,一个进程读半双工,只能一个进程写,一个进程读l只能在父子进程之间使用只能在父子进程之间使用27管道的创建管道的创建npipe函数创建一个通信缓冲区,程序可以通过文函数创建一

22、个通信缓冲区,程序可以通过文件描述符件描述符fildes0和和fildes1来访问这个缓冲区来访问这个缓冲区nfiledes0为读而打开,为读而打开,filedes1为写而打开为写而打开n写入写入fildes1的数据可以按照先进先出的顺序从的数据可以按照先进先出的顺序从fildes0中读出中读出#include int pipe(int fildes2);n如果成功则返回如果成功则返回0。否则返回。否则返回-1,并设置,并设置errno。28管道的使用方法管道的使用方法29管道程序示例管道程序示例#include#include#include#include#defineBUFSIZE10i

23、ntmain(void)char bufinBUFSIZE = empty;char bufout = hello;int bytesin;pid_t childpid;int fd2;if (pipe(fd) = -1) perror(Failed to create the pipe);return 1;n程序中父进程向管道写入一个字符串,子进程读程序中父进程向管道写入一个字符串,子进程读出这个字符串(出这个字符串(parentwritepipe.c)30管道程序示例管道程序示例bytesin = strlen(bufin);childpid = fork();if (childpid =

24、 -1) perror(Failed to fork);return 1;if (childpid) /* parent code */write(fd1, bufout, strlen(bufout)+1);else /* child code */bytesin = read(fd0, bufin, BUFSIZE);fprintf(stderr, %ld:my bufin is %.*s, my bufout is %sn,(long)getpid(), bytesin, bufin, bufout);return 0;31popen和和pclose函数函数#include nFILE

25、*popen(const char *cmdstring, const char * type) ;l返回:若成功则为文件指针,若出错则为返回:若成功则为文件指针,若出错则为N U L Lnint pclose(FILE fp*) ;l返回:返回:cmdstring的终止状态,若出错则为的终止状态,若出错则为- 1n函数函数popen 先执行先执行fork,然后调用,然后调用exec以执行以执行cmdstring,并且返回一个标准,并且返回一个标准I/O文件指针文件指针l如果如果type是是r,则文件指针连接到,则文件指针连接到cmdstring的标准输出。的标准输出。l如果如果type 是是

26、w,则文件指针连接到则文件指针连接到cmdstring 的标准输入的标准输入32popen和和pclose函数函数33命名管道(命名管道(FIFO)nFIFO也被称为命名管道也被称为命名管道n管道只能由相关进程使用,它们共同的祖先进程管道只能由相关进程使用,它们共同的祖先进程创建了管道创建了管道n通过通过FIFO,不相关的进程也能交换数据,不相关的进程也能交换数据n不相关的进程可以通过名字来直接引用不相关的进程可以通过名字来直接引用FIFOnFIFO是一种特殊的文件是一种特殊的文件n创建创建FIFO类似于创建文件,类似于创建文件,FIFO的路径名存在于的路径名存在于文件系统中文件系统中34创建

27、创建FIFOn#include n#include nint mkfifo(const char *pathname, mode_t mode) ;n返回:若成功则为返回:若成功则为0,若出错则为,若出错则为- 1nmode参数的定义与参数的定义与open函数中的函数中的mode相同相同线程同步与进程间通信线程同步与进程间通信n线程同步机制线程同步机制l互斥量互斥量l读写锁读写锁l条件变量条件变量n进程间通信机制进程间通信机制l管道管道lXSI IPC机制机制l信号信号XSI IPC机制机制n信号量信号量(semaphore set),用于管,用于管理对资源的访问。理对资源的访问。n消息队列消

28、息队列(message queue),程,程序之间传递数据的一种简单方法。序之间传递数据的一种简单方法。n共享内存共享内存(shared memory),用,用于在程序之间高效的共享数据于在程序之间高效的共享数据XSI IPC基础基础2024/7/3038key值和值和ID值值 nLinux系统为每个系统为每个IPC机制都分配了唯一的机制都分配了唯一的ID,所有,所有针对该针对该IPC机制的操作都使用对应的机制的操作都使用对应的ID。因此,通信。因此,通信的双方都需要通过某个办法来获取的双方都需要通过某个办法来获取ID值。值。l创建者进程根据创建函数的返回值可获取该值创建者进程根据创建函数的返

29、回值可获取该值l但另一个进程如何实现呢?但另一个进程如何实现呢?显然,显然,UNIX/Linux两个进程不两个进程不能随意访问对方的空间,也就不能够直接获取这一能随意访问对方的空间,也就不能够直接获取这一ID值值 (特例是子进程可以继承父亲进程的数据,实现父亲进程(特例是子进程可以继承父亲进程的数据,实现父亲进程向子进程的单向传递)向子进程的单向传递)2024/7/3039key值和值和ID值值 n为解决为解决IPC机制的机制的ID值不能跨进程传递的问题:值不能跨进程传递的问题:lIPC在实现时约定使用在实现时约定使用key值做为参数创建值做为参数创建IPC对象对象l如果在创建时使用相同的如果

30、在创建时使用相同的key值将得到同一个值将得到同一个IPC对象的对象的ID(即一方创建,另一方获取该对象(即一方创建,另一方获取该对象ID)l保证通信的不同进程方可以使用同一个保证通信的不同进程方可以使用同一个IPC对象对象 2024/7/3040 ftok 2024/7/30人民邮电出版社出版杨宗德编著41示例示例XSI IPC 系统调用系统调用功能功能消息队列消息队列信号量信号量共享内存共享内存分配一个分配一个IPC对象,获得对象,获得对对IPC的访问的访问msggetsemgetshmgetIPC操作操作: 发送发送/接收消息,接收消息,信号量操作,连接信号量操作,连接/释放共释放共享内

31、存享内存msgsnd/msgrcvsemopshmat/shmdtIPC控制:获得控制:获得/修改状态修改状态信息,取消信息,取消IPCmsgctlsemctlshmctl信号量信号量n并发程序设计并发程序设计l互斥和同步互斥和同步lPV操作操作(原语原语) P操作:用于等待操作:用于等待V操作:用于信号操作:用于信号PV操作和信号量操作和信号量procedure p(var s:samephore) s.value=s.value-1; if (s.value0) sleep(s.queue); procedure v(var s:samephore) s.value=s.value+1;

32、if (s.value=0) wakeup(s.queue); 一个理论性的例子一个理论性的例子2024/7/3046信号量通信机制概念图信号量通信机制概念图 Linux/UNIX的信号的信号量集结构量集结构nstruct semid_ds struct ipc_perm sem_perm; struct sem *sem_base; /* 指向信号量集合的指针指向信号量集合的指针 */ time_t sem_otime; /* 最后一次操作的时间最后一次操作的时间 */ time_t sem_ctime; /* 最后一次改变此结构的时间最后一次改变此结构的时间 */ unsigned sho

33、rt sem_nsems; /* 集合中信号量个数集合中信号量个数*/Linux/UNIX的信号的信号量结构量结构nstruct sem pid_t sempid; /* 最后操作该信号量的进程最后操作该信号量的进程ID */ unsigned short semncnt ; /* 等待对该信号量执行等待对该信号量执行P 操作的进程数操作的进程数 */ unsigned short semzcnt; /* 等待等待semval为为0的进程数的进程数 */ unsigned short semval; /* 信号量当前值信号量当前值 */信号量系统调用信号量系统调用#include #inclu

34、de #include int semget(key_t key, int nsems, int semflg);int semop(int semid, struct sembuf *sops, unsigned nsops);int semctl(int semid, int semnum, int cmd, .);semget函数函数 功能:创建一个新信号集或取得一个已有信号量集的功能:创建一个新信号集或取得一个已有信号量集的IDint semget(key_t key, int nsems, int semflg);n“key” 参数参数l预定义常数预定义常数IPC_PRIVATE;l

35、约定的关键字约定的关键字lftok函数函数n“nsems”l指定需要的信号量数目,为了使用方便可以取值为指定需要的信号量数目,为了使用方便可以取值为1。n“semflg” 参数参数l设置访问权限设置访问权限(低低9位,常用位,常用8进制数表示进制数表示),还可以附加以下参,还可以附加以下参数值(按位或)数值(按位或)IPC_CREAT(如果(如果key不存在,则创建)不存在,则创建)IPC_EXCL(如果(如果key存在则返回失败)存在则返回失败)IPC_NOWAIT(如果需要等待,则直接返回错误)(如果需要等待,则直接返回错误)#includekey_tftok(constchar*path

36、,intid);semget函函数示例数示例 sem_id = semget(key_t)1234, 1, 0666 | IPC_CREAT);semop函数函数功能:改变信号量的值功能:改变信号量的值n函数原型为函数原型为int semop(int semid, struct sembuf *sops, size_t nsops);nsemid,指定该信号量集的,指定该信号量集的ID(一般为(一般为semget的返回值)的返回值)nsops 参数参数struct sembuf unsigned short sem_num; (信号量编号(信号量编号 ) short sem_op;(信号量操作

37、,该值为正则表示增加信号量(信号量操作,该值为正则表示增加信号量的值,如果为的值,如果为1,则在原来基础上加,则在原来基础上加1;如果为负则表示减小;如果为负则表示减小信号量的值;如果为信号量的值;如果为0表示对信号量的当前值是否为表示对信号量的当前值是否为0的测试)的测试) short sem_flg;(操作标志(操作标志 )semop函数(续)函数(续)nsem_flg用于对操作进行适当的控制,主要用于对操作进行适当的控制,主要有有2个控制标志。个控制标志。lIPC_NOWAIT 当指定的操作不能完成时,进程不等待立即返回,返回值当指定的操作不能完成时,进程不等待立即返回,返回值为为-1,

38、errno置为置为EAGAIN。lSEM_UNDO(建议)(建议) 进程退出时,执行信号量解除(进程退出时,执行信号量解除(undo)操作。)操作。nnsops参数参数结构数组的元素个数结构数组的元素个数semop函数(续)函数(续)int semaphore_p(void) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = -1; /* P() */ sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore_p fai

39、ledn); return(0); return(1);semop函数(续)函数(续)static int semaphore_v(void) struct sembuf sem_b; sem_b.sem_num = 0; sem_b.sem_op = 1; /* V() */ sem_b.sem_flg = SEM_UNDO; if (semop(sem_id, &sem_b, 1) = -1) fprintf(stderr, semaphore_v failedn); return(0); return(1);semctl函数函数功能:直接控制信号量信功能:直接控制信号量信功能:直接控制信

40、号量信功能:直接控制信号量信息息息息int semctl(int semid, int semnum, int cmd,union semun arg);nsemid,指定该信号量集的,指定该信号量集的ID(一般为(一般为semget的返回的返回值)值)nsemnum,为信号量集中信号量的编号(如果表示某个,为信号量集中信号量的编号(如果表示某个信号量,此值为信号量,此值为信号量下标信号量下标;如果;如果标识整个信号量集,标识整个信号量集,此值为此值为0)ncmd,要执行的操作,如果是对整个信号量集的操作,要执行的操作,如果是对整个信号量集的操作,则在则在/usr/include/linux/

41、ipc.h中定义,具体操作:中定义,具体操作:nIPC_RMID,删除,删除nIPC_SET,设置,设置ipc_perm参数参数nIPC_STAT,获取,获取ipc_perm参数参数nIPC_INFO,获取系统信息,获取系统信息2024/7/3057semctl函数函数semctl函数函数narg参数参数union semun int val; struct semid_ds *buf; unsigned short *arry; ;nSemctl函数的函数的command参数通常使用下面两个值:参数通常使用下面两个值:lSETVAL:用来把信号量初始化为一个已知的值。:用来把信号量初始化为一

42、个已知的值。 这个值通这个值通过过union semun中的中的val成员设置,其作用是在信号量第一次成员设置,其作用是在信号量第一次使用前对它进行设置。使用前对它进行设置。lIPC_RMID:用于删除一个已经无需继续使用的信号量标识符:用于删除一个已经无需继续使用的信号量标识符2024/7/3059参考代码参考代码2024/7/3060参考代码参考代码2024/7/3061参考代码参考代码2024/7/3062消息队列消息队列2024/7/3063消息队消息队列结构列结构2024/7/3064消息结构消息结构2024/7/3065创建消息队列创建消息队列 2024/7/3066消息队列属性控

43、制消息队列属性控制 2024/7/3067发送信息到消息队列发送信息到消息队列 2024/7/3068发送信息到消息队列(发送信息到消息队列(2)2024/7/3069从消息队列接收信息从消息队列接收信息 2024/7/3070消消息队息队列参考代码:发送者列参考代码:发送者structmsg_stlongintmsg_type;chartextMAX_TEXT;intmain()intrunning=1;structmsg_stdata;charbufferBUFSIZ;intmsgid=-1;/建立消息队列msgid=msgget(key_t)1234,0666|IPC_CREAT);if

44、(msgid=-1)fprintf(stderr,msggetfailedwitherror:%dn,errno);exit(EXIT_FAILURE);/向消息队列中写消息,直到写入endwhile(running)/输入数据printf(Entersometext:);fgets(buffer,BUFSIZ,stdin);data.msg_type=1;strcpy(data.text,buffer);/向队列发送数据if(msgsnd(msgid,(void*)&data,MAX_TEXT,0)=-1)fprintf(stderr,msgsndfailedn);exit(EXIT_FAI

45、LURE);/输入end结束输入if(strncmp(buffer,end,3)=0)running=0;sleep(1);exit(EXIT_SUCCESS);2024/7/3071消消息队息队列参考代码:接收者列参考代码:接收者structmsg_stlongintmsg_type;chartextBUFSIZ;intmain()intrunning=1;intmsgid=-1;structmsg_stdata;longintmsgtype=0;/建立消息队列msgid=msgget(key_t)1234,0666|IPC_CREAT);if(msgid=-1)fprintf(stderr

46、,msggetfailedwitherror:%dn,errno);exit(EXIT_FAILURE);/从队列中获取消息,直到遇到end消息为止while(running)if(msgrcv(msgid,(void*)&data,BUFSIZ,msgtype,0)=-1)fprintf(stderr,msgrcvfailedwitherrno:%dn,errno);exit(EXIT_FAILURE);printf(Youwrote:%sn,data.text);/遇到end结束if(strncmp(data.text,end,3)=0)running=0;/删除消息队列if(msgctl

47、(msgid,IPC_RMID,0)=-1)fprintf(stderr,msgctl(IPC_RMID)failedn);exit(EXIT_FAILURE);exit(EXIT_SUCCESS);共享内存共享内存n共享内存共享内存l共享内存是内核为进程创建的一个特殊内存段,它将共享内存是内核为进程创建的一个特殊内存段,它将出现在自己的地址空间中,其它进程可以将同一段共出现在自己的地址空间中,其它进程可以将同一段共享内存连接享内存连接(attach)到自己的地址空间到自己的地址空间l最快的进程间通信方式最快的进程间通信方式l不提供任何同步功能不提供任何同步功能共享内存示意图共享内存示意图进程

48、进程A的的逻辑地址空间逻辑地址空间进程进程B的的逻辑地址空间逻辑地址空间物理内存物理内存共享存储共享存储共享资源,需要考虑进程间的同步!共享资源,需要考虑进程间的同步!2024/7/3074共享内存与管道对比共享内存与管道对比 2024/7/3075共享内共享内存结构存结构共享内存系统调用共享内存系统调用#include #include #include int semget(key_t key, int size, int flag);void *shmat(int shmid, void *addr, int flag);int shmdt(void *addr);int shmctl(

49、int shmid, int cmd, struct shmid_ds *buf);shmget函数函数#include #include #include int semget(key_t key, size_t size, int shmflag);成功返回一个共享内存标识符,失败返回成功返回一个共享内存标识符,失败返回成功返回一个共享内存标识符,失败返回成功返回一个共享内存标识符,失败返回1 1第一个参数第一个参数key为共享内存段命为共享内存段命名(一般由名(一般由ftok产生)产生)第二个参数第二个参数size为需要共享的内存容量。(如为需要共享的内存容量。(如果共享内存已果共享内存

50、已存存在在时,不能不时,不能不大于该共享内存段的大小)大于该共享内存段的大小)第三个参数设置访问权限第三个参数设置访问权限(低低9位位)与)与IPC_CREAT, IPC_EXCL 的按的按位或。位或。功能:获得或创建一个共享内存标识符。功能:获得或创建一个共享内存标识符。shmget函数(续)函数(续)shmid = shmget(key_t)1234, sizeof(struct shared_use_st),0666 | IPC_CREAT);shmat函数函数#include #include #include void *shmat(int shm_id, const void *a

51、ddr, int shmflg) ;成功返回共享存储段连接的实际地址,失败返回成功返回共享存储段连接的实际地址,失败返回-1n第一个参数第一个参数shm_id为为shmget返回的共享内存标识符。返回的共享内存标识符。n第二个参数第二个参数addr指明共享内存段要连接到的地址(进程空间内部指明共享内存段要连接到的地址(进程空间内部地址),通常指定为空指针,表示让系统来选择共享内存出现的地地址),通常指定为空指针,表示让系统来选择共享内存出现的地址。址。n第三个参数第三个参数shmflg可以设置为两个标志位(通常设置为可以设置为两个标志位(通常设置为0)SHM_RND(与(与shm_addr联合

52、使用,控制内存连接的地址)联合使用,控制内存连接的地址)SHM_RDONLY,要连接的共享内存段是只读的。要连接的共享内存段是只读的。功能:将共享内存段连接到一个进程的地址空间中。功能:将共享内存段连接到一个进程的地址空间中。shmdt函数函数#include #include #include int shmdt(const void *shmaddr) ;其中其中shmaddr为为shmat返回的地址。返回的地址。功能:将共享内存从当前进程中分离。功能:将共享内存从当前进程中分离。shmctl函数函数int shmctl(int shm_id, int command, struct sh

53、mid_ds *buf) ;成功返回成功返回0,失败返回,失败返回-1功能:查看及修改共享内存段的功能:查看及修改共享内存段的shmid_dsshmid_ds结构,删除该结构以及结构,删除该结构以及相连的共享存储段标识。相连的共享存储段标识。第二个参数第二个参数commad取值:取值:IPC_STAT buf指向当前共享内存段的指向当前共享内存段的shmid_ds结构结构 IPC_SET 把共享内存段的当前关联值设置为把共享内存段的当前关联值设置为shmid_ds结构结构 给出的值给出的值IPC_RMID 从系统中删除该共享存储段。从系统中删除该共享存储段。shmctl函函数续数续n第三个参数

54、第三个参数buf是一个结构指针,它指向共享内存是一个结构指针,它指向共享内存模式和访问权限的结构模式和访问权限的结构struct shmid_ds uid_t shm_perm.uid; uid_t shm_perm.gid; mode_t shm_perm.mode; ;83共享内存参考代码:读取共享内存共享内存参考代码:读取共享内存structshared_use_stintwritten;/非0:表示可读,0表示可写chartextTEXT_SZ;/记录写入和读取的文本;intmain()intrunning=1;/程序是否继续运行的标志void*shm=NULL;/分配的共享内存的原始

55、首地址structshared_use_st*shared;/指向shmintshmid;/共享内存标识符/创建共享内存shmid=shmget(key_t)1234,sizeof(structshared_use_st),0666|IPC_CREAT);if(shmid=-1)fprintf(stderr,shmgetfailedn);exit(EXIT_FAILURE);/将共享内存连接到当前进程的地址空间shm=shmat(shmid,0,0);if(shm=(void*)-1)fprintf(stderr,shmatfailedn);exit(EXIT_FAILURE);printf(

56、nMemoryattachedat%Xn,(int)shm);/设置共享内存shared=(structshared_use_st*)shm;shared-written=0;while(running)/读取共享内存中的数据/判断是否有数据可读取if(shared-written!=0)printf(Youwrote:%s,shared-text);sleep(rand()%3);/读取完数据,设置written使共享内存段可写shared-written=0;/输入了end,退出循环(程序)if(strncmp(shared-text,end,3)=0)running=0;else/有其他

57、进程在写数据,不能读取数据sleep(1);/把共享内存从当前进程中分离if(shmdt(shm)=-1)fprintf(stderr,shmdtfailedn);exit(EXIT_FAILURE);/删除共享内存if(shmctl(shmid,IPC_RMID,0)=-1)fprintf(stderr,shmctl(IPC_RMID)failedn);exit(EXIT_FAILURE);exit(EXIT_SUCCESS);84共享内存参考代码:写共享内存共享内存参考代码:写共享内存intmain()intrunning=1;void*shm=NULL;structshared_use_

58、st*shared=NULL;charbufferBUFSIZ+1;/用于保存输入的文本intshmid;/创建共享内存shmid=shmget(key_t)1234,sizeof(structshared_use_st),0666|IPC_CREAT);if(shmid=-1)fprintf(stderr,shmgetfailedn);exit(EXIT_FAILURE);/将共享内存连接到当前进程的地址空间shm=shmat(shmid,(void*)0,0);if(shm=(void*)-1)fprintf(stderr,shmatfailedn);exit(EXIT_FAILURE);

59、printf(Memoryattachedat%Xn,(int)shm);/设置共享内存shared=(structshared_use_st*)shm;while(running)/向共享内存中写数据/数据还没有被读取,则等待数据被读取,不能向共享内存中写入文本while(shared-written=1)sleep(1);printf(Waiting.n);/向共享内存中写入数据printf(Entersometext:);fgets(buffer,BUFSIZ,stdin);strncpy(shared-text,buffer,TEXT_SZ);/写完数据,设置written使共享内存段

60、可读shared-written=1;/输入了end,退出循环(程序)if(strncmp(buffer,end,3)=0)running=0;/把共享内存从当前进程中分离if(shmdt(shm)=-1)fprintf(stderr,shmdtfailedn);exit(EXIT_FAILURE);sleep(2);exit(EXIT_SUCCESS);线程同步与进程间通信线程同步与进程间通信n线程同步机制线程同步机制l互斥量互斥量l读写锁读写锁l条件变量条件变量n进程间通信机制进程间通信机制l管道管道lXSI IPC机制机制l信号信号信号信号n信号的概念信号的概念nsignal函数函数n不

61、可靠信号与可靠信号不可靠信号与可靠信号n信号的发送接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongjmp函数函数nabort函数函数87信号的概念信号的概念n基本概念基本概念n信号的产生信号的产生n信号的处理方式信号的处理方式n常见信号常见信号n信号与文字说明的转换信号与文字说明的转换88信号的概念信号的概念n当某个程序长时间运行,如何中断它的执行,让当某个程序长时间运行,如何中断它的执行,让其退出?其退出?nCtrl+c中断程序执行中断程序执行l是如何中断程序执行的呢?是如何中断程序执行的呢?89信号的概念信号的概念n信号是软件中断。

62、信号是软件中断。n例如,例如,Ctrl + C,将产生,将产生SIGINT信号,中断程序信号,中断程序执行执行n它可以作为进程间通信的一种方式,但更主要的它可以作为进程间通信的一种方式,但更主要的是,信号总是中断一个进程的正常运行,它更多是,信号总是中断一个进程的正常运行,它更多地被用于处理一些非正常情况地被用于处理一些非正常情况n每个信号都有一个名字,以每个信号都有一个名字,以SIG开头。开头。lSIGABRT:进程异常终止信号,:进程异常终止信号,abort产生产生lSIGALRM:闹钟信号,计时器超时后产生:闹钟信号,计时器超时后产生lLinux 2.4.22共有共有31种不同的信号种不

63、同的信号l查看头文件查看头文件/usr/include/bits/signum.h90信号的产生信号的产生很多条件可以很多条件可以产生产生产生产生一个信号一个信号n当用户按某些终端键时,产生信号。例如在终端上按当用户按某些终端键时,产生信号。例如在终端上按Ctrl+CCtrl+C键通常产生中断信号键通常产生中断信号(SIGINT)(SIGINT)。n硬件异常产生信号:除数为硬件异常产生信号:除数为0 0、无效的存储访问等等。这些、无效的存储访问等等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生适当的信号。

64、件发生时正在运行的进程产生适当的信号。n进程用进程用KillKill函数可将信号发生给另一个进程或进程组;函数可将信号发生给另一个进程或进程组;n用户用用户用KillKill命令将信号(终止信号命令将信号(终止信号SIGTERMSIGTERM)发送给其他进)发送给其他进程程n当检测到某种软件条件已经发生,并将其通知有关进程时产当检测到某种软件条件已经发生,并将其通知有关进程时产生信号。如生信号。如SIGPIPESIGPIPE、SIGALRMSIGALRMn等等等等91进程对信号的处理方式进程对信号的处理方式n进程对信号的进程对信号的处理方式处理方式处理方式处理方式可以有三种:可以有三种:l l

65、忽略此信号忽略此信号忽略此信号忽略此信号。大多数信号都可使用这种方式进行处理,。大多数信号都可使用这种方式进行处理,但有两种信号却不能被忽略。它们是:但有两种信号却不能被忽略。它们是:SIGKILL和和SIGSTOP。这两种信号不能被忽略的原因时:它们向。这两种信号不能被忽略的原因时:它们向超级用户提供了一种使进程终止或停止的可靠方法;超级用户提供了一种使进程终止或停止的可靠方法;l l捕捉信号捕捉信号捕捉信号捕捉信号。为了做到这一点要通知内核在某种信号发。为了做到这一点要通知内核在某种信号发生时,调用一个用户函数。在用户函数中,可执行用生时,调用一个用户函数。在用户函数中,可执行用户希望对这

66、种事件进行的处理。户希望对这种事件进行的处理。l l执行系统默认动作执行系统默认动作执行系统默认动作执行系统默认动作。对大多数信号的系统默认动作是。对大多数信号的系统默认动作是终止该进程。终止该进程。92常见的信号常见的信号n nSIGABRT SIGABRT 处理异常信号处理异常信号处理异常信号处理异常信号。当前进程调用。当前进程调用abort后发送该后发送该信号;信号;n nSIGALRM SIGALRM 报时时钟报时时钟报时时钟报时时钟。当进程设置的时钟超时后,内核向。当进程设置的时钟超时后,内核向进程发送一个时钟信号;进程发送一个时钟信号;n nSIGCHLD SIGCHLD 子进程被

67、终止或停止子进程被终止或停止子进程被终止或停止子进程被终止或停止。每当子进程被终止或停。每当子进程被终止或停止时,内核都会给父进程发送一个信号止时,内核都会给父进程发送一个信号SIGCHLD,该信,该信号表示的是一个子进程的消亡。号表示的是一个子进程的消亡。n nSIGHUP SIGHUP 挂起信号挂起信号挂起信号挂起信号。当终端连接断开时,内核向所有依。当终端连接断开时,内核向所有依附在此控制终端上的进程发送此信号;附在此控制终端上的进程发送此信号;n nSIGINT SIGINT 中断信号中断信号中断信号中断信号。当用户按下中断键时,从内核向所有。当用户按下中断键时,从内核向所有与终端会话

68、有联系的进程发出信号。与终端会话有联系的进程发出信号。93常见的信号(续)常见的信号(续)n nSIGPIPE SIGPIPE 写无写无写无写无接收方的管道或套接字信号接收方的管道或套接字信号接收方的管道或套接字信号接收方的管道或套接字信号。管道和套接。管道和套接字都是一种进程间通讯机制;字都是一种进程间通讯机制;n nSIGQUIT SIGQUIT 退出信号退出信号退出信号退出信号。与。与SIGINT信号非常类似,当用户在信号非常类似,当用户在终端上敲下退出键时,内核会发出这个信号。与终端上敲下退出键时,内核会发出这个信号。与SIGINT不同点在于,该信号会导致进程的异常终止。不同点在于,该

69、信号会导致进程的异常终止。n nSIGTTIN/SIGTTOU SIGTTIN/SIGTTOU 后台进程读后台进程读后台进程读后台进程读/ /写信号写信号写信号写信号。每当一个后台进。每当一个后台进程试图从控制终端读入程试图从控制终端读入/写数据时就会产生此信号,此信号写数据时就会产生此信号,此信号的默认动作是终止进程的运行。的默认动作是终止进程的运行。n nSIGURG SIGURG 高带宽数据通知信号高带宽数据通知信号高带宽数据通知信号高带宽数据通知信号。该信号通知进程,网络。该信号通知进程,网络连接中出现了紧急情况或者带外数据;连接中出现了紧急情况或者带外数据;n nSIGUSR1SIG

70、USR1和和和和SIGUSR2 SIGUSR2 用户自定义信号用户自定义信号用户自定义信号用户自定义信号。94信号值与文字说明的转换信号值与文字说明的转换nLinux提供了一个数组提供了一个数组extern char* sys_siglist;n数组的下标是信号的编号,给出的字符串指针指数组的下标是信号的编号,给出的字符串指针指向相应信号的说明文字向相应信号的说明文字95psignal函数函数n该函数类似于该函数类似于perror,根据信号值,输出相关的,根据信号值,输出相关的信息信息n函数原型函数原型#includevoid psignal(int signo, const char* ms

71、g);n该函数将字符串该函数将字符串msg输出,后面接一个冒号和一输出,后面接一个冒号和一个空格,再接着对该信号的说明,最后一个换行个空格,再接着对该信号的说明,最后一个换行符符96strsignal函数函数n该函数类似于该函数类似于strerror函数,将信号值翻译成可读函数,将信号值翻译成可读的字符串的字符串n函数原型函数原型#includechar* strsignal(int signo);n参数:参数:signo,即信号编号,即信号编号n返回值:返回值:signo对应的说明文字对应的说明文字n在在Solaris 9中,若信号编号无效,则返回中,若信号编号无效,则返回NULL,在,在F

72、reeBSD、Linux中,则返回字符串,说明中,则返回字符串,说明信号不可识别信号不可识别信号信号n信号的概念信号的概念nsignal函数函数n不可靠信号与可靠信号不可靠信号与可靠信号n信号的发送接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongjmp函数函数nabort函数函数98signal函数函数n用于设置信号的处理方法用于设置信号的处理方法n函数原型函数原型lvoid (*signal(int signo, void (*func)(int)(int);n返回类型与参数返回类型与参数l返回类型:返回类型:void (*)(int

73、)l第一个参数第一个参数signo:信号名:信号名l第二个参数第二个参数func:类型:类型void (*)(int)99signal函数函数n说明:说明:typedef void (*Sigfunc)(int);Sigfunc *signal(int, Sigfunc *);n第二个参数第二个参数funclSIG_IGN:内核忽略该信号(:内核忽略该信号(SIGKILL、SIGSTOP不不能忽略)能忽略)lSIG_DFL:接收到信号:接收到信号signo后,按照系统默认动作后,按照系统默认动作处理处理l用户自己提供的信号处理函数。当收到信号时,进程用户自己提供的信号处理函数。当收到信号时,进

74、程立即转移去执行信号处理函数立即转移去执行信号处理函数n返回值:返回值:l成功,返回信号以前的处理方式成功,返回信号以前的处理方式l出错,返回出错,返回SIG_ERR#define SIG_ERR (void (*)()-1#define SIG_DFL (void (*)() 0#define SIG_IGN (void (*)() 1 100signal函函数使用示例数使用示例n忽略忽略SIGINT信号信号n忽略忽略SIGSTOP信号信号n忽略忽略SIGKILL信号信号101signal函函数使用示例数使用示例n捕获捕获SIGINT信号信号n捕获捕获SIGSTOP信号信号n捕获捕获SIGK

75、ILL信号信号102signal函数函数n获取当前信号处理方式获取当前信号处理方式SigFunc *sig_int;sig_int = signal(SIGINT, SIG_IGN);signal(SIGINT, sig_int);n必须两次调用必须两次调用signal(两次改变信号处理方式),(两次改变信号处理方式),才能获取当前信号处理方式才能获取当前信号处理方式n当改变信号处理方式之间,有信号达到,该如何当改变信号处理方式之间,有信号达到,该如何?103signal函数存在的问题不可靠函数存在的问题不可靠n在早期的在早期的UNIX系统中(如系统中(如SVR4中),进程每次处理信号中),进

76、程每次处理信号时,随即将信号动作复位为默认动作。因此需要重复安装时,随即将信号动作复位为默认动作。因此需要重复安装信号函数,例如:信号函数,例如: voidsig_int( int signo ); signal( SIGINT, sig_int ); void sig_int( int signo ) signal( SIGINT, sig_int );104signal函数存在的问题(续)函数存在的问题(续)n上述解决方法存在的问题是:上述解决方法存在的问题是:l在信号发生之后到信号处理程序中调用在信号发生之后到信号处理程序中调用signal函数之函数之间有一个间有一个时间窗口时间窗口时间

77、窗口时间窗口。在此期间,可能发生另一次信号,。在此期间,可能发生另一次信号,而第二个信号有可能造成执行默认动作。(而第二个信号有可能造成执行默认动作。(信号丢失,信号丢失,信号丢失,信号丢失,即不可靠即不可靠即不可靠即不可靠)n进程所能做的仅仅是捕捉或忽略信号,而不能让进程所能做的仅仅是捕捉或忽略信号,而不能让内核阻塞信号内核阻塞信号l不要忽略信号不要忽略信号l但是在发生信号时,记住它,在进程做好准备的时候但是在发生信号时,记住它,在进程做好准备的时候传递它传递它105第二个问题的典型例子第二个问题的典型例子int sig_int_flag;int main(void) void sig_in

78、t(); . signal(SIGINT,sig_int); . while(sig_int_flag = 0) pause(); .void sig_int() signal(SIGINT,sig_int); sig_int_flag = 1;问题:有可能在while检测变量之后,而在pause函数之前发生SIGINT信号,而且信号只发生此一次,由此,该程序将一直睡眠。这种情况导致程序在大多数时间能正确运行。假设在此处发假设在此处发假设在此处发假设在此处发生信号?生信号?生信号?生信号?信号信号n信号的概念信号的概念nsignal函数函数n不可靠信号与可靠信号不可靠信号与可靠信号n信号的发送

79、接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongjmp函数函数nabort函数函数107不可靠信号与可靠信号不可靠信号与可靠信号n不可靠信号不可靠信号:建立在早期机制上的信号。这些信号建立在早期机制上的信号。这些信号的值从的值从1到到31,已有预定义含义,如,已有预定义含义,如SIGINTl进程每次处理信号后进程每次处理信号后,系统就将对信号的响应设置为默系统就将对信号的响应设置为默认动作认动作(需两次调用需两次调用signal)linux已解决已解决l信号可能丢失信号可能丢失l不可靠信号不支持排队不可靠信号不支持排队l在信号处理函数执

80、行过程中,到来的所有相同信号,在信号处理函数执行过程中,到来的所有相同信号,都被合并为一个信号都被合并为一个信号n可靠信号可靠信号:后期引入的,其信号值从后期引入的,其信号值从34到到64l可靠信号支持排队,不会丢失可靠信号支持排队,不会丢失l可靠信号也被称为实时信号可靠信号也被称为实时信号l不可靠信号被称为非实时信号不可靠信号被称为非实时信号108信号的排队信号的排队n当正在处理可靠信号当正在处理可靠信号A时,若收到另一可靠信号时,若收到另一可靠信号B,则中断信号,则中断信号A的执行,转去执行信号的执行,转去执行信号B的处理;的处理;n若此时又收到信号若此时又收到信号B,则该信号在,则该信号

81、在B队列排队;若队列排队;若此时又收到信号此时又收到信号A,则该信号在,则该信号在A队列排队队列排队n若信号若信号B处理完毕,则检查处理完毕,则检查B队列中有无信号尚需队列中有无信号尚需处理,处理完毕后,再返回中断点执行处理,处理完毕后,再返回中断点执行109可靠信号与不可靠信号可靠信号与不可靠信号n早期的信号安装和发送机制早期的信号安装和发送机制lsignallkilln改进后的信号安装和发送机制改进后的信号安装和发送机制lsigactionlsigqueuen信号可靠与否,只与信号值有关,与采用何种信信号可靠与否,只与信号值有关,与采用何种信号安装、发送函数无关号安装、发送函数无关信号信号

82、n信号的概念信号的概念nsignal函数函数n不可靠信号与可靠信号不可靠信号与可靠信号n信号的发送接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongjmp函数函数nabort函数函数111信号的发送接收机制信号的发送接收机制nkill函数、函数、raise函数函数nalarm函数、函数、pause函数函数nsleep函数及实现函数及实现n具有超时功能的具有超时功能的API112信号的发送接收机制信号的发送接收机制nkill函数、函数、raise函数函数nalarm函数、函数、pause函数函数nsleep函数及实现函数及实现n具有超时功能

83、的具有超时功能的API113kill函数函数n该函数将信号发送给其他进程或者进程组该函数将信号发送给其他进程或者进程组n函数原型函数原型#includeint kill(pid_t pid, int signo);n参数与返回值参数与返回值lsigno:需要发送的信号编号:需要发送的信号编号lpid:发送的目的地:发送的目的地l成功返回成功返回0,出错返回,出错返回-1114kill函数函数npid的取值的取值lpid 0:将信号发送给进程:将信号发送给进程ID为为pid的进程的进程lpid = 0:将信号发送给起进程组:将信号发送给起进程组ID等于发送进程的进等于发送进程的进程组程组ID,而

84、且发送进程有许可权向其发送信号的所有,而且发送进程有许可权向其发送信号的所有进程进程lpid 0 :将信号发送给其进程组:将信号发送给其进程组ID等于等于pid绝对值,绝对值,而且发送进程有许可权向其发送信号的所有进程而且发送进程有许可权向其发送信号的所有进程lpid = -1 将信号发送给除进程将信号发送给除进程0以外的所有进程,但发以外的所有进程,但发送进程必须有许可权送进程必须有许可权115raise函数函数n该函数将信号发送给自己该函数将信号发送给自己n函数原型函数原型#includeint raise(int signo);n参数与返回值参数与返回值lsigno:需要发送的信号编号:

85、需要发送的信号编号l成功返回成功返回0,出错返回,出错返回-1116发送信号的权限发送信号的权限n有发送信号许可权的有发送信号许可权的基本规则基本规则基本规则基本规则是是:l1)超级用户可以将信号发送给任意进)超级用户可以将信号发送给任意进程程l2)发送者的实际或有效用户)发送者的实际或有效用户ID,必须等于接收者实际,必须等于接收者实际或有效用户或有效用户IDn当当signo为为0时,则时,则kill仍执行正常的错误检测,但仍执行正常的错误检测,但不发送信号。这常被用来确定一个特定进程是否不发送信号。这常被用来确定一个特定进程是否存在。如果向一个并不存在的进程发送空信号,存在。如果向一个并不

86、存在的进程发送空信号,则则kill返回返回-1,errno则被设置为则被设置为ESRCH。117alarm函数函数n注意:每个进程只有一个计时器注意:每个进程只有一个计时器n若在调用若在调用alarm时,以前已为该进程设置的计时器时,以前已为该进程设置的计时器并未超时,则将该计时器的余留值作为本次并未超时,则将该计时器的余留值作为本次alarm调用的值返回,以前登记的计时器,则被新调用的值返回,以前登记的计时器,则被新计时器替代计时器替代n取消定时器取消定时器l若有以前为进城登记的尚未超时的计时器,而且本次若有以前为进城登记的尚未超时的计时器,而且本次调用调用seconds等于等于0,则取消计

87、时器,则取消计时器118alarm函数函数n该函数用于设置一个计时器,在将来某个指定的该函数用于设置一个计时器,在将来某个指定的时间,该计时器将超时,产生时间,该计时器将超时,产生SIGALRM信号信号n函数原型函数原型#includeunsigned int alarm(unsigned int seconds);n参数与返回值参数与返回值lseconds:经过:经过seconds秒后,产生秒后,产生SIGALRMl返回返回0或以前设置的计时器时间的余留秒数或以前设置的计时器时间的余留秒数119pause函数函数n该函数使调用进程挂起,直到捕捉到一个信号该函数使调用进程挂起,直到捕捉到一个信

88、号n函数原型函数原型#includeint pause();n返回值返回值l只有执行了一个信号处理程序并从其返回时,只有执行了一个信号处理程序并从其返回时,pause才才返回返回l返回返回-1,errno被设置为被设置为EINTR120sleep函数函数n该函数用于让调用进程挂起,直到该函数用于让调用进程挂起,直到l已经过了指定的时间已经过了指定的时间l或者调用进程捕捉到一个信号,并从信号处理程序返回或者调用进程捕捉到一个信号,并从信号处理程序返回n函数原型函数原型#includeunsigned int sleep(unsigned int seconds);n返回值:返回值:l若已经过了指

89、定的时间,则返回若已经过了指定的时间,则返回0l若调用进程捕捉到一个信号,并从信号处理程序返回,若调用进程捕捉到一个信号,并从信号处理程序返回,则则sleep提前返回,返回值是未睡够的秒数提前返回,返回值是未睡够的秒数121sleep的实现方式一的实现方式一static void sig_alrm(int signo) return; /nothing to do, just return to wake up the pauseunsigned int sleep1(unsigned int nsecs) if (signal(SIGALRM, sig_alrm) = SIG_ERR) re

90、turn(nsecs); alarm(nsecs); /* start the timer */ pause(); /next caught signal wakes us up return( alarm(0) ); /turn off timer, return unslept time There is a race There is a race conditioncondition122sleep1函数函数n上述实现存在如下问题:上述实现存在如下问题:l如果调用者此前曾设置了计时器,则它被如果调用者此前曾设置了计时器,则它被sleep1函数函数中的第一次中的第一次alarm调用擦去。

91、(调用擦去。(考虑解决方法考虑解决方法考虑解决方法考虑解决方法)l该程序中修改了对该程序中修改了对SIGALRM的配置。如果编写一个函的配置。如果编写一个函数供其他函数调用,则在该函数调用时要保存原配置,数供其他函数调用,则在该函数调用时要保存原配置,并在返回前恢复原配置;并在返回前恢复原配置;l在调用在调用alarm和和pause之间有一个竞争条件。(之间有一个竞争条件。(思考在思考在思考在思考在什么情况下会发生?结果如何?什么情况下会发生?结果如何?什么情况下会发生?结果如何?什么情况下会发生?结果如何?)123sleep1函数函数n对于问题对于问题1l检查第一次调用检查第一次调用alar

92、m的返回值,若其小于本次调用的返回值,若其小于本次调用alarm的参数值,则只应等到上次设置的计时器超时;的参数值,则只应等到上次设置的计时器超时;l若上次设置的计时器超时时间晚于本次设置值,则在若上次设置的计时器超时时间晚于本次设置值,则在sleep1函数返回前,复位此计时器,使其在上次计时函数返回前,复位此计时器,使其在上次计时器的设定时间再次发生超时器的设定时间再次发生超时124sleep1函数函数n对于问题对于问题2l保存保存signal函数的返回值,在返回前复位原配置函数的返回值,在返回前复位原配置n对于问题对于问题3l可以使用可以使用setjmp,或者,或者lsigprocmask

93、、sigsuspendnsleep的早期实现,更正了问题的早期实现,更正了问题1和问题和问题2125sleep2函数函数static jmp_buf env_alrm;static void sig_alrm(int signo) longjmp(env_alrm, 1);unsigned int sleep2(unsigned int nsecs) if (signal(SIGALRM, sig_alrm) = SIG_ERR) return(nsecs); if (setjmp(env_alrm) = 0)(setjmp(env_alrm) = 0) alarm(nsecs); /* st

94、art the timer */ pause(); /* next caught signal wakes us up */ return( alarm(0) ); /* turn off timer, return unslept time */126sleep2函数函数n这种实现解决了竞争条件,这种实现解决了竞争条件,l即使即使pause从未执行,在发生从未执行,在发生SIGALRM时,时,sleep2也也会返回会返回n但却带来另一个难于察觉的问题:但却带来另一个难于察觉的问题:l它涉及到与其他信号的相互作用。如果它涉及到与其他信号的相互作用。如果SIGALRM中断中断了某个其他信号处理程

95、序,则调用了某个其他信号处理程序,则调用longjmp会提早终止会提早终止该信号处理程序该信号处理程序l下页下页PPT的例子的例子127unsigned int sleep2(unsigned unsigned int sleep2(unsigned int);int);static void sig_int(int);static void sig_int(int);int main(void)int main(void) unsigned int unslept; unsigned int unslept; if (signal(SIGINT, sig_int) = if (signal(

96、SIGINT, sig_int) = SIG_ERR)SIG_ERR) err_sys(signal(SIGINT) err_sys(signal(SIGINT) error);error); unslept = sleep2(5); unslept = sleep2(5); printf(sleep2 returned: printf(sleep2 returned: %un, unslept);%un, unslept); exit(0); exit(0); static void sig_int(int static void sig_int(int signo)signo) int i

97、; int i; volatile int j; volatile int j; printf(nsig_int printf(nsig_int startingn);startingn); for (i = 0; i 2000000; for (i = 0; i 2000000; i+)i+) j += i * i; j += i * i; printf(sig_int printf(sig_int finishedn);finishedn); return; return; ExampleExample useuse ofof sleep2sleep2128sleep2函数函数n上述程序运

98、行后,立即键入上述程序运行后,立即键入Ctrl+cn程序输出程序输出Sig_int strartingSleep2 returned: 0 n问题问题lSIGINT的处理并未结束的处理并未结束129sleep函数的实现函数的实现n方式一:方式一:lSolaris 9使用使用alarm实现实现sleep函数函数n方式二:方式二:lFreeBSD、Linux使用使用nanosleep提供时间延迟提供时间延迟130sleep的实现方式二的实现方式二nLinux中并未提供系统调用中并未提供系统调用sleep,sleep是在库是在库函数中实现的函数中实现的nnanosleep则是则是Linux的系统调用

99、,提供高分辨率的系统调用,提供高分辨率的时间延迟,该函数可以使的时间延迟,该函数可以使sleep的实现与信号无的实现与信号无关关131nanosleep函数函数n该函数挂起调用者线程,直到超时或者接收到信该函数挂起调用者线程,直到超时或者接收到信号号n函数原型函数原型#includeint nanosleep(const struct timespec* rqtp struct timespec* rmtp);132nanosleep函数函数ntimespec结构体结构体struct timespec time_t tv_sec;/秒数秒数long tv_nsec;/纳秒数纳秒数;133nan

100、osleep函数函数int nanosleep(const struct timespec* rqtp struct timespec* rmtp);n参数与返回值参数与返回值lrqtp:指定需要等待的时间,秒数和纳秒数:指定需要等待的时间,秒数和纳秒数lrmtp:当:当nanosleep返回时,在返回时,在rmtp指定的结构体中指定的结构体中存储距离超时的时间;若不关心该时间,可设为存储距离超时的时间;若不关心该时间,可设为NULLl返回值:超时返回返回值:超时返回0,被信号中断,则返回,被信号中断,则返回-1n示例示例5.15134sleep函数实现的问题函数实现的问题n使用使用alarm

101、实现的实现的sleep函数,与函数,与alarm函数之间函数之间存在交互的可能存在交互的可能l例如:首先调用例如:首先调用alarm(10)l过了过了3秒,又调用了秒,又调用了sleep(5)lSleep将在将在5秒后返回,但是否在秒后返回,但是否在2秒后又产生一个秒后又产生一个SIGALRM信号呢?信号呢?nPOSIX.1标准未对这些交互做任何说明,因此,标准未对这些交互做任何说明,因此,考虑到移植性,不应对考虑到移植性,不应对sleep的实现做任何假设的实现做任何假设135中断的系统调用中断的系统调用n早期的早期的UNIX系统的一个特性是系统的一个特性是l如果进程在执行一个低速的系统调用(

102、究竟什么是系如果进程在执行一个低速的系统调用(究竟什么是系统调用?与一般函数调用有何区别?)期间,捕捉到统调用?与一般函数调用有何区别?)期间,捕捉到了一个信号,了一个信号,l则该系统调用就被中断不再继续执行则该系统调用就被中断不再继续执行l该系统调用返回出错,该系统调用返回出错,errno被设置为被设置为EINTRl这样做的理由:进程可能被这些低速系统调用永远阻这样做的理由:进程可能被这些低速系统调用永远阻塞,例如读网络塞,例如读网络136低速系统调用低速系统调用n常见的低速系统调用包括常见的低速系统调用包括l读写(读写(read、write)管道、终端设备、网络)管道、终端设备、网络l打开

103、(打开(open)终端设备等)终端设备等lpause、wait函数函数l某些某些ioctl操作操作l某些进程间通信函数某些进程间通信函数l注意:磁盘注意:磁盘I/O并不属于这类低速系统调用。并不属于这类低速系统调用。因为只要因为只要不是硬件错误,不是硬件错误,I/O操作总会返回。操作总会返回。137被中断的系统调用的处理被中断的系统调用的处理n被中断的系统调用,必须显式地处理出错返回,被中断的系统调用,必须显式地处理出错返回,典型的代码:典型的代码:Again:if(n=read(fd, buf, BUFFSIZE) = 0) _set-_val_cnt = 0;150sigfillset函数

104、函数nint sigfillset( sigset_t *set );n该函数初始化由该函数初始化由set指向的信号集,使其包括所有指向的信号集,使其包括所有信号信号n实现(实现(bits/sigset.h)中的关键代码片断)中的关键代码片断#define _sigfillset(set) int _cnt = _SIGSET_NWORDS; sigset_t *_set = (set); while (-_cnt = 0) _set-_val_cnt = 0UL;151sigaddset函数函数nint sigaddset( sigset_t *set, int signo);n该函数将一个

105、信号添加到该函数将一个信号添加到set中中n查看查看bits/sigset.h中的中的108到到120行行n实现中的关键代码翻译实现中的关键代码翻译int _sigaddset(_sigset_t *_set, int _sig)unsigned int _mask = 1 _val_word |= _mask;.152sigaddset函数函数n在在sigset_t结构体中,第一个结构体中,第一个32比特,对应信号比特,对应信号1到到31,第二个,第二个32比特,对应可靠信号比特,对应可靠信号34到到64n这一点与教材描述的结构不同这一点与教材描述的结构不同n示例示例5.17l在在gdb中,

106、使用中,使用“x /12tb &st”查看地址空间。查看地址空间。153sigdelset函数函数nint sigdelset( sigset_t *set, int signo);n该函数从该函数从set中删除信号中删除信号signon查看查看bits/sigset.h中的中的108到到120行行n实现中的关键代码翻译实现中的关键代码翻译int _sigdelset(_sigset_t *_set, int _sig)unsigned int _mask = 1 _val_word &= _mask;.154sigismember函数函数nint sigismember(const sigs

107、et_t *set, int signo);n该函数判断信号该函数判断信号signo是否在是否在set中中n查看查看bits/sigset.h中的中的108到到120行行n实现中的关键代码翻译实现中的关键代码翻译int _sigismember(_const _sigset_t *_set, int _sig)unsigned int _mask = 1 _val_word & _mask) ? 1 : 0,.155信号集和可靠信号机制信号集和可靠信号机制n基本概念基本概念n信号集及基本操作信号集及基本操作nsigprocmask函数,函数,sigpending函数函数nsigaction函数

108、函数nsigsuspend函数函数n可重入函数和线程安全的函数可重入函数和线程安全的函数156信号阻塞信号阻塞n当程序执行敏感任务时(比如更新数据库),不希当程序执行敏感任务时(比如更新数据库),不希望外界信号中断程序的运行。在这个阶段并不是简望外界信号中断程序的运行。在这个阶段并不是简单地忽略信号,而是单地忽略信号,而是阻塞阻塞阻塞阻塞这些信号,当进程处理完这些信号,当进程处理完关键任务后,还会处理这些信号。关键任务后,还会处理这些信号。nsigprocmask函数可以检测或更改进程的信号屏蔽函数可以检测或更改进程的信号屏蔽字字157sigprocmask函数函数n该函数检测或更改进程需要阻

109、塞的信号集合,或该函数检测或更改进程需要阻塞的信号集合,或者同时执行这两个操作者同时执行这两个操作n函数原型函数原型#includeint sigprocmask(int how, const sigset_t *set, sigset_t *oset);n返回值返回值l成功返回成功返回0l出错返回出错返回-1158sigprocmask函数函数n参数参数loset:进程的当前信号屏蔽字通过:进程的当前信号屏蔽字通过oset返回,若不关返回,若不关心当前信号屏蔽字,可以设为心当前信号屏蔽字,可以设为NULLl若若set不为不为NULL,则,则若若how=SIG_BLOCK,则进程新的信号屏蔽字

110、是,则进程新的信号屏蔽字是当前信号屏蔽字和当前信号屏蔽字和set指向信号集的并集,指向信号集的并集,set包含包含了希望阻塞的附加信号了希望阻塞的附加信号若若how=SIG_UNBLOCK,则进程新的信号屏蔽字,则进程新的信号屏蔽字是当前信号屏蔽字和是当前信号屏蔽字和set指向信号集补集的交集,指向信号集补集的交集,set包含了希望解除阻塞的信号包含了希望解除阻塞的信号若若how=SIG_SETMASK,则进程新的信号屏蔽字,则进程新的信号屏蔽字将被将被set指向的信号集的值代替指向的信号集的值代替159sigprocmask函数函数n参数参数l若若set为为NULL,how也无意义也无意义n

111、调用调用sigprocmask后,如果有任何未决的、不再后,如果有任何未决的、不再阻塞的信号,则在阻塞的信号,则在sigprocmask返回前,至少会返回前,至少会将其中一个信号递送给该进程。将其中一个信号递送给该进程。n该函数仅为单线程的进程定义的,对于多线程的该函数仅为单线程的进程定义的,对于多线程的进程,有另一套函数进程,有另一套函数n示例示例5.18160sigpending函数函数n该函数返回一个信号集,其中的各个信号对于调用该函数返回一个信号集,其中的各个信号对于调用进程是阻塞的而不能递送,因而也一定是当前未决进程是阻塞的而不能递送,因而也一定是当前未决的的n函数原型函数原型#in

112、clude int sigpending ( sigset_t *set );n参数与返回值参数与返回值lset:sigpending函数填充该结构体,返回未决信号函数填充该结构体,返回未决信号l成功返回成功返回0,出错返回,出错返回-1n程序演示程序演示(5.5)161sigprocmaskn if (sigismember(&pendmask, SIGQUIT)nn cout SIGQUIT pending endl;nn if (sigprocmask(SIG_SETMASK, &oldmask, NULL) 0)nn cout SIG_SETMASK error endl;nretur

113、n 0;nn cout SIGQUIT unblocked endl;n npause();能否使用:SIG_UNBLOCK162信号集和可靠信号机制信号集和可靠信号机制n基本概念基本概念n信号集及基本操作信号集及基本操作nsigprocmask函数,函数,sigpending函数函数nsigaction函数函数nsigsuspend函数函数n可重入函数和线程安全的函数可重入函数和线程安全的函数163sigaction函数函数n该函数用于替换该函数用于替换signal函数,即可以检查、修改信函数,即可以检查、修改信号的处理动作号的处理动作n函数原型函数原型#includeint sigacti

114、on(int signo, const struct sigaction *act, struct sigaction *oact);n返回值返回值l成功返回成功返回0,出错返回,出错返回-1164sigaction函数函数int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);n参数参数lsigno:指定需要处理的特定信号。应该在该信号收到:指定需要处理的特定信号。应该在该信号收到之前调用之前调用sigaction。除了。除了SIGSTOP和和SIGKILL之外,之外,可以把可以把signo设

115、为任何类型的信号设为任何类型的信号l若若act非空,则要修改信号的处理动作非空,则要修改信号的处理动作l若若oact非空,则系统经由非空,则系统经由oact指针返回该信号的上一个指针返回该信号的上一个动作动作lact、oact可以为空可以为空165sigaction结构体结构体struct sigaction void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int, siginfo_t *, void *); ;sa_handler用于设置信号处理函数的地址,或SIG_IGN、SIG_D

116、FLsa_mask说明了一个信号集,在调用该信号处理函数之前,这一信号集要附加到进程的信号屏蔽字中;仅当从信号处理函数返回时,再将进程的信号屏蔽字复原在信号处理函数被调用时,操作系统建立的新信号屏蔽字包括了正在被处理的信号。因此,保证了在处理一个给定信号时,同种信号会被阻塞(可靠信号与不可靠信号)166设置信号处理动作:设置信号处理动作:sigactionnsa_flagssa_flags成员用来改变成员用来改变signo信号的行为属性。信号的行为属性。sa_flags的若干值可以用或运算组合。的若干值可以用或运算组合。n比如把比如把sa_flagssa_flags设为设为SA_RESETHA

117、ND,使得在,使得在执行该信号处理函数时,信号处理函数重新设为执行该信号处理函数时,信号处理函数重新设为SIG_DFL。n把把sa_flagssa_flags设为设为SA_RESTART,则如果该信号,则如果该信号中断了系统调用,在处理完信号处理程序后,重中断了系统调用,在处理完信号处理程序后,重新启动该系统调用。新启动该系统调用。167Linux中常用的中常用的sa_flags值值nSA_INTERRUPT(0x20000000)l由此信号中断的系统调用不会自动重启(示例由此信号中断的系统调用不会自动重启(示例5.19)nSA_NOCLDSTOP(1)l若若signo是是SIGCHLD,当子

118、进程停止时,不产生此信,当子进程停止时,不产生此信号,但当其终止时,仍旧产生号,但当其终止时,仍旧产生nSA_NOCLDWAIT(2)l若若signo是是SIGCHLD,则当调用进程的子进程终止时,则当调用进程的子进程终止时,不创建僵死进程;不创建僵死进程;l若调用进程在后面调用了若调用进程在后面调用了wait,则调用进程阻塞,直到,则调用进程阻塞,直到其所有子进程都终止,此时返回其所有子进程都终止,此时返回-1,并将,并将errno设置为设置为ECHILDl(示例(示例5.20)168Linux中常用的中常用的sa_flags值值nSA_NODEFER(0x40000000)l在捕捉到此信号

119、时,在执行其信号捕捉函数时,系统在捕捉到此信号时,在执行其信号捕捉函数时,系统不自动阻塞此信号不自动阻塞此信号l除非除非sa_mask包括了此信号包括了此信号l此种类型的操作对应于早期的不可靠信号此种类型的操作对应于早期的不可靠信号nSA_RESETHAND(0x80000000)l在执行该信号处理函数时,信号处理函数重新设为在执行该信号处理函数时,信号处理函数重新设为SIG_DFL,并清除,并清除SA_SIGINFO标志标志169Linux中常用的中常用的sa_flags值值nSA_RESTART(0x10000000)l由此信号中断的系统调用会自动重启动由此信号中断的系统调用会自动重启动n

120、SA_SIGINFO(4)l此选项对信号处理函数提供了附加信息:一个指向此选项对信号处理函数提供了附加信息:一个指向siginfo结构的指针以及一个指向进程上下文标识符的结构的指针以及一个指向进程上下文标识符的指针指针170sigaction结构体结构体struct sigaction void (*sa_handler)(int); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int, siginfo_t *, void *); ;sa_sigaction字段:用于设置一个替代的信号处理函数若sa_flag指定了SA_SIGINF

121、O标志,则使用由sa_sigaction字段指定的信号处理函数,而不是由sa_handler指定的信号处理函数注意sa_sigaction和sa_handler可能使用同一个存储区域,因此只能使用其中之一171信号处理函数信号处理函数n原来的信号处理函数原型:原来的信号处理函数原型:lvoid handler(int signo);n现在的信号处理函数原型:现在的信号处理函数原型:lvoid handler(int signo, siginfo_t *info, void *context);n查看查看siginfo_t结构(程序结构(程序5.6)nContext:可以转换为:可以转换为uco

122、ntext_t类型指针,用类型指针,用于标识信号传递时进程的上下文于标识信号传递时进程的上下文172sigqueue函数函数n用于向进程发送一个带参数的信号用于向进程发送一个带参数的信号n函数原型:函数原型:int sigqueue(pid_t pid, int signo, const union sigval val);nval:typedef union sigval int sival_int; /该值可以传递给信号处理函数该值可以传递给信号处理函数 void *sival_ptr;sigval_t173信号处理函数信号处理函数nvoid handler(int signo, sigi

123、nfo_t *info, void *context)nn cout si_value.sival_int endl;nn程序演示程序演示5.7174信号集和可靠信号机制信号集和可靠信号机制n基本概念基本概念n信号集及基本操作信号集及基本操作nsigprocmask函数,函数,sigpending函数函数nsigaction函数函数nsigsuspend函数函数n可重入函数和线程安全的函数可重入函数和线程安全的函数175保护一段代码使其不被某种信号中断保护一段代码使其不被某种信号中断信号首先被阻塞,若在此处被处理,pause可能永远睡眠176sigsuspend函数函数n针对上述问题,需要在一

124、个原子操作中先恢复信针对上述问题,需要在一个原子操作中先恢复信号屏蔽字,然后使进程休眠;该功能可由号屏蔽字,然后使进程休眠;该功能可由sigsuspend函数实现。函数实现。n函数原型函数原型#includeint sigsuspend(const sigset_t *sigmask);n将进程的信号屏蔽字设为将进程的信号屏蔽字设为sigmask指向的值,在指向的值,在捕捉到一个信号之前,该进程挂起;捕捉到一个信号之前,该进程挂起;n从一个信号处理函数返回后,从一个信号处理函数返回后,sigsuspend返回,返回,并将信号屏蔽字恢复为调用并将信号屏蔽字恢复为调用sigsuspend之前的之前

125、的值;值;n函数总是返回函数总是返回-1,errno设置为设置为EINTR177sigsuspend函数函数n该函数可以用于保护临界区代码,使其不被指定该函数可以用于保护临界区代码,使其不被指定的信号中断。的信号中断。n该函数也可以用于等待一个信号处理程序设置一该函数也可以用于等待一个信号处理程序设置一个全局变量个全局变量178sigsuspend保护临界区代码保护临界区代码#include #include #include static void sig_int(int);int main(void) sigset_t newmask, oldmask, zeromask; if (sig

126、nal(SIGINT, sig_int) = SIG_ERR) err_sys(signal(SIGINT) error); sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGINT); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) 0) err_sys(SIG_BLOCK error);179 /* critical region of code */ pr_mask(in critical region: ); /* allow all signals

127、and pause */ if (sigsuspend(&zeromask) != -1) err_sys(sigsuspend error); pr_mask(after return from sigsuspend: ); /* reset signal mask which unblocks SIGINT */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) 0) err_sys(SIG_SETMASK error); /* and continue processing . */ exit(0);static void sig_int(int

128、signo) pr_mask(nin sig_int: ); return;180volatile sig_atomic_t quitflag; /* set nonzero by signal handler */int main(void) void sig_int(int); sigset_t newmask, oldmask, zeromask; if (signal(SIGINT, sig_int) = SIG_ERR) err_sys(signal(SIGINT) error); if (signal(SIGQUIT, sig_int) = SIG_ERR) err_sys(sig

129、nal(SIGQUIT) error); sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGQUIT); pr_mask(Before Sigprocmask:);sigsuspend: wait for global variable181 /* block SIGQUIT and save current signal mask */ if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) 0) err_sys(SIG_BLOCK error); pr_mask(

130、Before critical code:); while (quitflag = 0) sigsuspend(&zeromask); pr_mask(After critical code:); /* SIGQUIT has been caught and is now blocked; do whatever */ quitflag = 0; /* reset signal mask which unblocks SIGQUIT */ if (sigprocmask(SIG_SETMASK, &oldmask, NULL) 0) err_sys(SIG_SETMASK error); pr

131、_mask(After Sigprocmask:); exit(0);182void sig_int(int signo)if(signo = SIGINT) printf(.);else quitflag = 1;183sleep的可靠实现(三)的可靠实现(三)static void sig_alrm(void) return; /* nothing to do, just returning wakes up sigsuspend() */unsigned int sleep(unsigned int nsecs) struct sigaction newact, oldact; sigs

132、et_t newmask, oldmask, suspmask; unsigned int unslept; newact.sa_handler = sig_alrm; sigemptyset(&newact.sa_mask); newact.sa_flags = 0;这个程序涉及到信号这个程序涉及到信号这个程序涉及到信号这个程序涉及到信号处理的方方面面,希处理的方方面面,希处理的方方面面,希处理的方方面面,希望大家认真阅读。望大家认真阅读。望大家认真阅读。望大家认真阅读。184 sigaction(SIGALRM, &newact, &oldact); /* set our handler,

133、 save previous information */sigemptyset(&newmask); sigaddset(&newmask, SIGALRM); /* block SIGALRM and save current signal mask */ sigprocmask(SIG_BLOCK, &newmask, &oldmask); alarm(nsecs); suspmask = oldmask; sigdelset(&suspmask, SIGALRM); /* make sure SIGALRM isnt blocked */ sigsuspend(&suspmask);

134、/* wait for any signal to be caught */ /* some signal has been caught, SIGALRM is now blocked */185 unslept = alarm(0); sigaction(SIGALRM, &oldact, NULL /* reset signal mask, which unblocks SIGALRM */ sigprocmask(SIG_SETMASK, &oldmask, NULL); return(unslept);186信号集和可靠信号机制信号集和可靠信号机制n基本概念基本概念n信号集及基本操作

135、信号集及基本操作nsigprocmask函数,函数,sigpending函数函数nsigaction函数函数nsigsuspend函数函数n可重入函数和线程安全的函数可重入函数和线程安全的函数187可重入函数可重入函数n进程捕捉到信号并对其进行处理时,进程正在执进程捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理函数临时中断行的指令序列就被信号处理函数临时中断n从信号处理函数返回后,则继续执行被中断的正从信号处理函数返回后,则继续执行被中断的正常指令序列常指令序列n假设进程正在调用假设进程正在调用malloc函数,信号将其中断,函数,信号将其中断,信号处理函数中也调用信号处理函数

136、中也调用malloc函数,会有什么问函数,会有什么问题?题?188可重入函数可重入函数n可重入函数是指:可重入函数是指:l某个函数被信号中断执行,信号处理函数中又调用了某个函数被信号中断执行,信号处理函数中又调用了该函数,而不会带来任何问题的函数该函数,而不会带来任何问题的函数n不可重入的原因不可重入的原因l使用静态的数据结构使用静态的数据结构l调用调用malloc或或freel标准标准I/O函数,例如函数,例如printf等等189可重入函数可重入函数n每个线程只有一个每个线程只有一个errno。n当信号处理函数调用某个当信号处理函数调用某个API后,后,errno可能被重可能被重新设置新设

137、置n这样,被信号中断的程序将看到被修改后的这样,被信号中断的程序将看到被修改后的errno,而不是原来的值,而不是原来的值n所以,信号处理函数中,需要首先保存所以,信号处理函数中,需要首先保存errno,在,在信号处理函数返回前,恢复信号处理函数返回前,恢复errno190线程安全的函数线程安全的函数n对于同一个函数,若两个或两个以上的线程同时对于同一个函数,若两个或两个以上的线程同时执行,均得到正确的结果,则称该函数是线程安执行,均得到正确的结果,则称该函数是线程安全的函数全的函数n可重入的函数一定是线程安全的函数?可重入的函数一定是线程安全的函数?n线程安全的函数,不一定是可重入的?线程安

138、全的函数,不一定是可重入的?n例如:互斥变量保护的临界区例如:互斥变量保护的临界区n例如例如malloc函数函数191信号集和可靠信号机制信号集和可靠信号机制n基本概念基本概念n信号集及基本操作信号集及基本操作nsigprocmask函数,函数,sigpending函数函数nsigaction函数函数nsigsuspend函数函数n可重入函数和线程安全的函数可重入函数和线程安全的函数信号信号n信号的概念信号的概念nsignal函数函数n不可靠信号与可靠信号不可靠信号与可靠信号n信号的发送接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongj

139、mp函数函数nabort函数函数193sigsetjmp和和siglongjmp函数函数nP57在分析在分析sleep2时,使用到了时,使用到了setjmp和和longjmpn在信号处理中,当捕捉到信号并进入信号处理函在信号处理中,当捕捉到信号并进入信号处理函数时,当前信号被自动加入到进程的信号屏蔽字数时,当前信号被自动加入到进程的信号屏蔽字中中n当退出信号处理函数时,当前信号又会从进程信当退出信号处理函数时,当前信号又会从进程信号屏蔽字中删除号屏蔽字中删除n当使用当使用longjmp跳出信号处理函数时,信号屏蔽跳出信号处理函数时,信号屏蔽字会有何改动?(示例字会有何改动?(示例5.21)19

140、4sigsetjmp和和siglongjmp函数函数n从示例从示例5.21可知,可知,longjmp并未恢复进程的信号并未恢复进程的信号屏蔽字屏蔽字nPOSIX.1定义了定义了sigsetjmp和和siglongjmp,以解,以解决上述问题。决上述问题。n调用调用sigsetjmp时,除了保存上下文外,还将保存时,除了保存上下文外,还将保存当前的信号屏蔽字当前的信号屏蔽字n调用调用siglongjmp执行跳转后,将恢复调用执行跳转后,将恢复调用sigsetjmp时的信号屏蔽字时的信号屏蔽字195sigsetjmp和和siglongjmp函数函数n函数原型函数原型#includeint sigs

141、etjmp(sigjmp_buf env, int savemask);n参数和返回值参数和返回值l若若savemask非非0,则,则sigsetjmp在在env中保存进程的当中保存进程的当前信号屏蔽字前信号屏蔽字l返回值:若直接调用则返回返回值:若直接调用则返回0,若从,若从siglongjmp调用返调用返回则返回回则返回siglongjmp的第二个参数的第二个参数196sigsetjmp和和siglongjmp函数函数n函数原型函数原型#includevoid siglongjmp(sigjmp_buf env, int val);n参数参数lval作为作为sigsetjmp的返回值的返回

142、值197static void sig_usr1(int), sig_alrm(int);static sigjmp_buf jmpbuf;static volatile sig_atomic_t canjump;int main(void) if (signal(SIGUSR1, sig_usr1) = SIG_ERR) err_sys(signal(SIGUSR1) error); if (signal(SIGALRM, sig_alrm) = SIG_ERR) err_sys(signal(SIGALRM) error); pr_mask(starting main: ); /* Pro

143、g prmask 10.10*/ if (sigsetjmp(jmpbuf, 1) pr_mask(ending main: ); exit(0); canjump = 1; /* now sigsetjmp() is OK */ for ( ; ; ) pause();198static void sig_usr1(int signo) time_t starttime; if (canjump = 0) return; /* unexpected signal, ignore */ pr_mask(starting sig_usr1: ); alarm(3); /* SIGALRM in

144、3 seconds */ starttime = time(NULL); for ( ; ; ) /* busy wait for 5 seconds */ if (time(NULL) starttime + 5) break; pr_mask(finishing sig_usr1: ); canjump = 0; siglongjmp(jmpbuf, 1); /* jump back to main, dont return */ 保护机制保护机制199static void sig_alrm(int signo) pr_mask(in sig_alrm: ); return;200执行序

145、列执行序列mainmainsignal()signal()pr_mask()sigsetjmp()pause()sig_usr1sig_usr1递送递送递送递送SIGUSR1SIGUSR1pr_mask()alarm()time()time()sig_alrmsig_alrmpr_mask()return()递送递送递送递送SIGALRMSIGALRM从信号处理程序返回从信号处理程序返回从信号处理程序返回从信号处理程序返回pr_mask()siglongjmp()sigsetjmp()pr_mask()exit()201If we take sigsetjmp If we take sigse

146、tjmp with non-zero value for with non-zero value for parameter parameter savesigssavesigs202If we take sigsetjmp If we take sigsetjmp with zero value for with zero value for parameter parameter savesigssavesigs203示例程序几点注意示例程序几点注意n该程序演示两种信号特性:该程序演示两种信号特性:1.当执行信号处理程序时,自动阻塞该信号;当执行信号处理程序时,自动阻塞该信号;2.信号能嵌

147、套发生。信号能嵌套发生。3.sigsetjmp和和siglongjmp的联合使用方式;的联合使用方式;nsigsetjmp和和siglongjmp联合使用必须要有保护联合使用必须要有保护机制,即程序中的机制,即程序中的canjmp变量的判断;保护变量变量的判断;保护变量定义为定义为sig_atomic_t是为了保证在写该变量时不是为了保证在写该变量时不会被中断。会被中断。204示例程序示例程序n示例程序中,信号有可能中断示例程序中,信号有可能中断sigsetjmp的调用,的调用,即即jmpbuf并未正确地保存现场;这样在信号处理并未正确地保存现场;这样在信号处理函数中,调用函数中,调用sigl

148、ongjmp将可能出错将可能出错n针对这种情况,引入了针对这种情况,引入了canjump变量。只有当变量。只有当canjump变量被设为变量被设为1后,信号处理函数才能调用后,信号处理函数才能调用siglongjmp205示例程序示例程序n主程序中设置主程序中设置canjmp时,会被信号中断,信号处时,会被信号中断,信号处理函数读理函数读canjmp,而引起错误吗?,而引起错误吗?ncanjmp的类型是的类型是sig_atomic_t,在写这种类型的,在写这种类型的变量时,不会被中断变量时,不会被中断l在具有虚拟存储器的系统上,这种变量不会垮页在具有虚拟存储器的系统上,这种变量不会垮页l可以用

149、一条机器指令对其进行访问可以用一条机器指令对其进行访问l为什么又使用为什么又使用volatile?信号信号n信号的概念信号的概念nsignal函数函数n不可靠信号与可靠信号不可靠信号与可靠信号n信号的发送接收机制信号的发送接收机制n信号集与可靠信号机制信号集与可靠信号机制nsigsetjmp和和siglongjmp函数函数nabort函数函数207abort函数函数n该函数可以使异常程序终止该函数可以使异常程序终止n函数原型函数原型#includevoid abort();n此函数将此函数将SIGABRT信号发送给调用进程信号发送给调用进程nISO C要求若捕捉到此信号而且相应信号处理程要求若

150、捕捉到此信号而且相应信号处理程序返回,序返回,abort仍不会返回到其调用者(示例仍不会返回到其调用者(示例5.22)208abort函数函数nPOSIX.1说明了说明了abort并不理会进程对并不理会进程对SIGABRT的阻塞和忽略的阻塞和忽略n让进程捕捉该信号的意图让进程捕捉该信号的意图l在进程终止前由其执行所需的清理操作在进程终止前由其执行所需的清理操作l若信号处理函数不终止自己,若信号处理函数不终止自己,abort终止该进程终止该进程实验六:线程同步实验实验六:线程同步实验n实验目的:实验目的:l掌握掌握Linux互斥量机制互斥量机制l掌握掌握Linux条件变量机制条件变量机制l掌握多

151、线程协同工作的程序设计思想掌握多线程协同工作的程序设计思想n实验内容:实验内容:l利用利用Linux互斥量机制和互斥量机制和Linux条件变量机制实现哲学条件变量机制实现哲学家进餐问题(家进餐问题(5个哲学家)个哲学家)实验七:综合应用实验实验七:综合应用实验n实验目的:实验目的:l掌握掌握Linux信号量机制信号量机制 l掌握掌握Linux共享内存机制共享内存机制l掌握多进程协同工作的程序设计思想掌握多进程协同工作的程序设计思想n实验内容:实验内容:l利用利用Linux信号量机制、共享内存机制和多进程实现生信号量机制、共享内存机制和多进程实现生产者、消费者问题产者、消费者问题实验七:综合应用实验实验七:综合应用实验

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 研究生课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号