6.Linux应用编程——管道、信号

来源:互联网 发布:sql语句insert 编辑:程序博客网 时间:2024/04/30 03:36
进程间通信(IPC):
常用的进程间通信方式(7种):
传统的进程间通信方式:无名管道(pipe)、有名管道(fifo)和信号(signal)
System V IPC对象:System V共享内存(share memory)、System V消息队列(message queue)、System V信号灯(semaphore)
BSD:套接字(socket)

管道概念:管道是Unix中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程
的一个数据流称为一个“管道”, 管道的本质是固定大小的内核缓冲区。


1.无名管道(pipe)
  无名管道:半双工通信(一端作为读,一端肯定作为写);
  特点:只能用于具有共同祖先的进程(如父进程与fork出的子进程)之间进行通信, 原因是
  pipe创建的是两个文件描述符,不同进程之间无法直接获得;通常,一个管道由一个进程创
  建,然后该进程调用fork,此后父子进程共享该管道.
  注:不能使用lseek
  无名管道函数:pipe
  描述:创建一无名管道
  int pipe(int pipefd[2]);
  参数:
  Pipefd:文件描述符数组,其中pipefd[0]表示读端,pipefd[1]表示写端


2.有名管道(fifo)
  有名管道:用于非亲缘关系进程(不相关的进程之间)
  半双工
  单独执行读断或者写端都会阻塞
命令行创建:
  $ mkfifo <filename>
      myfifo
程序中创建:
  mkfifo:创建有名管道文件
  原型:int mkfifo(const char *pathname, mode_t mode);
  参数:1、创建文件路径名  2、文件权限
  示例:mkfifo("./1.txt", 0644);
    if(mkfifo(“./1.txt”,0644)==-1)
    {
    if(errno==EEXIST)
    {
    fd=open(“./1.txt”,);
    }
    else
    {
    perror();
    }
    }




注:要区分信号量和信号。
信号量如下
posix——信号量(常用于线程间通信)
sem_init();
sem_wait();
sem_post();这几个函数主要是用于线程间通信,使用到的机制叫做信号量(用来描述资源)
注:要区分信号量和信号。


System V——信号量(又叫信号灯,常用于进程间通信)
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, ...);//控制信号灯集


信号(signal)如下
3.信号通信(signal):
信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式(把一种不确定什么时候发生的方式称为异步)


中断:执行一个任务的过程中被其他的事件或者信号打断,然后去执行其他的事件或者信号,执行完成后回到第一个任务继续执行。
异步:不确定什么时候发生的事件叫做异步事件


信号分类:不可靠信号(1-31)和可靠信号(34-64)
kill -l 查看信号     总共有62个信号
SIGKILL()和SIGSTOP()不能够捕捉、阻塞和忽略


所谓的可靠信号代表不能造成信号丢失


kill就是一个注册的过程
例子:kill -9 PID 就是给PID注册9信号


信号的执行过程:
产生:由函数或者软件产生、由硬件产生
注册:
注销并且执行:
注:重点了解注册


对信号的操作方式:
缺省操作(默认操作)、捕捉(修改默认操作)、忽略
SIGKILL和SIGSTOP不能够捕捉、阻塞和忽略
常用信号:
Linux的信号的种类有60多种。可以用kill -l命令查看所有的信号,每个信号的含义如下:
1 SIGHUP:当用户退出shell时,由该shell启动的所有进程将收到这个信号,默认动作为终止进程
2 SIGINT:当用户按下了<Ctrl+C>组合键时,用户终端向正在运行中的由该终端启动的程序发出此信号。默认动作为终止进程。
3 SIGQUIT:当用户按下<ctrl+\>组合键时产生该信号,用户终端向正在运行中的由该终端启动的程序发出些信号。默认动作为终止进程。
4 SIGILL:CPU检测到某进程执行了非法指令。默认动作为终止进程并产生core文件
5 SIGTRAP:该信号由断点指令或其他 trap指令产生。默认动作为终止里程 并产生core文件。
6 SIGABRT:调用abort函数时产生该信号。默认动作为终止进程并产生core文件。
7 SIGBUS:非法访问内存地址,包括内存对齐出错,默认动作为终止进程并产生core文件。
8 SIGFPE:在发生致命的运算错误时发出。不仅包括浮点运算错误,还包括溢出及除数为0等所有的算法错误。默认动作为终止进程并产生core文件。
9 SIGKILL:无条件终止进程。本信号不能被忽略,处理和阻塞。默认动作为终止进程。它向系统管理员提供了可以杀死任何进程的方法。
10 SIGUSE1:用户定义的信号。即程序员可以在程序中定义并使用该信号。默认动作为终止进程。
11 SIGSEGV:指示进程进行了无数内存访问。默认动作为终止进程并产生core文件。
12 SIGUSR2:这是另外一个用户自定义信号 ,程序员可以在程序中定义并使用该信号。默认动作为终止进程。1
13 SIGPIPE:Broken pipe向一个没有读端的管道写数据。默认动作为终止进程。
14 SIGALRM:定时器超时,超时的时间由系统调用alarm设置。默认动作为终止进程。
15 SIGTERM:程序结束信号,与SIGKILL不同的是,该信号可以被阻塞和终止。通常用来要示程序正常退出。执行shell命令Kill时,缺省产生这个信号。默认动作为终止进程。
16 SIGCHLD:子进程结束时,父进程会收到这个信号。默认动作为忽略这个信号。
17 SIGCONT:停止进程的执行。信号不能被忽略,处理和阻塞。默认动作为终止进程。
18 SIGTTIN:停止进程的运行,但该信号可以被处理和忽略。按下<ctrl+z>组合键发出灾个信号。默认动作为暂停进程。
19 SIGTSTP:停止进程的运行,可该信号可以被处理可忽略。按下<ctrl+z>组合键时发出这个信号。默认动作为暂停进程。
21 SIGTTOU:该信号类似于SIGTTIN,在后台进程要向终端输出数据时发生。默认动作为暂停进程。
22 SIGURG:套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。默认动作为忽略该信号。
23 SIGXFSZ:进程执行时间超过了分配给该进程的CPU时间 ,系统产生该信号并发送给该进程。默认动作为终止进程。
24 SIGXFSZ:超过文件的最大长度设置。默认动作为终止进程。
25 SIGVTALRM:虚拟时钟超时时产生该信号。类似于SIGALRM,但是该信号只计算该进程占用CPU的使用时间。默认动作为终止进程。
26 SGIPROF:类似于SIGVTALRM,它不公包括该进程占用CPU时间还包括执行系统调用时间。默认动作为终止进程。
27 SIGWINCH:窗口变化大小时发出。默认动作为忽略该信号。
28 SIGIO:此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
29 SIGPWR:关机。默认动作为终止进程。
30 SIGSYS:无效的系统调用。默认动作为终止进程并产生core文件。
31 SIGRTMIN~(64)SIGRTMAX:LINUX的实时信号,它们没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程


