newlist

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  33 随笔 :: 0 文章 :: 0 评论 :: 0 引用

公告

2012年2月19日 #

摘要:网络编程socket api存在一批核心接口,而这一批核心接口就是几个看似简单的函数,尽管实际上这些函数没有一个是简单。connect函数就是这些核心接口的一个函数,它完成主动连接的过程。

connect函数的功能是完成一个有连接协议的连接过程,对于TCP来说就是那个三路握手过程,它的函数原型:

#include<sys/socket.h> int connect(int sockfd, const struct sockaddr* server_addr, socklen_t addrlen) 返回:0──成功, -1──失败。

 

为了理解connect函数,我们需要对connect函数的功能进行介绍。connect函数的功能可以用一句话来概括,就是完成面向连接的协议的连接过程,它是主要连接的。面向连接的协议,在建立连接的时候总会有一方先发送数据,那么谁调用了connect谁就是先发送数据的一方。如此理解connect三个参数是容易了,我必需指定数据发送的地址,同时也必需指定数据从哪里发送,这正好是connect的前两个参数,而第三个参数是为第二个参数服务的。

参数sockfd
指定数据发送的套接字,解决从哪里发送的问题。内核需要维护大量IO通道,所以用户必需通过这个参数告诉内核从哪个IO通道,此处就是从哪个socket接口中发送数据。sockfd是先前socket返回的值。
参数server_addr
指定数据发送的目的地,也就是服务器端的地址。这里服务器是针对connect说的,因为connect是主动连接的一方调用的,所以相应的要存在一个被连接的一方,被动连接的一方需要调用listen以接受connect的连接请求,如此被动连接的一方就是服务器了。
参数addrlen
指定server_addr结构体的长度。我们知道系统中存在大量的地址结构,但socket接口只是通过一个统一的结构来指定参数类型,所以需要指定一个长度,以使内核在进行参数复制的时候有个有个界限。

 

与所有的socket网络接口一样,connect总会在某个时候可能失败,此时它会返回-1,相应的errno会被设置,用户可能通过这个值确定是哪个错误。常见的错误有对方主机不可达或者超时错误,也可以是对方主机没有相应的进程在对应端口等待。

posted @ 2012-02-19 21:55 newlist 阅读(125) 评论(0) 编辑

摘要:对于服务器编程中最重要的一步等待并接受客户的连接,那么这一步在编程中如何完成,accept函数就是完成这一步的。它从内核中取出已经建立的客户连接,然后把这个已经建立的连接返回给用户程序,此时用户程序就可以与自己的客户进行点到点的通信了。

accept函数等待并接受客户请求:

#include<sys/socket.h>int accept(int sockfd, struct sockaddr* addr, socklen_t* len)
返回:非负描述字——成功, -1——失败

 

accept默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。此时我们需要区分两种套接字,一种套接字正如accept的参数sockfd,它是监听套接字,在调用listen函数之后,一个套接字会从主动连接的套接字变身为一个监听套接字;而accept返回是一个连接套接字,它代表着一个网络已经存在的点点连接。自然要问的是:为什么要有两种套接字?原因很简单,如果使用一个描述字的话,那么它的功能太多,使得使用很不直观,同时在内核确实产生了一个这样的新的描述字。

参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。

 

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。

posted @ 2012-02-19 19:25 newlist 阅读(155) 评论(0) 编辑

摘要:listen函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其它进程的请求,从而成为一个服务器进程。在TCP服务器编程中listen函数把进程变为一个服务器,并指定相应的套接字变为被动连接。

listen函数在一般在调用bind之后-调用accept之前调用,它的函数原型是:

#include<sys/socket.h>int listen(int sockfd, int backlog)

返回:0──成功, -1──失败

 

