linux_多进程多线程及其同步_第1页
linux_多进程多线程及其同步_第2页
linux_多进程多线程及其同步_第3页
linux_多进程多线程及其同步_第4页
linux_多进程多线程及其同步_第5页
已阅读5页,还剩35页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Linux 多进程、多线程及网络编程指南1、 进程(多进程)2、 线程(多线程)3、 进程间通信4、 并发程序的同步5、 socket网络编程1、进程1.1进程定义进程是程序在计算机上的执行活动,是一个运行着一个或多个线程的地址空间和这些线程所需要的系统资源,其中包括程序代码、数据、变量、打开文件的文件描述符、和环境。1.2进程的五种状态在五状态进程模型中,进程状态被分成下列五种状态。进程在运行过程中主要是在就绪、运行和阻塞三种状态间进行转换。创建状态和退出状态描述进程创建的过程和进程退出的过程。1)运行状态(Running):进程占用处理器资源;处于此状态的进程的数目小于等于处理器的数目。在

2、没有其他进程可以执行时(如所有进程都在阻塞状态),通常会自动执行系统的空闲进程。2)就绪状态(Ready):进程已获得除处理器外的所需资源,等待分配处理器资源;只要分配了处理器进程就可执行。就绪进程可以按多个优先级来划分队列。例如,当一个进程由于时间片用完而进入就绪状态时,排入低优先级队列;当进程由IO操作完成而进入就绪状态时,排入高优先级队列。3)阻塞状态(Blocked):当进程由于等待I/O操作或进程同步等条件而暂停运行时,它处于阻塞状态。4)创建状态(New):进程正在创建过程中,还不能运行。操作系统在创建状态要进行的工作包括分配和建立进程控制块表项、建立资源表格(如打开文件表)并分配

3、资源、加载程序并建立地址空间表等。5)退出状态(Exit):进程已结束运行,回收除进程控制块之外的其他资源,并让其他进程从进程控制块中收集有关信息(如记帐和将退出代码传递给父进程)。进程三种状态的转换如图所示:就绪执行等待时间片到因等待时间发生而唤醒因等待时间发生而睡眠调度到进程三种状态的转换关系1.3 进程的结构Linux系统是一个多进程的系统,进程间有并行性、互不干扰等特点。通俗的说进程间是分离的任务,拥有各自的权力和责任,每个进程运行在各自独立的虚拟地址空间,一个进程发生了异常,它也不会影响到系统中的其他进程。Linux进程包含三个段:数据段、代码段、堆栈段。数据段:存放的是全局变量、常

4、数、static 定义的静态变量和动态分配的数据(malloc函数取得的空间)等。代码段:存放的是程序代码的数据。堆栈段:存放的是子程序的返回地址、子程序的参数以及程序的局部变量。1.4 进程的执行模式当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核运行态(或简称为内核态)。此时处理器处于特权级最高的(0级)内核代码中执行。当进程处于内核态时,执行的内核代码会使用当前进程的内核栈。每个进程都有自己的内核栈。当进程在执行用户自己的代码时,则称其处于用户运行态(用户态)。即此时处理器在特权级最低的(3级)用户代码中运行。当正在执行用户程序而突然被中断程序中断时,此时用户程

5、序也可以象征性地称为处于进程的内核态。因为中断处理程序将使用当前进程的内核栈。这与处于内核态的进程的状态有些类似。在内核模式下运行的进程可以执行机器的特权指令,并且此时该进程的运行不受用户的干扰,即使是root用户也不能干扰内核模式下程序的运行。进程上下文和中断上下文处理器总处于以下状态中的一种:1、内核态,运行于进程上下文,内核代表进程运行于内核空间;2、内核态,运行于中断上下文,内核代表硬件运行于内核空间;3、用户态,运行于用户空间。1.4 创建新进程通过fork调用创建一个新进程,这个系统调用复制当前进程在系统表中创建一个新的表项,新表项中的许多属性与当前进程是相同的。新进程与原进程几乎

6、一样,但还是有不同之处。父子进程的异同:继承属性差异真实的用户ID和组ID,有效用户ID和组ID进程组IDSESSION ID所打开文件及文件的偏移量控制终端设置用户ID和设置组ID标记位根目录与当前工作目录文件缺省创建的权限掩码可访问的内存段环境变量及其他资源分配进程ID父进程ID子进程运行的时间记录父进程对文件的锁fork 和 exec函数族结合在一起使用就是创建新进程所需要的一切了,值得注意的是exec函数执行之后原进程之后的代码将不会被执行。 创建一个新进程也可以使用vfork,但vfork并不完全拷贝父进程的数据段而是和父进程共享数据段。这是因为vfork函数是与exec函数族相连,

