多进程编程
来源:互联网 发布:xp系统分区软件 编辑:程序博客网 时间:2024/06/01 09:00
多进程编程包括以下内容:
一、复制进程映像fork系统调用和替换进程映像的系统调用。
二、僵尸进程以及如何处理僵尸进程;
三、进程间的通信机制(inter-process Communication),最简单的是管道;
四、3种systemV进程间的通信方式:信号量,消息队列,和共享内存;
(1)fork系统调用
1、创建新进程的系统调用是fork pid_t fork(void),该函数调用一次返回俩次,在父进程中返回的是子进程的PID,在子进程中返回的是0,所以可以根据返回值判断是父进程还是子进程;
2、fork函数是复制当前进程,在内核中创建一个新的进程表项,这个进程表项的一些属性和父进程相同,堆指针,栈指针以及局部变量,也有一些许多属性被赋予了新的值,比如说子进程的PPID变成原来进程的PID,原来进程的信。信号处理函数在子进程中失效;
3、子进程会复制父进程的代码,也会复制进程的数据,复制数据采用的是写时拷贝。
4、此外,在父进程中打开的文件描述符在默认子进程也是打开的,这些文件描述符的引用计数加1。
(2)僵尸进程
有2种使子进程变成僵尸进程情况:1、在子进程退出之后,父进程读取他的退出状态之前,我们称子进程为僵尸进程;2、在子进程还未退出,父进程结束,或者异常终止,这样的子进程是僵尸进程;
有俩个函数:pid_t wait(int *stat_loc); pid_t waitpid(pid_t pid,int *stat_loc,int options),这俩个函数在父进程中调用,用来等待子进程的结束,并获取子进程返回信息。
1、pid_t wait(int * stat_loc) 是一个阻塞函数,只有该进程的某一个子进程结束的时候才返回,返回结束子进程的PID,并且把该子进程的退出状态返回给stat_loc参数指向内存中,
2、waitpid(),是一个非阻塞函数,他可以等待指定PID的子进程的退出,如果此时PID是-1,那么它就和wait函数一样等待任意一个子进程的结束,之所以说他是一个非阻塞的函数,是因为有一个参数options参数,如果该参数指定为WNOHANG,在子进程未结束或者异常终止的时候,他立即返回0,但是如果子进程确实正常退出,他就返回子进程的PID,
我们知道为了提高程序的效率,我们希望的是在事情已经发生的情况下调用非阻塞函数,所以我们最好在子进程退出之后调用waitpid,那么我们如何判断一个子进程/退出了呢?SIGCHLD,在子进程结束时候,他给父进程发送一个SIGCHLD信号,我们可以在父进程中捕获此信号,然后在对子进程处理,彻底结束子进程。
(2)PIPE(管道)
管道能在父,子进程间传递数据,利用的是fork调用之后俩个管道文件描述符(fd[1] ,fd[0])都保持打开,一对这样的文件描述符只能保障父子进程间一个方向的数据传输,父 ,子进程必须有一个关闭fd[0],另一个关闭fd[1].显然要实现父子进程之间的双向数据传输,就必须使用俩个管道,否则只能实现单项数据的传输。socketpair创建的管道,是双向的管道,数据既可在fd[0]读,也可以在fd[1]读,但是pipe只能在fd[0]读,fd[1]写,socketpair创建的管道,fd[1]也可以被用读,fd[0]也可以写。
#include<sys/types.h> #include<sys/socket.h>#include<unistd.h>#include<iostream>#include<stdio.h>#include<stdlib.h>#include<string.h>using namespace std;int main(){ int ret; pid_t pid; int fd[2]; char buf[256];// ret = socketpair(AF_UNIX,SOCK_STREAM,0,fd); //socketpair俩边都可以 pipe(fd); pid =fork(); if(pid == 0) { // close(fd[0]); //read(fd[1],buf,256);//这样无法读出数据 char *str="hello world"; close(fd[0]); write(fd[1],str,strlen(str));//这样才可以; printf("chid:%s\n",buf); } else if(pid > 0) { // char *str="hello word"; // close(fd[1]); // write(fd[0],str,strlen(str)); char buf[256]={0}; close(fd[1]); read(fd[0],buf,256); printf("%s\n",buf); }}
管道缓存区一有空闲区域,写进程就会试图向管道写入数据,如果读进程不读走管道缓存区的数据,那么写一直被阻塞等待,在写管道时,如果要求写的字节数小于等于PIPE_BUF,则多个进程对同一个管道的写操作不会较错进行。
(3)信号量
1、信号量可以确保在任意时刻关键代码段(临界区)只有一个进程在访问。
2、信号量可以取自然数值,并且只支持俩种操作(等待,信号)也就是(p,v)操作。他有一组函数分别是senget,semopt,semctl
3、原子操作:不会被线程调度机制打断的操作,这种操作一旦开始执行,就一直运行到结束,中间不会切换到任何线程。
4、为什么不适用一个普通的变量来模拟二进制信号量,因为所有的高级语言都没有一个原子操作可以同时完成来个不走:检查变量是否为true/false,如果是在将他更改为false/true;
5、semget用来获取一个信号量集,或者获取一个已经存在的信号量集;semopt对信号量的操作实际是修改这些内核变量的修改。有一些重要的内核变量(unsigned short semval,unsigned short semzcnt ,unsigned short semncnt,pid_t semid);semop(int sem_id,struct sembuf* sem_ops,size_t num_sem_ops);struct sembuf{unsigned short int sem_num;short int sem_op;short int sem_falg};
sem_op 和sem_flag按照如下方式来影响semop:当
(1)sem_op > 0,semop将被操作的信号的值sem_value增加semop.如果设置了SEM_UNDO,则系统更新semadj变量(会跟踪进程对信号量的修改)当进程退出时取消正在进行的semop的操作;
(2)当sem_op=0;如果semvalue是0,则调用成功立即返回。如果信号量的值不为0,如果说设置了sem_falg = IPC_NOWAIT,则sem_op立即返回并设置error.如果未指定标志位,则semop被投入睡眠,直到有以下三件事发生:第一,信号量的值semvalue变为0;第二,被操作的信号量所在的信号量集被进程移除,调用被信号中断。
(3)sem_op < 0,进行的是P操作,如果当前的信号量的绝对值大于等于sem_op,那么调用进程获得该信号量。如果信号量的值小于sem_op的值,调用进程将会被挂起,直到信号量的值大于等于了sem_op的值。
(4)消息队列
消息队列可以在俩个进程之间进行数据的传送,但是他不像管道那样,只能先进先被接收端收,它的数据被 打包为一组结构体msgbuf;
struct msgbuf
{
long mytype;
char mtext[512];
};
根据mytype确定接受哪先数据:
当mstype>0时,接收端接受消息队列中的第一个mutype的消息;
当mstype=0时,接收端接受消息队列中第一个消息。
当mstype<0时,读取消息队列中第一个比mstype绝对值小的那个消息。
(5)共享内存
共享内存是最高效的IPC机制,但是为了在任何时刻保证只有一个进程访问该数据,所以一般讲共享内存和信号量一起使用。
- UNIX多进程编程
- 多进程编程
- 多进程编程
- 多进程编程
- 多进程编程
- Windows多进程编程
- 多进程编程学习
- linux多进程编程
- linux 多进程编程
- perl多进程编程
- python多进程编程
- PHP多进程编程
- 多进程编程
- Unix 多进程编程
- Unix 多进程编程
- Linux多进程编程
- PHP多进程编程
- python多进程编程
- 阿里云 centos 6.8 及以上配置ipv6
- java迭代器
- 根据不同的时间日期查询数据
- 有一种感动叫ACM(记WJMZBMR在成都赛区开幕式上的讲话)
- 数组和链表的区别
- 多进程编程
- MOOC清华《程序设计基础》第6章第2题:求f(a,b)问题(动态规划)
- 迷宫问题
- 一些常用的行级标签和块级标签
- 12期 8月期刊自荐
- POJ 3096
- js中的继承
- LeetCode 648 : Replace Words(java)
- CSU-ACM2017暑假集训2-二分搜索 poj3104-drying