参数sockfd
被listen函数作用的套接字,sockfd之前由socket函数返回。在被socket函数返回的套接字fd之时,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套接字调用connect函数,期待它主动与其它进程连接,然后在服务器编程中,用户希望这个套接字可以接受外来的连接请求,也就是被动等待用户来连接。由于系统默认时认为一个套接字是主动连接的,所以需要通过某种方式来告诉系统,用户进程通过系统调用listen来完成这件事。
参数backlog
这个参数涉及到一些网络的细节。在进程正理一个一个连接请求的时候,可能还存在其它的连接请求。因为TCP连接是一个过程,所以可能存在一种半连接的状态,有时由于同时尝试连接的用户过多,使得服务器进程无法快速地完成连接请求。如果这个情况出现了,服务器进程希望内核如何处理呢?内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接,这样的一个队列内核不可能让其任意大,所以必须有一个大小的上限。这个backlog告诉内核使用这个数值作为上限。
毫无疑问,服务器进程不能随便指定一个数值,内核有一个许可的范围。这个范围是实现相关的。很难有某种统一,一般这个值会小30以内。

 

当调用listen之后,服务器进程就可以调用accept来接受一个外来的请求。关于accept更的信息,请接着关注本系统文章。

posted @ 2012-02-19 19:21 newlist 阅读(431) 评论(0) 编辑

摘要:在套接口中,一个套接字只是用户程序与内核交互信息的枢纽,它自身没有太多的信息,也没有网络协议地址和端口号等信息,在进行网络通信的时候,必须把一个套接字与一个地址相关联,这个过程就是地址绑定的过程。许多时候内核会我们自动绑定一个地址,然而有时用户可能需要自己来完成这个绑定的过程,以满足实际应用的需要,最典型的情况是一个服务器进程需要绑定一个众所周知的地址或端口以等待客户来连接。这个事由bind的函数完成。

从bind函数功能我们很容易推测出这个函数的需要的参数与相应的返回值,如果此时大家已经对socket接口有点熟悉了:

#include<sys/socket.h>int bind(int sockfd, struct sockaddr* addr, socklen_t addrlen)
返回:0──成功, -1──失败

 

参数sockfd
指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。
参数addr
指定地址。这是一个地址结构,并且是一个已经经过填写的有效的地址结构。调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果。
参数addrlen
正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。这已经成为socket接口中最常见的参数之一了。

 

bind函数并不是总是需要调用的,只有用户进程想与一个具体的地址或端口相关联的时候才需要调用这个函数。如果用户进程没有这个需要,那么程序可以依赖内核的自动的选址机制来完成自动地址选择,而不需要调用bind的函数,同时也避免不必要的复杂度。在一般情况下,对于服务器进程问题需要调用bind函数,对于客户进程则不需要调用bind函数。

http://www.cnblogs.com/wangchenxicool/articles/2165346.html

posted @ 2012-02-19 17:28 newlist 阅读(102) 评论(0) 编辑

SOCKET socket( int af,int type,int protocol );

 

  应用程序调用socket函数来创建一个能够进行网络通信的套接字。

 

  第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;

 

  第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);

 

  第三个参数指定应用程序所使用的通信协议。

 

  该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。

 

  下面是一个创建流套接字的例子:

 

  struct protoent *ppe;

 

  ppe=getprotobyname("tcp");

 

  SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);

posted @ 2012-02-19 17:27 newlist 阅读(23) 评论(0) 编辑

2012年2月11日 #

线程终止时的清理

不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

在POSIX线程API中提供了一个pthread_cleanup_push()/pthread_cleanup_pop()函数对用于自动释放资源--从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。API定义如下:

 

void pthread_cleanup_push(void (*routine) (void *), void *arg)void pthread_cleanup_pop(int execute)

 

pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push()的调用将在清理函数栈中形成一个函数链,在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

 

#define pthread_cleanup_push(routine,arg) / { struct _pthread_cleanup_buffer _buffer; / _pthread_cleanup_push (&_buffer, (routine), (arg));#define pthread_cleanup_pop(execute) / _pthread_cleanup_pop (&_buffer, (execute)); }

 

可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。

 