7、创建执行另一个程序的新进程。并且调用vfork对于父子进程的执行次序有限制,调用vfork时,父进程被挂起,子进程运行至调用exec函数族或调用exit时解除这种状态。而fork是不会阻塞调用进程的,父子进程的执行顺序是不确定的。1.5 僵尸进程一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程. 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程。僵尸进程过多会

8、严重占用系统的进程ID号,从而导致整个系统不能工作。僵尸进程的避免 1)父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。 2)如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后, 父进程会收到该信号,可以在handler中调用wait回收。 3)如果父进程不关心子进程什么时候结束,可用signal(SIGCHLD, SIG_IGN)通知内核,那么子进程结束后,内核会回收,并不再给父进程发送信号。 4)还有一些技巧,就是fork两次,父进程fork一个子进程,然后继续工作,子进程fork一 个孙进程后退出,那么孙进程被ini

9、t接管,孙进程结束后,init会回收。不过子进程的回收 还要自己做。避免僵尸进程示例如下:开始pid=fork()pid 0pid = 0pid安装信号SIGCHLD的处理函数sleep 2 secDo working向父进程发送SIGCHLD信号sleep 1 sec结束调用wait或waitpid清理子进程1.5 等待进程处理多进程时,经常用到有关进程间等待的操作,进程等待可以是父进程等待子进程也可以是进程组成员间的等待。进程等待常用的系统调用是wait和waitpid。pid_t wai(int *status); 调用专用于等待一个子进程,调用wait的时候,调用者父进程将被挂起,直到

10、该进程的一个子进程结束时,该调用返回。如果调用没有子进程,则错误返回,调用成功,如果status指针不为空,那么被等待退出的进程的状态信息将被写入status所指向的位置,调用失败时,返回-1。pid_t waitpid(pid_t pid, int *status, int options); 该调用比wait灵活一些,可以用来等待制定进程,且可以使进程不挂起而立刻返回。参数pid用于指定要等待的进程,参数status类似wait系统调用参数的作用,参数options则用于改变waitpid的行为,比如选项WNOHANG,它的作用是防止waitpid调用将调用者的执行挂起。可以使用该选项检查

11、某个特定子进程是否退出。如果子进程没有结束或意外终止,它返回0,否则返回child_pid,如果waitpid失败,它将返回-1,并设置errno。失败的情况可能是:没有子进程(errno设置为ECHILD)、调用被某个信号中断(EINTR)或选项参数无效(EINVAL)。头文件sys/wait.h 文件中定义的宏可以解释程序退出的原因,如:WIFEXITED(stat_val) 如果子进程正常结束,它就取一个非零值。WEXITSTATUS(stat_val) 如果WIFEXITED非零,它返回子进程的退出码。其他有关进程退出宏的用法可查阅相关文档。1.6 终止进程可以使用exit(int s

12、tatus)或_exit(int status)系统调用来终止进程,当程序执行到exit和_exit时,进程会无条件的停止所有操作,清理包括PCB在内的各种数据结构,并终止调用进程的运行。但这两个函数有一些区别,如下图所示:exit(int status)函数与_exit(int status)函数的最大区别就在于,exit()函数在调用exit系统调用之前要检查文件的打开情况,把文件缓冲区里的内容写回到文件中。参数status可以传递进程结束时的状态,0表示正常结束,其他数值表示出现了错误,进程非正常结束,在实际编程中,可以用wait系统调用接受子进程的返回值,从而针对不同情况进程不同处理。

13、进程运行中调用退出处理函数exit(int status)_exit(status)清理I/O缓冲调用exit系统调用进程终止运行1.7编写多进程程序目的:通过编写一个多进程程序进一步加深linux中多进程编程的步骤,该程序设计到fork、exec、wait、waitpid等函数的使用。内容:一个父进程,fork两个子进程,其中一个子进程运行”ls -l”指令,另一个子进程暂停5s后异常退出,父进程并不阻塞自己,并等待子进程的退出信息,待收集到该信息,父进程就返回。1) 流程图如下:开始P1=fork()P10P1=0 P1 P2P2=fork()P2=fork() P2P2=0P2=0P20

