通信信号
信号:一种异步通信机制
系统支持的信号都有默认的处理方式
常用的信号及其处理:SIGINT | SIGQUIT | SIGALRM | SIGCHLD |
常用信号及其处理方式:默认处理,忽略,自定义信号处理函数
注意:信号处理函数中不要做耗时的操作
注册信号处理函数:signal , sigaction
发送信号:kill , 组合键
线程创建和调度
线程是程序执行的某一条指令流的影像
线程函数:用于提供线程执行的指令(代码)
线程库:pthread
线程的创建:pthread_create
线程等待获取线程返回值:pthread_join
获取线程自身的线程ID:pthread_self
判断是否是同一个线程:pthread_equal
取消指定线程:pthread_cancel
2)进程通信
进程通信是进程之间相互通信或操作的一种方式
信号机制是异步的
信号依靠值来区分
2.1)当信号发生时,用户可以要求进程以下列3种方式之一对信号做出响应。
1、 捕捉信号:对于要捕捉的信号,可以为其指定信号处理函数,信号发生时该函数自动被调用,在该函数内部实现对该信号的处理。
2、 忽略信号:大多数信号都可使用这种方式进行处理,但是SIGKILL和SIGSTOP这两个信号不能被忽略,同时这两个信号也不能被捕获和阻塞。此外,如果忽略某某些由硬件异常产生的信号(如非法存储访问或除以0),则进程的行为是不可预测的。
3、 按照系统默认方式处理。大部分信号的默认操作是终止进程,且所有的实时信号的默认动作都是终止进程。
2.2)信号的优先级
信号实质上是软中断,中断有优先级,信号也有优先级。如果一个进程有多个未决信号,则对于同一个未决的实时信号,内核将按照发送的顺序来递送信号。如果存在多个未决信号,则值(或者说编号)越小的越先被递送。如果即存在不可靠信号,又存在可靠信号(实时信号),虽然POSIX对这一情况没有明确规定,但Linux系统和大多数遵循POSIX标准的操作系统一样,将优先递送不可靠信号。
2.3)SIG信号
信号的值定义在signal.h中,在Linux中没有16和32这两个信号。上面信号的含义如下:
(1) SIGHUP:当用户退出Shell时,由该Shell启的发所有进程都退接收到这个信号,默认动作为终止进程。
(2) SIGINT:用户按下组合键时,用户端时向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
(3) SIGQUIT:当用户按下组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程并产生core文件。
(4) SIGILL :CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件。
(5) SIGTRAP:该信号由断点指令或其他trap指令产生。默认动作为终止进程并产生core文件。
(6) SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
(7) SIGBUS:非法访问内存地址,包括内存地址对齐(alignment)出错,默认动作为终止进程并产生core文件。
(8) SIGFPE:在发生致命的算术错误时产生。不仅包括浮点运行错误,还包括溢出及除数为0等所有的算术错误。默认动作为终止进程并产生core文件。
(9) SIGKILL:无条件终止进程。本信号不能被忽略、处理和阻塞。默认动作为终止进程。它向系统管理员提供了一种可以杀死任何进程的方法。
(10) SIGUSR1:用户定义的信号,即程序可以在程序中定义并使用该信号。默认动作为终止进程。
(11) SIGSEGV:指示进程进行了无效的内存访问。默认动作为终止进程并使用该信号。默认动作为终止进程。
(12) SIGUSR2:这是另外一个用户定义信号,程序员可以在程序中定义并使用该信号。默认动作为终止进程。
(13) SIGPIPE:Broken pipe:向一个没有读端的管道写数据。默认动作为终止进程。
(14) SIGALRM:定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
(15) SIGTERM:程序结束(terminate)信号,与SIGKILL不同的是,该信号可以被阻塞和处理。通常用来要求程序正常退出。执行Shell命令kill时,缺少产生这个信号。默认动作为终止进程。
(16) SIGCHLD:子程序结束时,父进程会收到这个信号。默认动作为忽略该信号。
(17) SIGCONT:让一个暂停的进程继续执行。
(18) SIGSTOP:停止(stopped)进程的执行。注意它和SIGTERM以及SIGINT的区别:该进程还未结束,只是暂停执行。本信号不能被忽略、处理和阻塞。默认作为暂停进程。
(19) SIGTSTP:停止进程的动作,但该信号可以被处理和忽略。按下组合键时发出该信号。默认动作为暂停进程。
(20) SIGTTIN:当后台进程要从用户终端读数据时,该终端中的所有进程会收到SIGTTIN信号。默认动作为暂停进程。
(21) SIGTTOU:该信号类似于SIGTIN,在后台进程要向终端输出数据时产生。默认动作为暂停进程。
(22) SIGURG:套接字(socket)上有紧急数据时,向当前正在运行的进程发出此信号,报告有紧急数据到达。默认动作为忽略该信号。
(23) SIGXCPU:进程执行时间超过了分配给该进程的CPU时间,系统产生该信号并发送给该进程。默认动作为终止进程。
(24) SIGXFSZ:超过文件最大长度的限制。默认动作为yl终止进程并产生core文件。
(25) SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是它只计算该进程占有用的CPU时间。默认动作为终止进程。
(26) SIGPROF:类似于SIGVTALRM,它不仅包括该进程占用的CPU时间还抱括执行系统调用的时间。默认动作为终止进程。
(27) SIGWINCH:窗口大小改变时发出。默认动作为忽略该信号。
(28) SIGIO:此信号向进程指示发出一个异步IO事件。默认动作为忽略。
(29) SIGPWR:关机。默认动作为终止进程。
(30) SIGRTMIN~SIGRTMAX:Linux的实时信号,它没有固定的含义(或者说可以由用户自由使用)。注意,Linux线程机制使用了前3个实时信号。所有的实时信号的默认动作都是终止进程
3)线程
了解 C10K问题
一条进程带一万条线程连接
解决方案
解决这一问题,主要思路有两个:
每个进程/线程处理一个连接
每个进程/线程同时处理多个连接(IO多路复用)
这些操作系统提供的功能就是为了解决C10K问题:FreeBSD推出了kqueue,Linux推出了epoll,Windows推出了IOCP,Solaris推出了/dev/poll。
了解两个网站
stackoverflow
segmentfault
进程控制块 PCB区
栈区
堆区(堆区和栈区相向增长)
数据区(全局变量)(多线程共享数据区)
文本区
4)pthread族函数
4.1)创建线程
pthread_create
pthread头文件
#include <pthread.h>
参数
int pthread_create ( pthread_t *thread, const pthread_attr_t *attr,void (start_routine) (void *), void *arg );
Compile and link with -pthread.
编译时加上 -lpthread 或 -pthread
4.2)pthread_join
int pthread_join(pthread_t thread, void **retval);
//函数pthread_join用来等待一个线程的结束,线程间同步的操作
4.3)pthread_exit
void pthread_exit(void *retval);
//线程通过调用pthread_exit函数终止执行,就如同进程在结束时调用exit函数一样。这个函数的作用是,终止调用它的线程并返回一个指向某个对象的指针。
4.4)pthread_cancel
是计算机语言,它发送终止信号给thread线程,如果成功则返回0,否则为非0值
4.6)pthread_self
pthread_self 是一种函数,功能是获得线程自身的ID
4.7)pthread_cond_wait
条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起
程序
信号处理函数
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<time.h>
void sig_handle(int signo)
{
switch(signo)
{
case SIGINT:
printf("%d",getpid());
break;
}
}
int main (int argc ,char *argv[])
{
pid_t pid;
signal(SIGINT , SIG_ING);//忽略信号
signal(SIGINT, sig_handle);//显示信号
signal(SIGINT, SIG_DFT);//默认信号
pid = fork() ;
while(1)
{
sleep(2);
printf("befor \n");
}
return 0 ;
}
父子进程信号通讯
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
//练习2:创建一个子进程,子进程进入一个无限的循环,模拟一秒从数据中心取一个数据并处理的
//过程,直到子进程收到父进程发送过来的SIGUSR1信号,打印子进程pid,并退出
//父进程:创建子进程之后,循环等待信号的到达,当收到SIGINT(由组合键ctrl+c模拟)信号
//之后,向子进程发送一个SIGUSR1信号
pid_t pid;
void parent_sig_handler(int signo);
void child_sig_handler(int signo);
int main (int argc , char *argv[])
{
signal(SIGINT , parent_sig_handler);//显示父程序的信号
if((pid = fork()) == 0)
{
while(1)
{
int client_id = 0;
signal(SIGINT,SIG_IGN); //忽略信号
signal(SIGINT ,child_sig_handler ); //显示子程序的信号
while(1)
{
client_id=rand()%100+1;
printf("client in %d\n",client_id);
sleep(1);
printf("client out %d\n",client_id);
}
}
}
else if(pid > 0)
{
while(1);
}
return 0;
}
//父程序
void parent_sig_handler(int signo)
{
switch(signo)
{
case SIGCHLD:
wait(NULL);
break;
case SIGINT :
//kill函数向指定 进程发送指定信号值
//注意,信号发送无法传递数据
kill (pid, SIGUSR1);
break;
default:
break;
}
}
//子程序
void child_sig_handler(int signo)
{
if(signo == SIGUSR1)
{
printf(child pid"%d\n",getpid() );
exit(0);
}
}