Shell脚本
TODO
系统调用
TODO
进程
TODO
信号
进程间通信概述
进程间通信(IPC):
进程是一个独立的资源分配单元,不同进程(这里所说的进程通常指的是用户进程)之间的资源是独立的, 没有 关联,不能在一个进程中直接访问另一个进程的资源(例如打开的文件描述符)。
进程不是孤立的,不同的进程需要进行信息的交互和状态的传递等,因此需要进程间通信。
进程间通信功能:
数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之间共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程
的所有操作,并能够及时知道它的状态改变。
Linux操作系统支持的主要进程间通信的通信机制

进程间通信的实质:
系统只要创建一个进程,就会给当前进程分配4G的虚拟内存(32位操作系统)。03G为用户空间,34G的内核空间,
用户空间是进程私有的,只能自己访问和使用。堆栈、数据区、代码区都是用户空间。
内核空间是所有进程公有。绝大多数进程间通信方式,是对内核空间操作
特殊的进程间通信方式:
信号通信
- 信号是软件中断,在软件层次上对中断机制的一种模拟。
- 信号是一种异步的通信机制,进程不必等待信号的到达、进程也不知道信号什么时候到达
- 信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以通过他来通知用户空间进程发生了哪些系统事件
- 每个信号的名字都以SIG开头
- 使用kill -l可以列出所有信号