14、P20sleep 2 secexeclp()P=waitpid(P2,NULL,WNOHANG)execlp()noP=0yessleep 1 secyesP=0noyesP=P2getChild2no结束2) 具体代码int main(void)pid_t child1,child2,child;child1 = fork();child2 = fork();if(child1 = -1)perror(child1 fork errorn);exit(1);else if(child1 = 0)if(execlp(ls,ls,-l,NULL) 0)perror(child1 execlp);

15、if(child2 = -1)perror(child2 fork);exit(1);else if(child2 = 0)printf(In child2: sleep for 5 seconds and the exitn);sleep(5);exit(0);elsedochild = waitpid(child2,NULL,WNOHANG);if(child = 0)printf(The child2 process has not exited!n);sleep(1);while(child = 0);if(child = child2)printf(Get child2n);else

16、printf(Error occured!n);2、线程2.1 线程的定义一个程序中的多条执行路线就叫做线程,线程是一个进程内部的一个控制序列。2.2 线程的分类线程按其调度者可分为用户级线程和核心级线程。1) 用户级线程用户级线程主要解决的是上线问切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要待定的内核支持。在这里,操作系统往往会提供一个用户控件线程库,该线程库提供了线程的创建、调度、撤销等功能,而内核仍然仅对进程进行管理。如果一个进程中的某一个线程调用了一个阻塞的系统调用,那么该进程包括该进程中的其他所有线程也同时被阻塞。这种用户级线程的主要缺点是在一个进程中的多

17、个线程的调度中无法发挥多处理器的优势。2) 这种线程允许不同进程中的线程按照同一相对优先调度方法进行调度,这样就可以发挥多处理器的并发优势。2.3 线程的优缺点优点:1) 可以让程序看起来好像是同时在做两件事情。2) 线程之间的切换需要操作系统做的工作比进程之间的切换少得多。缺点:1) 编写多线程程序需要非常仔细的设计。2) 对多线程程序的雕饰要比对单线程程序的调试困难得多,因为线程之间的交互非常难于控制。2.4 创建线程 函数pthread_create会在调用后立刻返回,原线程会继续执行之后的指令。同时,新线程开始执行线程函数。Linux异步调用这两个线程,因此原线程和新线程并无特定的执行

18、顺序。当创建一个新线程时,新的执行线程将拥有自己的栈(因此也有自己的局部变量),但与它的创建者共享全局变量、文件描述符、信号处理函数和当前的目录状态。2.5 等待线程和多进程中使用wait或waitpid等待一个进程一样,多线程编程中也有类似作用的函数用于等待一个线程,该函数的定义如下:int pthread_join(pthread_t thread, void *value_ptr);参数thread制定了将要等待的线程,参数value_ptr指向另一个指针,而后者指向线程的返回值,该函数成功时返回0,失败时返回错误代码。值得注意的是,如果不是用pthread_join函数等待子线程结束,

19、那么原线程先于子线程结束将释放所有资源结束整个进程,子线程也将退出。2.6 退出线程一般情况下,一个线程有两种退出方式,方式1:线程函数执行完毕,从线程函数中返回,线程函数的返回值也被作为线程的返回值。方式2:线程显示调用pthread_exit,这个函数可以直接在线程函数中调用,也可以在其他直接或间接被线程函数调用的函数中调用。调用pthread_exit的参数就是线程的返回值。方式3:线程被其他线程取消,通常调用int pthread_cancel(pthread_t thread);2.7 线程的属性线程属性提供了一种可以用于在细微度调整线程行为方式的机制。调用pthread_threa

20、d函数的时候如果只想线程属性对象的指针为NULL,那么默认的线程属性被用于配置新线程。也可以通过创建传递一个线程属性对象来指定属性中的值。定义线程属性的步骤如下:1) 创建一个pthread_attr_t 对象。最简单的方法是定义一个该类型的自动变量。2) 调用pthread_attr_init,传递一个指向新创建对象的指针,这会将各个属性职位默认值。3) 修改属性对象,使各个属性包含期望的值。4) 调用pthread_attr_destroy释放这个属性对象。这个pthread_attr_t对象本身不会被释放;可以通过pthread_attr_init重新初始化它。创建脱离属性线程的示例:#

21、include void *thread_function(void *thread_arg)int main()pthread_attr_t attr;pthread_t thread;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);pthread_create(&thread,&attr,&thread_function,NULL);pthread_attr_destory(&attr);/*do other works*/*do not need to wait the