pthread_cleanup_push(pthread_mutex_unlock, (void *) &mut);pthread_mutex_lock(&mut);/* do some work */pthread_mutex_unlock(&mut);pthread_cleanup_pop(0);

 

必须要注意的是,如果线程处于PTHREAD_CANCEL_ASYNCHRONOUS状态,上述代码段就有可能出错,因为CANCEL事件有可能在pthread_cleanup_push()和pthread_mutex_lock()之间发生,或者在pthread_mutex_unlock()和pthread_cleanup_pop()之间发生,从而导致清理函数unlock一个并没有加锁的mutex变量,造成错误。因此,在使用清理函数的时候,都应该暂时设置成PTHREAD_CANCEL_DEFERRED模式。为此,POSIX的Linux实现中还提供了一对不保证可移植的pthread_cleanup_push_defer_np()/pthread_cleanup_pop_defer_np()扩展函数,功能与以下代码段相当:

 

{ int oldtype; pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &oldtype); pthread_cleanup_push(routine, arg); ... pthread_cleanup_pop(execute); pthread_setcanceltype(oldtype, NULL); }

 

 

线程终止的同步及其返回值

一般情况下,进程中各个线程的运行都是相互独立的,线程的终止并不会通知,也不会影响其他线程,终止的线程所占用的资源也并不会随着线程的终止而得到释放。正如进程之间可以用wait()系统调用来同步终止并释放资源一样,线程之间也有类似机制,那就是pthread_join()函数。

 

void pthread_exit(void *retval) int pthread_join(pthread_t th, void **thread_return)int pthread_detach(pthread_t th)

 

pthread_join()的调用者将挂起并等待th线程终止,retval是pthread_exit()调用者线程(线程ID为th)的返回值,如果thread_return不为NULL,则*thread_return=retval。需要注意的是一个线程仅允许唯一的一个线程使用pthread_join()等待它的终止,并且被等待的线程应该处于可join状态,即非DETACHED状态。

如果进程中的某个线程执行了pthread_detach(th),则th线程将处于DETACHED状态,这使得th线程在结束运行时自行释放所占用的内存资源,同时也无法由pthread_join()同步,pthread_detach()执行之后,对th请求pthread_join()将返回错误。

一个可join的线程所占用的内存仅当有线程对其执行了pthread_join()后才会释放,因此为了避免内存泄漏,所有线程的终止,要么已设为DETACHED,要么就需要使用pthread_join()来回收。

 

关于pthread_exit()和return

理论上说,pthread_exit()和线程宿体函数退出的功能是相同的,函数结束时会在内部自动调用pthread_exit()来清理线程相关的资源。但实际上二者由于编译器的处理有很大的不同。

在进程主函数(main())中调用pthread_exit(),只会使主函数所在的线程(可以说是进程的主线程)退出;而如果是return,编译器将使其调用进程退出的代码(如_exit()),从而导致进程及其所有线程结束运行。

其次,在线程宿主函数中主动调用return,如果return语句包含在pthread_cleanup_push()/pthread_cleanup_pop()对中,则不会引起清理函数的执行,反而会导致segment fault。

http://blog.csdn.net/newnewman80/article/details/6276788

posted @ 2012-02-11 03:10 newlist 阅读(35) 评论(0) 编辑

与互斥锁不同,条件变量是用来等待而不是用来上锁的。条件变量用来自动阻塞一个线程,直到某特殊情况发生为止通常条件变量和互斥锁同时使用。

条件变量使我们可以睡眠等待某种条件出现。条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。

条件的检测是在互斥锁的保护下进行的。如果一个条件为假,一个线程自动阻塞,并释放等待状态改变的互斥锁。如果另一个线程改变了条件,它发信号给关联的条件变量,唤醒一个或多个等待它的线程,重新获得互斥锁,重新评价条件。如果两进程共享可读写的内存,条件变量可以被用来实现这两进程间的线程同步。

使用条件变量之前要先进行初始化。可以在单个语句中生成和初始化一个条件变量如:pthread_cond_t my_condition=PTHREAD_COND_INITIALIZER;(用于进程间线程的通信)。可以利用函数pthread_cond_init动态初始化。