产生信号的方式
用户按某些终端键时,将产生信号
例如:“Ctrl+C”
硬件异常
例如:无效的内存访问(除数为0)
软件异常
调用kill函数
运行kill命令
信号的默认处理方式
- 终止进程:当信号产生后,当前进程立即结束
- 缺省处理:当前进程不做任何处理
- 停止进程:当前进程停止
- 让停止进程恢复运行:当信号产生后,停止的进程恢复执行(后台进程)
PS:每一个信号只有一个默认的处理方式
进程收到信号的处理方式
- 执行系统默认动作
- 忽略此进程
- 执行自定义的信号处理函数
PS:SIGKILL和SIGSTOP这两个信号只能以默认的处理方式执行,不能忽略、自定义
常见信号
TODO
信号基本操作
kill函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| #include <sys/types.h> #include <signal.h>
int kill(pid_t pid, int signum);
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <sys/types.h>
int main(int argc, char **argv) { pid_t pid; pid = fork(); if(pid < 0) { perror("fork"); exit(1); } else if(pid > 0) { while(1) { printf("this is father proc\n"); sleep(1); } } else { printf("this is son proc\n"); sleep(3);
kill(getppid(), SIGINT); }
return 0; }
this is father proc this is son proc this is father proc this is father proc
|
alarm函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <unistd.h>
unsigned int alarm(unsigned int seconds);
#include <stdio.h> #include <unistd.h>
int main(int argc, char **argv) {
unsigned int sec;
sec = alarm(5); printf("sec = %d\n", sec);
sleep(3);
sec = alarm(6); printf("sec = %d\n", sec);
while (1) { printf("hello world\n"); sleep(1); }
return 0; }
sec = 0 sec = 2 hello world hello world hello world hello world hello world hello world Alarm clock
|
raise函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <signal.h>
int raise(int sig);
功能: 给调用进程本身发送信号 参数: sig: 指定的信号 返回值: 成功 0 失败 非0
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h>
int main(int argc, char **argv) {
int num = 0;
while (1) { printf("hello world\n"); sleep(1);
num++; if(num == 5) { raise(SIGINT); } }
return 0; }
hello world hello world hello world hello world hello world
|
abort函数
即使SIGABRT信号加入阻塞集,一旦进程调用了abort函数,进程还是会被终止,且在终止前会刷新缓冲区,关闭文件描述符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <stdlib.h>
void abort(void);
功能:向进程发送一个SIGABRT信号,默认情况下进程会退出 参数:无 返回值:无
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h>
int main(int argc, char **argv) {
int num = 0;
while (1) { printf("hello world\n"); sleep(1);
num++; if (num == 5) { abort(); } }
return 0; }
hello world hello world hello world hello world hello world Aborted
|
pause函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <unistd.h>
int pause(void); 功能: 将调用进程挂起直至捕捉到信号为止。这个函数通常用于判断信号是否已到。 返回值: 直到捕捉到信号,pause信号才返回-1,且errno被设置成EINTR
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h>
int main(int argc, char **argv) { pid_t pid; pid = fork(); if (pid < 0) { perror("fork"); exit(1); } else if (pid > 0) { printf("this is father proc\n"); pause(); } else { printf("this is son proc\n"); sleep(3);
kill(getppid(), SIGINT); }
return 0; }
this is father proc this is son proc
|
signal函数
程序中可用signal()改变信号的处理方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <signal.h>
typedef void (*sighandler_t)(int); sighandler_t signal(int signum,sighandler_t handler);
|
signal函数的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h>
void handler(int sig);
int main(int argc, char **argv) { #if 0 if (signal(SIGINT, SIG_DFL) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGTSTP, SIG_DFL) == SIG_ERR) { perror("fail to signal"); exit(1); } #endif
#if 0 if (signal(SIGINT, SIG_IGN) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGQUIT, SIG_IGN) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGTSTP, SIG_IGN) == SIG_ERR) { perror("fail to signal"); exit(1); } #endif
#if 1 if (signal(SIGINT, handler) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGQUIT, handler) == SIG_ERR) { perror("fail to signal"); exit(1); } if (signal(SIGTSTP, handler) == SIG_ERR) { perror("fail to signal"); exit(1); } #endif
while (1) { printf("hello world\n"); sleep(1); }
return 0; }
void handler(int sig) { if (sig == SIGINT) { printf("SIGINT正在处理\n"); } if (sig == SIGQUIT) { printf("SIGQUIT正在处理\n"); } if (sig == SIGTSTP) { printf("SIGTSTP正在处理\n"); } }
|
signal函数的返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <sys/types.h> #include <unistd.h>
void handler(int sig); void *ret_handler;
int main(int argc, char **argv) { if((ret_handler = signal(SIGINT,handler)) == SIG_ERR) { perror("fail to signal\n"); exit(1); }
while (1) { printf("hello world\n"); sleep(1); }
return 0; }
void handler(int sig) { printf("*****************\n"); printf("test\n"); printf("*****************\n");
if (signal(SIGINT, ret_handler) == SIG_ERR) { perror("fail to signal\n"); exit(1); } }
|
可重入函数
可重入函数是指函数可以由多个任务并发使用,而不必担心数据错误
可重入函数就是可以被中断的函数,当前函数可以在任何时刻中断它,并执行另一块代码。当执行完毕后,回到原本的代码还可以继续正常运行
编写可重入函数:
- 不使用(返回)静态的数据、全局变量(除非用信号量互斥)
- 不调用动态内存分配、释放函数
- 不调用任何不可重入的函数(如标准I/O函数)
即使信号处理函数使用的都是可重入函数(常见的可重入函数),也要注意进入处理函数时,首先要保存errno的值,结束时,在恢复原值。因为在信号处理过程中,errno值随时可能被改变
信号集
信号集概述
一个用户进程常常需要对多个信号做出处理。为了方便对多个信号进行处理,在Linux系统中引入了信号集。信号集是用来表示多个信号的数据结构。
信号集数据类型
sigset_t
管道、命名管道
无名管道概述
管道又叫无名管道
无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符
任何个进程创建的时候,系统都会给他分配4G的虚拟内存,分为3G用户空间和1G内核空间,内核空间是所有进程公有的,无名管道就是创建在内核空间的,多个进程知道同一个无名管道的空间,就可以利用他进行通信

无名管道虽然是在内核空间创建的,但是会给当前用户进程两个文件描述符,一个负责读操作,一个负责执行写操作。
管道特点:
- 半双工,数据在同一时刻只能在一个方向上流动。
- 数据只能从管道的一端写入,另一端读出。
- 写入管道中的数据符合先进先出的规则。
- 管道传送的数据是无格式的,这要求管道的双方约定好数据格式
- 管道不是普通文件,不属于某一个文件系统,只存在内存中