22、 detach thread*/return 0;一个线程可以创建为一个可等待的线程(默认),或者是一个脱离的线程。一个可等待的线程,类似于一个进程,在结束的时候不会被系统自动清理,它的退出状态停留在系统中直到另外一个线程调用pthread_join获取它的返回值。直到这时它的资源才被释放。而一个脱离线程在结束的时候会被自动清理,因此其他线程无法与他的结束时间进行同步,也无法获取它的返回值。int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);函数用于设置脱离属性。值得注意的是,一个线程创建成为一个可等待线

23、程,它随后也可以通过pthread_detach进程转换变成一个脱线程。一旦线程成为脱离线程,它将无法转换回可等待线程。2.8 线程取消一个线程执行过程中可能会被其他线程请求取消,请求取消的线程调用pthread_cancel来取消线程,通常一个被取消的线程会被其他线程等待(pthread_wait)从而释放它占用的资源除非这个线程时脱离线程。一个取消线程的退出之由特殊值PTHREAD_CANCELED指定。但是线程的运行有可能在一段不可分割的代码中,必须全部得到执行或者干脆不执行。例如,线程可能分配一些资源,使用并稍后释放它们,如果线程中途被取消,它可能没有机会释放这些资源,从而导致资源的泄

24、露。为防止这类情况发生,一个线程可以控制自身是否被取消,以及合适允许取消操作。对于线程的取消可能处于三种情况:1) 线程可以一步取消。线程可以再执行中的任意时刻被取消。2) 线程可以被同步取消。线程可以被取消,但不是在任意时刻都可以。取消请求会被排队,而线程只有在到达特殊的执行点执行取消操作。(默认)3) 线程不可被取消。尝试取消线程的请求会被直接忽略。pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);/设置线程异步取消pthread_setcanceltype(PTHREAD_CANCEL_,DEFERRED,NULL);/设置线

25、程同步取消pthread_testcancel(thread_id);/在一个可同步取消的线程中处理一个没有被处理的线程取消请求。pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);/设置线程不可取消pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);/设置线程可取消线程可以使用int pthread_setcancelstate(int state, int *oldstate);函数指定一段临界区,临界区是指一段必须完整执行或者不执行的代码,一旦一个线程进入了临界区那么,在到达临界点之前它

26、是无法被取消的。参数oldstate存放之前线程的取消状态,在退出临界区的时候用于恢复线程取消状态到临界区之前的状态,而不是无条件地将线程取消状态设置为PTHREAD_CANCEL_ENABLE.这样可以保证从一个临界区中运行另一个包含临界区的代码段不会出错。程序示例:用临界区保护银行事务Float *account_balances;int process_transaction(int from_acct,int to_acct,float dollars)int old_cancel_state;if(account_balancesfrom_acct dollars)return 1;

27、/*进入临界区*、pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&old_cancel_state);/*转移余额*/account_balancesto_acct += dollars;account_balancesfrom_acct -=dollars;/*退出临界区*/pthread_setcancelstate(old_cancel_state,NULL);return 0;2.9、线程专有数据 在单线程的程序里,有两种基本的数据:全局变量和局部变量。但在多线程程序里,还有第三种数据类型:线程数据(TSD: Thread-Specific

28、 Data)。它和全局变量很象,在线程内部,各个函数可以象使用全局变量一样调用它,但它对线程外部的其它线程是不可见的。这种数据的必要性是显而易见 的。例如我们常见的变量errno,它返回标准的出错信息。它显然不能是一个局部变量,几乎每个函数都应该可以调用它;但它又不能是一个全局变量,否则在 A线程里输出的很可能是B线程的出错信息。要实现诸如此类的变量,我们就必须使用线程数据。我们为每个线程数据创建一个键,它和这个键相关联,在各个线程 里,都使用这个键来指代线程数据,但在不同的线程里,这个键代表的数据是不同的,在同一个线程里,它代表同样的数据内容。使用线程数据通常包4个步骤:1) 创建一个键;调

