学习笔记9

linux 信号及处理过程

信号本质:

信号是软件中断,是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
其实,在头文件<signal.h>中,内核将信号都定义为正整数(信号编号)。

信号来源

信号事件的发生有两个来源。
1.硬件来源:

  • 用户按终端键,引起终端产生的信号(比如Ctrl + C键产生SIGINT)。

  • 硬件异常产生信号:除数为0、无效的内存引用等。这些条件通常由硬件检测到,并将其通知内核。然后内核为该条件发生时正在运行的进程产生相应的信号。
    2.软件来源:

  • 最常用发送信号的系统函数是kill, raise, alarm和setitimer以及sigqueue函数。

常见信号

Linux信号的编号是从1-64,其中32和33空缺,没有对应的信号。通过kill -l 可查看所有的信号。
学习笔记9

  • 1~31之间的信号叫做不可靠信号, 不支持排队, 信号可能会丢失, 也叫做非实时信号。
  • 34~64之间的信号叫做可靠信号, 支持排队, 信号不会丢失, 也叫做实时信号。

信号代码从1到32是不可靠信号,不可靠信号主要有以下问题:

(1)每次信号处理完之后,就会恢复成默认处理,这可能是调用者不希望看到的(早期的signal函数,linux2.6.35.6内核经验证已经不再恢复默认动作)。

(2)存在信号丢失的问题(进程收到的信号不作排队处理,相同的信号多次到来会合并为一个)。

现在的Linux对信号机制进行了改进,因此,不可靠信号主要是指信号丢失。

信号代码从SIGRTMIN到SIGRTMAX之间的信号是可靠信号。可靠信号不存在丢失,由sigqueue发送,可靠信号支持排队。

  • 可靠信号注册机制:

内核每收到一个可靠信号都会去注册这个信号,在信号的未决信号链中分配sigqueue结构,因此,不会存在信号丢失的问题。

  • 不可靠信号的注册机制:

而对于不可靠的信号,如果内核已经注册了这个信号,那么便不会再去注册,对于进程来说,便不会知道本次信号的发生。

可靠信号与不可靠信号与发送函数没有关系,取决于信号代码,前面的32种信号就是不可靠信号,而后面的32种信号就是可靠信号。

信号响应的方式

  • 忽略信号SIG_IGN ,但有两种信号不能被忽略SIGKILL,SIGSTOP。
  • 捕捉信号处理,即用户自定义的信号处理函数来处理。
  • 采用系统默认处理SIG_DFL,执行缺省操作。

信号的处理过程

信号产生->信号注册->信号在进程中注销->信号处理函数执行完毕

(1)信号的产生是指触发信号的事件的发生

(2)信号注册指的是在目标进程中注册,该目标进程中有未决信号的信息:

struct sigpending pending:
struct sigpending{undefined
struct sigqueue *head, **tail;
sigset_t signal;
};

struct sigqueue{undefined
struct sigqueue *next;
siginfo_t info;
}

其中 sigqueue结构组成的链称之为未决信号链,sigset_t称之为未决信号集。

*head,**tail分别指向未决信号链的头部与尾部。

siginfo_t info是信号所携带的信息。

信号注册的过程就是将信号值加入到未决信号集siginfo_t中,将信号所携带的信息加入到未决信号链的某一个sigqueue中去。

Linux IPC

管道(Pipe)

管道用来连接不同进程之间的数据流。
(1)在两个程序之间传递数据的最简单的方法是使用popen()和pclose()函数:

        #include <stdio.h>
        FILE *popen(const char *command, const char *open_mode);
        int pclose(FILE *stream);
popen()函数首先调用一个shell,然后把command作为参数传递给shell。这样每次调用popen()函数都需要启动两个进程;但是由于在Linux中,所有的参数扩展(parameter expansion)都是由shell执行的,这样command中包含的所有参数扩展都可以在command程序启动之前完成。

(2)pipe()函数:
        #include <unistd.h>
        int pipe(int pipefd[2]);