条件变量分为两部分: 条件和变量. 条件本身是由互斥量保护的. 线程在改变条件状态前先要锁住互斥量. 它利用线程间共享的全局变量进行同步的一种机制。

相关的函数如下:

1 intpthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr);    2 intpthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex); 3 intpthread_cond_timewait(pthread_cond_t*cond,pthread_mutex*mutex,consttimespec*abstime); 4 intpthread_cond_destroy(pthread_cond_t*cond); 5 intpthread_cond_signal(pthread_cond_t*cond); 6 intpthread_cond_broadcast(pthread_cond_t*cond); //解除所有线程的阻塞

简要说明:     

      (1)初始化.init()或者pthread_cond_t cond=PTHREAD_COND_INITIALIER;属性置为NULL
      (2)等待条件成立.pthread_wait,pthread_timewait.wait()释放锁,并阻塞等待条件变量为真
      timewait()设置等待时间,仍未signal,返回ETIMEOUT(加锁保证只有一个线程wait)
      (3)激活条件变量:pthread_cond_signal,pthread_cond_broadcast(激活所有等待线程)
      (4)清除条件变量:destroy;无线程等待,否则返回EBUSY
 

详细说明

1. 初始化:

    条件变量采用的数据类型是pthread_cond_t, 在使用之前必须要进行初始化, 这包括两种方式:

  • 静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.
  • 动态: pthread_cond_init函数, 是释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.

#include <pthread.h> int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr); int pthread_cond_destroy(pthread_cond_t *cond); 成功则返回0, 出错则返回错误编号.

    当pthread_cond_init的attr参数为NULL时, 会创建一个默认属性的条件变量; 非默认情况以后讨论.

2. 等待条件:

#include <pthread.h> int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex); int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout); 成功则返回0, 出错则返回错误编号.

    这两个函数分别是阻塞等待和超时等待.

    等待条件函数等待条件变为真, 传递给pthread_cond_wait的互斥量对条件进行保护, 调用者把锁住的互斥量传递给函数. 函数把调用线程放到等待条件的线程列表上, 然后对互斥量解锁, 这两个操作是原子的. 这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化.

    当pthread_cond_wait返回时, 互斥量再次被锁住.

3. 通知条件:

#include <pthread.h> int pthread_cond_signal(pthread_cond_t *cond); int pthread_cond_broadcast(pthread_cond_t *cond); 成功则返回0, 出错则返回错误编号.

    这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.

示例程序

#include <stdio.h>
#include <pthread.h>
 pthread_mutex_t mutex;
pthread_cond_t cond;
void *thread1(void *arg) 
{
	
pthread_cleanup_push(pthread_mutex_unlock, &mutex);
	
	    //提供函数回调保护
	    while (1) {
		
printf("thread1 is running\n");
		
pthread_mutex_lock(&mutex);
		
pthread_cond_wait(&cond, &mutex);
		
printf("thread1 applied the condition\n");
		
pthread_mutex_unlock(&mutex);
		
sleep(4);
	
}
	
pthread_cleanup_pop(0);

}


void *thread2(void *arg) 
{
	
while (1) {
		
printf("thread2 is running\n");
		
pthread_mutex_lock(&mutex);
		
pthread_cond_wait(&cond, &mutex);
		
printf("thread2 applied the condition\n");
		
pthread_mutex_unlock(&mutex);
		
sleep(1);
	
}

}

int main() 
{
	
pthread_t thid1, thid2;
	
printf("condition variable study!\n");
	
pthread_mutex_init(&mutex, NULL);
	
pthread_cond_init(&cond, NULL);
	
pthread_create(&thid1, NULL, (void *) thread1, NULL);
	
pthread_create(&thid2, NULL, (void *) thread2, NULL);
	
	do {
		
pthread_cond_signal(&cond);
	
} while (1);
	
sleep(20);
	
pthread_exit(0);
	
return 0;

}

 