29、用int pthread_key_create(pthread_key_t *key,void(*destructor)(void *);函数,参数key保存新创建的键,参数destructor是一个清理函数,如果传递一个函数指针,则系统将在线程退出的时候以这个键值对应的数据对象为参数自动调用这个清理函数。清理函数非常有用,因为即使当线程在任何一个非特定运行时刻被取消,这个线程函数也会被保证调用。如果对应的数据对象时一个空指针,清理函数不会被调用。如果不需要清理函数,这里传递一个NULL。2) 为一个键指定线程数据;调用int pthread_setspecific(pthread_key_t

30、 key, const void *value);设定相应的线程专有数据,参数key是键值,参数value是要设置的数据的void*指针。3) 从一个键读取线程数据;以键值为参数调用void *pthread_getspecific(pthread_key_t key);可获得一个已经设置的线程专有数据。4) 删除键。以下是通过线程专有数据实现的每个线程日志的示例程序:值得注意的是,thread_function不需要关闭日志文件。这是因为当TSD键值被创建的时候,我们将close_thread_log 指定为这个TSD的清理函数。当线程退出的时候,系统将thread_log_key所映射的值

31、作为参数调用这个函数。这个函数会父子关闭文件指针。static pthread_key_t thread_log_key;void write_to_thread_log(const char *message)FILE *thread_log = (FILE*) pthread_getspecific(thread_log_key);fprintf(thread_log,%sn,message);void close_thread_log(void *thread_log)fclose(FILE*)thread_log);void *thread_function(void *args)ch

32、ar thread_log_filename20;FILE *thread_log;sprintf(thread_log_filename,thread%d.log,(int)pthread_self();thread_log = fopen(thread_log_filename,w);pthread_setspecific(thread_log_key,thread_log);write_to_thread_log(Thread starting.);long i = 0;long j = 0;printf(“hellon”);return NULL;int main(void)int i

33、;pthread_t threads5;pthread_key_create(&thread_log_key,close_thread_log);for(i=0; i5; +i)pthread_create(&(threadsi),NULL,thread_function,NULL);for(i=0; i5; +i)pthread_join(threadsi,NULL);return 0;2.10 线程终止时的清理因为线程有可能以之中不可预知的方式终止,比如说,线程在其他线程的干预下或者由于自身运行出错(访问非法地址)而退出,这种退出方式是不可预见的。不论是线程正常终止还是异常终止,都会存在资

34、源释放的问题,在不考虑运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别锁资源,就是一个必须考虑解决的问题。以上示例程序中,线程专有数据的清理函数可以很有效地防止线程退出或被取消的时候出现的资源泄漏。但有些情况,我们希望创建一个清理函数却不希望为每个线程创建一个线程专有数据对象。针对这种需求,linux系统提供了清理句柄。清理句柄就是一个当线程退出时被自动调用的函数。清理句柄接受一个void*类型的参数,且这个参数在注册清理句柄的时候被同时确定。这样就可以很方便地允许用同一个清理函数清理多份资源实例。清理句柄是一个临时性的工具,只在当线程被取消或中途推车而不是正常结

35、束允许的时候被调用。在一般情况下,程序应该显示释放分配的资源并清除已经设置的清理句柄。通过提供两个参数(一个指向清理函数的函数指针和一个作为清理函数参数的void *类型的值)调用pthread_cleanup_push可以创建一个清理句柄。对pthread_cleanup_push的调用可以通过调用pthread_cleanup_pop进行平衡;pthread_cleanup_pop会取消对一个句柄的注册。pthread_cleanup_pop接受一个int型参数,如果这个参数为非零值,则在取消注册这个句柄的同时,清理句柄将被执行。以下程序展示清理句柄的使用情况:void *allocate

36、_buffer(size_t size)return malloc(size);void deallocate_buffer(void *buffer)printf(free buffer n);free(buffer);void *thread_function(void *arg)void *temp_buffer = allocate_buffer(1024);pthread_cleanup_push(deallocate_buffer,temp_buffer);int x = 0;while(1)sleep(1);x+;if(x = 2)/pthread_exit(NULL);/线程调

37、用pthread_exit结束也会导致清理函数的调用/return (void *)1; /线程return返回将不会调用清理函数pthread_cleanup_pop(1);int main(void)pthread_t thread_id;int rc = pthread_create(&thread_id,NULL,thread_function,NULL);sleep(5);pthread_cancel(thread_id);/pthread_cancel取消线程会导致线程清理函数调用pthread_join(thread_id,NULL); /此处值得注意,如果不等待线程退出,那么主

38、线程有可能在子线程退出前结束,这时整个进程的资源会被系统回收,将看不到子线程处理函数的调用。return 0;3.1进程间通信3.1.1信号1)信号的定义信号是系统响应某些条件而产生的一个事件。信号是在软件层次上对中断机制的一种模拟。原理上,一个进程受到一个信号与处理器受到一个中断请求是一样的。2)信号的来源硬件来源:按下键盘或其他硬件故障软件来源:kill、raise、alarm、sigqueue等,软件来源还包括一些非法运算操作。3)信号的种类从可靠性:可靠信号与不可靠信号;信号值小于SIGRTMIN(SIGRTMIN=32,SIGRTMAX=63)的信号都是不可靠信号。信号值位于SIGR

