Linux多进程编程
来源:互联网 发布:gcc怎么用windows 编辑:程序博客网 时间:2024/06/05 19:23
操作系统中核心的概念就是进程:这是对正在运行程序的一个抽象。
一个进程就是某种类型的一个活动,它有程序、输入、输出、以及状态。单个处理器可以被若干进程共享,它使用某种调度算法进行进程的调度。注意:如果一个程序运行了两遍,就是两个进程。
进程创建 fork
#include <sys/types.h>#include <unistd.h>pid_t fork(void);返回:每次调用返回2次,父进程中返回子进程PID,在子进程中返回0,出错返回-1fork函数复制当前进程,在内核进程表中创建一个新的进程表项。新的进程表项去多属性和原进程的相同,比如堆指针、栈指针和标志寄存器的值。也有许多新的属性被更改,比如该进程的PPID设置为远进程的PID,信号为徒被清除(远近程设置的信号处理函数不再对新进程起作用)
exec系列函数
#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ..., char * const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);
path参数指定可执行文件的完整路径,file参数可以接收文件名,该文件的具体位置则在环境变量中PATH中寻找。arg接受可变参数,argv接受参数数组,他们都会被传递给新程序的main函数。envp用于设置新程序的环境变量,如果未设置,则新程序将使用environ指定的环境变量。
一般情况下,exec函数是不返回的,除非出错。出错时返回-1,并设置errno,调用成功后原程序中调用exec之后的代码都不会执行,因为此时源程序已经被exec的参数指定的程序完全替换了(代码和数据)。
exec函数不会关闭源程序中打开的文件描述符,除非该文件描述符被设置了SOCK_CLOEXEC
处理僵尸进程
对于多进程程序而言,父进程一般需要跟踪子进程的退出状态。因此,当子进程结束运行时,内核不会立即释放该进程的进程表表项,以满足父进程后续对该子进程退出信息的查询。在子进程结束运行之后,父进程读取器退出状态之前,我们称该子进程处于僵尸态。若父进程结束或者异常终止,而子进程继续运行,此时子进程的PPID将被系统设置为1,即init进程。init进程接管了该子进程,并等待它结束。
子进程停留在僵尸态,占据内核资源,这是绝对不允许的,毕竟内核资源有限。下面这对函数在父进程中调用,以等待子进程结束,并获取子进程的返回信息,从而避免了僵尸进程的产生,或者使子进程的僵尸态立即结束:
#include<sys/types.h>#include<sys/wait.h>pid_t wait(int*status);pid_twaitpid(pid_t pid, int *status, int options);
wait函数将阻塞进程,直到该进程的某个子进程结束运行为止。它返回结束运行的子进程的PID,并将子进程的退出状态信息存储于status参数指向的内存中。
waitpid只等待由pid参数指定的子进程,如果pid取值为-1,则和wait函数相同,即等待任意一个子进程结束。options的参数可以控制waitpid的行为,当该参数取值为WNOHANG时,waitpid将是非阻塞的:如果pid指定的目标子进程还没有结束或意外终止,则waitpid立即返回0;如果目标子进程确实正常退出了,则waitpid返回该子进程的PID。失败返回-1,并设置errno。
要在事件已经发生的情况下执行非阻塞调用才能提高程序的效率。对waitpid函数而言,我们最好在某个子进程退出之后再调用它。那么父进程从何得知某个子进程已经退出了?这正是SIGCHLD信号的用途。当一个进程结束时,它将给其父进程发送一个SIGCHLD信号。因此,我们可以在父进程中捕获SIGCHLD信号,并在信号处理函数中调用waitpid函数以彻底结束一个子进程。如下所示:
static void handle_child( int sig ){pid_t pid;int stat;while((pid = waitpid(-1, &stat, WNOHANG)) > 0){/* 对结束的子进程进行善后处理 */}}
管道
管道是父子进程之间通信的常用手段,管道能在父、子进程之间传递数据,利用的是fork调用之后两个管道文件描述符(fd[0]和fd[1])都保持打开。
管道有pipe函数创建,此时管道是单向的,pipe函数讲解见:网络编程API-中 (高级I/O函数)
管道程序示例:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#define err_sys(msg) \do { perror(msg); exit(-1); } while(0)int main(void){int pipefd[2];pid_t pid;if(pipe(pipefd) < 0)err_sys("pipe");if((pid= fork()) < 0)err_sys("fork");else if(pid == 0){char buf[10] = {0};close(pipefd[1]);read(pipefd[0], buf, sizeof(buf)); //当管道中没有数据时,read会阻塞printf("In child process:\n");printf(" %s\n", buf);exit(0);}else{close(pipefd[0]);sleep(1);write(pipefd[1], "hello", strlen("hello"));wait(NULL);}return 0;}
参考:
1、《Linux高性能服务器编程》 9章 多进程编程/管道
2、网络编程API-中 (高级I/O函数)
- linux多进程编程
- linux 多进程编程
- Linux多进程编程
- linux 多进程编程
- linux 多进程编程
- linux多进程编程
- linux多进程编程
- Linux多进程编程
- Linux 多进程编程
- Linux 多进程编程
- linux多进程编程
- Linux多进程编程
- Linux多进程编程 与windows进程
- linux/unix多进程编程
- 【linux】多进程、多线程编程
- Linux多进程编程讲解
- linux 多进程编程基础
- 嵌入式linux多进程编程
- hadoop学习笔记(倒排索引)
- Linux下开启mysql数据库的远程访问权限
- 黑马程序员---流程控制,函数,数组
- oracle分组查询
- iOS里面的委托
- Linux多进程编程
- LeetCode 题解(129): Majority Element II
- 基于Maven的Mybatis+spring+springMVC框架整合(mapper代理方式)
- 单片机串口收发数据
- 设置DatePickerDialog的最大日期为当前日期
- 设计模式-单例模式总结
- 香蕉派 Banana pi BPI-M1+ 双核开源单板计算机. 板载WIFI
- MySQL参数之sql_mode详解
- Java线程面试题 Top 50