首页 > 代码库 > Linux进程间的通信
Linux进程间的通信
一.进程与线程
- 进程是程序在某个数据集合上的一次运行活动,线程是进程中一个执行路径。进程有自己独立的地址空间,而线程没有,线程必须依赖进程而存在。
- 进程是系统资源分配的单位,线程是系统调度的单位
- 操作系统以进程为单位分配资源,进程中的每个线程共享进程的资源,每个线程有自己的程序计数器、栈。
二.进程启动的三种方式
- system("ps -ef"),使用shell启动一个新的进程
- exec族函数会用新进程代替原有的进程,系统会从新的进程运行,新的进程的PID值会与原来的进程的PID值相同。
- fork函数复制进程映像,创建一个新的进程,新进程的许多属性与当前进程是相同的,执行代码也完全相同,但新进程有自己的数据空间、环境、文件描述符。
三.孤儿进程和僵死进程
在linux中,子进程总是由父进程产生的,子进程和父进程是同时运行着的,父进程无法预知子进程什么时候结束,当一个进程完成它的工作后,父进程需要调用waitpid系统调用获取子进程的终止状态。孤儿进程指的是父进程已经运行结束了,而它的子进程还在运行,那么那些子进程将称为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。僵尸进程指的是一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程,进程处于僵死状态。很明显,如果进程不调用wait / waitpid的话,那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。有两种方法可以避免僵死进程,一种是子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号,在信号处理函数中调用wait进行处理僵尸进程;另外一种是创建进程的时候fork两次,原理是将子进程成为孤儿进程,从而其父进程变为init进程,通过init进程可以处理僵死进程。具体可以参考《孤儿进程和僵死进程总结》
四.进程与信号
信号提供了一种处理异步事件的方法。每个信号都有一个名字。这些名字都以三个字符SIG开头。在头文件<signal.h>中,这些信号都被定义为正整数(信号编号)。不存在编号为0的信号。(kill函数对信号编号0有特殊的应用)。信号是异步事件的经典实例。产生信号的事件对进程而言是随机出现的。进程不能简单地测试一个变量(例如errno)来判别是否出现了一个信号,而是必须告诉内核“在此信号出现时,请执行下列操作”。下面的实例是通过信号机制处理僵死进程,子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。
产生信号的方式:
- 当用户按某些终端键时,引发终端产生的信号。
- 硬件异常产生信号。
- 进程调用kill(2)函数可将信号发送给另一个进程或进程组。(接收信号进程和发送信号进程的所有者必须相同,或者发送信号进程的所有者必须是超级用户。)
- 用户可用kill(1)命令将信号发送给其他进程。
可以要求内核在某个信号出现时按照下列三种方式之一进行处理,我们称之为信号的处理或者与信号相关的动作。
- 忽略此信号。大多数信号都可使用这种方法进行处理,但是有两种信号决不能被忽略:SIGKILL和SIGSTOP。这两种信号不能被忽略的原因是:它们向超级用户提供了使进程终止或停止的可靠方法。另外,如果忽略某些由硬件异常产生的信号(例如除以0),则进程的运行行为是未定义的。
- 捕捉信号。为了做到这一点,要通知内核在某种信号发生时调用一个用户函数。在用户函数中,可执行用户希望对这种事件进行的处理。注意,不能捕捉SIGKILL和SIGSTOP信号。
- 执行系统默认动作。注意,针对大多数信号的系统默认动作是终止
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <errno.h> 4 #include <stdlib.h> 5 #include <signal.h> 6 7 static void sig_child(int signo); 8 9 int main() 10 { 11 pid_t pid; 12 //创建捕捉子进程退出信号 13 signal(SIGCHLD,sig_child); 14 pid = fork(); 15 if (pid < 0) 16 { 17 perror("fork error:"); 18 exit(1); 19 } 20 else if (pid == 0) 21 { 22 printf("I am child process,pid id %d.I am exiting.\n",getpid()); 23 exit(0); 24 } 25 printf("I am father process.I will sleep two seconds\n"); 26 //等待子进程先退出 27 sleep(2); 28 //输出进程信息 29 system("ps -o pid,ppid,state,tty,command"); 30 printf("father process is exiting.\n"); 31 return 0; 32 } 33 34 static void sig_child(int signo) 35 { 36 pid_t pid; 37 int stat; 38 //处理僵尸进程 39 while ((pid = waitpid(-1, &stat, WNOHANG)) >0) 40 printf("child %d terminated.\n", pid); 41 }
五.进程与管道
参考:
-
孤儿进程与僵尸进程[总结]
-
进程与信号
Linux进程间的通信