条件变量与互斥锁、信号量的区别

       1.互斥锁必须总是由给它上锁的线程解锁,信号量的挂出即不必由执行过它的等待操作的同一进程执行。一个线程可以等待某个给定信号灯,而另一个线程可以挂出该信号灯。

       2.互斥锁要么锁住,要么被解开(二值状态,类型二值信号量)。

       3.由于信号量有一个与之关联的状态(它的计数值),信号量挂出操作总是被记住。然而当向一个条件变量发送信号时,如果没有线程等待在该条件变量上,那么该信号将丢失。

       4.互斥锁是为了上锁而设计的,条件变量是为了等待而设计的,信号灯即可用于上锁,也可用于等待,因而可能导致更多的开销和更高的复杂性。

参考:http://blog.csdn.net/dai_weitao/archive/2007/08/22/1754964.aspx

posted @ 2012-02-11 03:04 newlist 阅读(334) 评论(0) 编辑

2012年2月10日 #

  由于Linux Access函数只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve执行时则会失败。     Linux Access函数(判断是否具有存取文件的权限)     相关函数stat,open,chmod,chown,setuid,setgid表头文件#include定义函数int access;Linux Access函数说明access会检查是否可以读/写某一已存在的文件。参数mode有几种情况组合, R_OK,W_OK,X_OK 和F_OK.R_OK,W_OK与X_OK用来检查文件是否具有读取、写入和执行的权限。F_OK则是用来判断该文件是否存在。由于access只作权限的核查,并不理会文件形态或文件内容,因此,如果一目录表示为“可写入”,表示可以在该目录中建立新文件等操作,而非意味此目录可以被当做文件处理。例如,你会发现DOS的文件都具有“可执行”权限,但用execve执行时则会失败。     Linux Access函数返回值若所有欲查核的权限都通过了检查则返回0值,表示成功,只要有一权限被禁止则返回-1.错误代码EACCESS 参数pathname 所指定的文件不符合所要求测试的权限。     EROFS 欲测试写入权限的文件存在于只读文件系统内。     EFAULT 参数pathname指针超出可存取内存空间。     EINVAL 参数mode 不正确。     ENAMETOOLONG 参数pathname太长。     ENOTDIR 参数pathname为一目录。     ENOMEM 核心内存不足ELOOP 参数pathname有过多符号连接问题。     EIO I/O 存取错误。     Linux Access函数附加说明使用access作用户认证方面的判断要特别小心,例如在access后再做open的空文件可能会造成系统安全上的问题。     范例/ 判断是否允许读取/etc/passwd / #include int main     执行/etc/passwd can be read

原文出自【比特网】,转载请保留原文链接:http://soft.chinabyte.com/os/362/12187862.shtml

posted @ 2012-02-10 00:44 newlist 阅读(93) 评论(0) 编辑

2012年2月9日 #

#include <signal.h>

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_restorer)(void); };

通过sa_mask设置信号掩码集。

信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。

sa_falgs还可以设置其他标志:

SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL

SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用

SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号

#include<stdio.h> #include<signal.h> #include<stdlib.h> #include<string.h> #define INPUTLEN 100 void inthandler(int); int main(){     struct sigaction newhandler;     sigset_t blocked;       //被阻塞的信号集    char x[INPUTLEN];     newhandler.sa_flags=SA_RESETHAND;     newhandler.sa_handler=inthandler;     sigemptyset(&blocked);      //清空信号处理掩码    sigaddset(&blocked,SIGQUIT);     newhandler.sa_mask=blocked;     if(sigaction(SIGINT,&newhandler,NULL)==-1)         perror("sigaction");     else         while(1){             fgets(x,INPUTLEN,stdin);        //fgets()会在数据的最后附加"\0"            printf("input:%s",x);         } } void inthandler(int signum){     printf("Called with signal %d\n",signum);     sleep(signum);     printf("done handling signal %d\n",signum); }

Ctrl-C向进程发送SIGINT信号,Ctrl-\向进程发送SIGQUIT信号。