popen()函数只能返回一个管道描述符,并且返回的是文件流(file stream),可以使用函数fread()和fwrite()来访问。pipe()函数可以返回两个管道描述符:pipefd[0]和pipefd[1],任何写入pipefd[1]的数据都可以从pipefd[0]读回;pipe()函数返回的是文件描述符(file descriptor),因此只能使用底层的read()和write()系统调用来访问。pipe()函数通常用来实现父子进程之间的通信。

(3)命名管道:FIFO
        #include <sys/types.h>
        #include <sys/stat.h>
        int mkfifo(const char *fifo_name, mode_t mode);
前面两种管道只能用在相关的程序之间,使用命名管道可以解决这个问题。在使用open()打开FIFO时,mode中不能包含O_RDWR。mode最常用的是O_RDONLY,O_WRONLY与O_NONBLOCK的组合。O_NONBLOCK影响了read()和write()在FIFO上的执行方式。

System V IPC

System V IPC指的是AT&T在System V.2发行版中引入的三种进程间通信工具:(1)信号量,用来管理对共享资源的访问 (2)共享内存,用来高效地实现进程间的数据共享 (3)消息队列,用来实现进程间数据的传递。我们把这三种工具统称为System V IPC的对象,每个对象都具有一个唯一的IPC标识符(identifier)。要保证不同的进程能够获取同一个IPC对象,必须提供一个IPC关键字(IPC key),内核负责把IPC关键字转换成IPC标识符。   

System V IPC具有相似的语法,一般操作如下:

(1)选择IPC关键字,可以使用如下三种方式:

   a)IPC_PRIVATE。由内核负责选择一个关键字然后生成一个IPC对象并把IPC标识符直接传递给另一个进程。
   b)直接选择一个关键字。
   c)使用ftok()函数生成一个关键字。

(2)使用semget()/shmget()/msgget()函数根据IPC关键字key和一个标志flag创建或访问IPC对象。如果key是IPC_PRIVATE;或者key尚未与已经存在的IPC对象相关联且flag中包含IPC_CREAT标志,那么就会创建一个全新的IPC对象。

(3)使用semctl()/shmctl()/msgctl()函数修改IPC对象的属性。

(4)使用semctl()/shmctl()/msgctl()函数和IPC_RMID标志销毁IPC实例。

System V IPC为每个IPC对象设置了一个ipc_perm结构体并在创建IPC对象的时候进行初始化。这个结构体中定义了IPC对象的访问权限和所有者:

struct ipc_perm{
   uid_t uid;   //所有者的用户id
   gid_t gid;   //所有者的组id
   uid_t cuid;  //创建者的用户id
   gid_t cgid;  //创建者的组id
   mode_t mode; //访问模式
   …
};

shell中管理IPC对象的命令是ipcs、ipcmk和ipcrm。

Socket

  套接字(Socket)是由Berkeley在BSD系统中引入的一种基于连接的IPC,是对网络接口(硬件)和网络协议(软件)的抽象。它既解决了无名管道只能在相关进程间单向通信的问题,又解决了网络上不同主机之间无法通信的问题。

  套接字有三个属性:域(domain)、类型(type)和协议(protocol),对应于不同的域,套接字还有一个地址(address)来作为它的名字。

  域(domain)指定了套接字通信所用到的协议族,最常用的域是AF_INET,代表网络套接字,底层协议是IP协议。对于网络套接字,由于服务器端有可能会提供多种服务,客户端需要使用IP端口号来指定特定的服务。AF_UNIX代表本地套接字,使用Unix/Linux文件系统实现。

  IP协议提供了两种通信手段:流(streams)和数据报(datagrams),对应的套接字类型(type)分别为流式套接字和数据报套接字。流式套接字(SOCK_STREAM)用于提供面向连接、可靠的数据传输服务。该服务保证数据能够实现无差错、无重复发送,并按顺序接收。流式套接字使用TCP协议。数据报套接字(SOCK_DGRAM)提供了一种无连接的服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP协议。

代码练习

https://gitee.com/zhang_yu_peng/practice-code/blob/master/t.c
https://gitee.com/zhang_yu_peng/practice-code/blob/master/ts.s

上一篇:进程间通信(IPC)


下一篇:Linux_System V进程通信_共享内存+命令查看共享内存