在以上列出的信号中,程序不可捕获、阻塞或忽略的信号有:SIGKILL, SIGSTOP
不能恢复至默认动作的信号有:SIGILL, SIGTRAP
默认会导致进程流产的信号有:SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGIOT, SIGQUIT, SIGSEGV, SIGTRAP, SIGXCPU, SIGXFSZ
默认会导致进程退出的信号有:SIGALRM, SIGHUP, SIGINT, SIGKILL, SIGPIPE, SIGPOLL, SIGPROF, SIGSYS, SIGTERM, SIGUSR1, SIGUSR2, SIGVTALRM
默认会导致进程停止的信号有:SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU
默认进程忽略的信号有:SIGCHLD, SIGPWR, SIGURG, SIGWINCH


此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在进程挂起时是继续,否则是忽略,不能被阻塞。


信号(signal)API:
kill函数:给pid注册信号sig
头文件:#include <signal.h>
原型:int kill(pid_t pid, int sig);
例程:
#include<stdio.h>
#include<signal.h>
int main(int argc, const char *argv[])
{
pid_t pid;
pid = fork();
if(pid == -1)
{
}
else if(pid == 0)
{
kill(getppid(),SIGKILL);
}
else
{
sleep(1);
printf("hello\n");
}
while(1);
return 0;
}


signal函数:
描述:记录如果有中断产生我去哪执行
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
描述:如果添加函数就表示要修改操作,
比如说用KILL注册一个SIGINT给子进程,默认操作是ctrl+c(与kill -2有不同,这里忽略),若用signal给SIGINT信号,函数fun,则操作变为进入fun函数进行一些列操作。
例程:
#include<stdio.h>
#include<signal.h>
void handler(int signo)
{
if(signo == SIGINT)
printf("this is sigint\n");
if(signo == SIGKILL)
printf("this is sigkill\n");
}


void handlertstp(int signo)
{


}
int main(int argc, const char *argv[])
{
signal(SIGINT,handler);
signal(SIGKILL,handler);
signal(SIGQUIT,SIG_IGN);
signal(SIGTSTP,handlertstp);//中断函数内部什么内容都没有也是捕捉操作
printf("hello\n");
// while(1);
pause();//如果没有信号pause函数阻塞,如果有信号注册先去执行中断函数\
当中断函数执行完成pause函数唤醒.pause函数的唤醒是捕捉操作唤醒的
printf("end\n");
return 0;
}




进程由task_struct描述


pause函数:
描述:阻塞,等待唤醒,如果没有信号pause函数阻塞,如果有信号注册先去执行
中断函数,当中断函数执行完成pause函数唤醒,pause函数的唤醒是捕捉操作唤醒
的,忽略不唤醒。
头文件:#include <unistd.h>
函数原型:int pause(void);


中断函数内部谁没内容都没有也是捕捉操作。


实例:
创建子进程代表售票员,父进程代表司机 ,过程如下:


售票员捕捉SIGINT(代表开车),发SIGUSR1给司机,司机打印(“let’s gogogo”)
售票员捕捉SIGQUIT(代表停车),发SIGUSR2给司机,司机打印(“stop the bus”)
司机捕捉SIGTSTP(代表车到总站),发SIGUSR1给售票员,售票员打印(“please get off the bus”)
程序:
#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>


pid_t pid;


void father_handler(int signo)
{
if(signo == SIGUSR1)
printf("gogogo\n");
if(signo == SIGUSR2)
printf("stop the bus\n");
if(signo == SIGTSTP)
{
kill(pid,SIGUSR1);
wait(NULL);
exit(0);
}
}


void child_handler(int signo)
{
if(signo == SIGINT)
kill(getppid(),SIGUSR1);
if(signo == SIGQUIT)
kill(getppid(),SIGUSR2);
if(signo == SIGUSR1)
{
printf("get off the bus\n");
exit(0);
}
}


int main(int argc, const char *argv[])
{


pid = fork();


if(pid == -1)
{


}
else if(pid == 0)
{
signal(SIGINT,child_handler);
signal(SIGQUIT,child_handler);
signal(SIGUSR1,child_handler);
signal(SIGTSTP,SIG_IGN);
}
else
{
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);


signal(SIGUSR1,father_handler);
signal(SIGUSR2,father_handler);
signal(SIGTSTP,father_handler);
}
while(1)
{
pause();
}
return 0;
}



0 0