$ ./sigactdemo ^CCalled with signal 2 ^\done handling signal 2 Quit (core dumped)

由于把SIGQUIT加入了信号掩码集,所以处理信号SIGINT时把SIGQUIT屏蔽了。当处理完SIGINT后,内核才向进程发送SIGQUIT信号。

$ ./sigactdemo ^CCalled with signal 2 ^Cdone handling signal 2

由于设置了SA_RESETHAND,第一次执行SIGINT的处理函数时相当于执行了signal(SIGINT,SIG_DFL),所以进程第二次收到SIGINT信号后就执行默认操作,即挂起进程。

修改代码,同时设置SA_RESETHAND和SA_NODEFER。

newhandler.sa_flags=SA_RESETHAND|SA_NODEFER;

$ ./sigactdemo ^CCalled with signal 2 ^C

在没有设置SA_NODEFER时,在处理SIGINT信号时,自动屏幕SIGINT信号。现在设置了SA_NODEFER,则当SIGINT第二次到来时立即响应,即挂起了进程。

http://www.cnblogs.com/zhangchaoyang/articles/2297536.html

posted @ 2012-02-09 09:40 newlist 阅读(8) 评论(0) 编辑

alarm(设置信号传送闹钟) 相关函数 signal,sleep

表头文件 #include<unistd.h>

定义函数 unsigned int alarm(unsigned int seconds);

函数说明 alarm()用来设置信号SIGALRM在经过参数seconds指定的秒数后传送给目前的进程。如果参数seconds 为0,则之前设置的闹钟会被取消,并将剩下的时间返回。

返回值返回之前闹钟的剩余秒数,如果之前未设闹钟则返回0。

范例 #include<unistd.h> #include<signal.h> void handler() { printf("hello\n"); } main() { int i; signal(SIGALRM,handler); alarm(5); for(i=1;i<7;i++){ printf("sleep %d ...\n",i); sleep(1); } }

执行 sleep 1 ... sleep 2 ... sleep 3 ... sleep 4 ... sleep 5 ... hello sleep 6 ...

 

 

kill(传送信号给指定的进程) 相关函数 raise,signal

表头文件 #include<sys/types.h> #include<signal.h>

定义函数 int kill(pid_t pid,int sig);

函数说明 kill()可以用来送参数sig指定的信号给参数pid指定的进程。参数pid有几种情况: pid>0 将信号传给进程识别码为pid 的进程。 pid=0 将信号传给和目前进程相同进程组的所有进程 pid=-1 将信号广播传送给系统内所有的进程 pid<0 将信号传给进程组识别码为pid绝对值的所有进程 参数sig代表的信号编号可参考附录D

返回值 执行成功则返回0,如果有错误则返回-1。

错误代码 EINVAL 参数sig 不合法 ESRCH 参数pid 所指定的进程或进程组不存在 EPERM 权限不够无法传送信号给指定进程

范例 #include<unistd.h> #include<signal.h> #include<sys/types.h> #include<sys/wait.h> main() { pid_t pid; int status; if(!(pid= fork())){ printf("Hi I am child process!\n"); sleep(10); return; } else{ printf("send signal to child process (%d) \n",pid); sleep(1); kill(pid ,SIGABRT); wait(&status); if(WIFSIGNALED(status)) printf("chile process receive signal %d\n",WTERMSIG(status)); } }

执行 sen signal to child process(3170) Hi I am child process! child process receive signal 6

 

 

pause(让进程暂停直到信号出现) 相关函数 kill,signal,sleep

表头文件 #include<unistd.h>

定义函数 int pause(void);

函数说明 pause()会令目前的进程暂停(进入睡眠状态),直到被信号(signal)所中断。

返回值 只返回-1。

错误代码 EINTR 有信号到达中断了此函数。

http://www.cnblogs.com/taobataoma/archive/2007/08/30/875662.html

posted @ 2012-02-09 09:39 newlist 阅读(13) 评论(0) 编辑

仅列出标题  下一页