39、TMIN和SIGRTMAX之间的信号为可靠信号。可靠信号支持排队。从时间上:实时信号与非实时信号;后32个信号表示实时信号,等同于前面阐述的可靠信号。总之,非实时信号都不支持排队,是不可靠信号;实时信号都支持排队,是可靠信号。4)进程对信号的响应a、忽略信号;对信号不做任何处理,但有两个信号不能忽略:SIGKILL及SIGSTOP。b、捕捉信号;定义信号处理函数,当信号发生时,执行相应的处理函数。c、缺省处理;linux系统对信号都规定了默认的操作。注意:进程对实时信号的缺省反应时进程终止。Linux采用何种方式处理信号,取决于安装信号处理函数的时候传递给安装函数的参数句柄。5)信号的发送in

40、t kill(pid_t pid, int sig);int raise(int sig);int sigqueue(pid_t pid, int sig, const union sigval value);unsigned int alarm(unsigned int seconds);int setitimer(int which, const struct itimerval *value,struct itimerval *ovalue);void abort(void); /SIGABORT被进程设置为阻塞信号,调用abort之后,SIGABORT仍能被进程接收。5)信号的安装ty

41、pedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);struct sigaction void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_r

42、estorer)(void);/该项已过时,不应该被使用。 ;5)信号集及信号集操作函数信号集是一种数据结构,用来描述信号的集合,linux系统所出现的信号可以部分甚至全部出现在信号集中,主要与信号阻塞相关函数配合使用,在多进程编程同步方面有着重要作用。int sigemptyset(sigset_t *set);/清空set信号集中的所有信号int sigfillset(sigset_t *set);/将linux系统的64中信号全部包含进信号集setint sigaddset(sigset_t *set, int signum);/在set信号集中加入信号signum int sigdel

43、set(sigset_t *set, int signum);/从set信号集中删除信号signumint sigismember(const sigset_t *set, int signum);/判断信号signum是否在set信号集中6)信号阻塞与信号未决信号的“未决”:是一种状态,指的是从信号的产生到信号被处理前的这一段时间;信号被阻塞并不是被丢弃,只是没有被处理。这种状态叫未决状态。解除阻塞后,处于“未决状态”的信号会继续被处理。对于不可靠信号,处于未决状态只能有一次,而对于可靠信号,处于未决状态可以有多次。信号的“阻塞”:是一个开关动作,指的是阻止信号被处理,但不是阻止信号产生。每

44、个进程都包含一个信号集,该信号集是用来描述哪些信号发送到进程时将被阻塞。信号的阻塞就是让系统暂时保留信号留待以后发送。由于另外有办法让系统忽略信号,所以一般情况下信号的阻塞只是暂时的,只是为了防止信号打断敏感的操作。a、当你需要修改某些全局变量时,你可以通过sigprocmask()函数阻塞处理函数中也使用该变量的信号。b、 在某些信号处理函数中,为了阻止同类信号的到来,可以使用sigaction()函数的sa_mask阻塞特定的信号阻塞信号的作用:使用函数sigprocmask()阻塞信号的传递,只是延迟信号的到达。信号会在解除阻塞后继续传递。这种情况往往需要在信号程序和其它程序共享全局变量时,如果全局变量的类型不是sig_atomic_t类型,当一部分程序恰好读、写到变量的一半发生信号,而信号程序里会改变该信号,那么就会产生混 乱。为了避免这种混乱,提供程序的可靠性,你必须在操作这类变量前阻塞信号,操作完成后恢复信号的传递。信号阻塞也用来处理必须保证连续操作的完整性方面。比如,你需要检测一个标志(可以是sig_atomic_t类型),该标志在信号程序中设置,当标志没有设置时可以执行某个操作。假如恰好在检测标志后发生信号,那么信号返回后,程

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论