- 什么是进程?
它是运行中的程序,一个程序可以有多个进程,进程包含程序计数器(用来存放下一条指令存放的地址)、寄存器(用来存储指令,数据和地址)、变量当前的值。 - 进程如何创建?
可以通过系统调用来创建子进程,fork().pid_t pd; pd=fork();
该系统调用创建了一个子进程,该子进程拥有与父进程一样的初始状态,PCB相同,用户态代码也相同,数据也相同。此时刚调用完fork()函数,还没有返回,至于先返回子进程还是先返回父进程要看系统调度策略。
子进程中fork()的返回值是0;父进程中fork()的返回值是子进程的进程号,如果创建失败返回-1。子进程可以通过getpid()获得自己的进程号。
进程创建之后拥有独立的地址,对数据的修改,对方是看不见的,但是双方可以共享打开文件之类的资源,通过共享内存的方式通信是进程通信的一种方式。进程只有一个父进程,可以有多个子进程。
终端输入PS可以看见当前正在运行的进程。 - 进程的终止
终止方式有:- 正常退出,exit()系统调用用来通知系统进程结束;
- 出错退出;
- 严重错误;
- 被其他进程杀死。
- 进程的状态
就绪态,运行态,阻塞态。
就绪态是进程可以运行,但是需要等待系统调用,一旦被系统调用就进入运行态;运行态是进程被系统调用,当分时片用完之后,系统就会由回到就绪态,系统会调用别的处在就绪状态的进程;阻塞态是进程在运行时,等待某一外部条件发生,而被阻塞,如果条件到达,进程就进入就绪态,等待系统再次调用。 - exec函数
前面fork()创建的子进程执行的还是父进程的函数,可以系统调用exec()以执行另一程序。 - wait()和waitpid()函数
当一个进程终止时会关闭所有文件描述符,占有的内存,但PCB还保留着,里面保留着进程退出的信息,如果是正常退出,则保存着退出状态,如果是异常退出,则保存着导致该进程异常退出的信号,wait()和waitpid()用于获取这些信息,然后清理该进程。如果一个进程已经终止,但是父进程没有对其进行清理,那么这个进程就会成为僵尸进程。ps u命令可以查看进程状态,记录带有<defunct>就是僵尸进程。pid_t wait(int *status); pid_t wait_pid(pid_t pid,int *status,int options);
如果调用成功,返回子进程的进程号;如果调用失败,返回-1。
父进程调用该函数有三种情况:
父进程阻塞(子进程还在运行);
带上子进程信息返回(子进程执行完毕);
出错立即返回(没有子进程)。
两个函数的区别:
wait()函数的调用,如果子进程还在执行,那么父进程会阻塞;
wait_pid()函数的调用,若指定options为WNOHANG,则父进程不会阻塞而立即返回0。
wait()等待第一个子进程结束,wait_pid()可以指定等待子进程。
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> int main(void) { pid_t pid; pid = fork(); if (pid < 0) { perror("fork failed"); exit(1); } if (pid == 0) { int i; for (i = 3; i > 0; i--) { printf("This is the child\n"); sleep(1); } exit(3); } else { int stat_val; waitpid(pid, &stat_val, 0); if (WIFEXITED(stat_val)) printf("Child exited with code %d\n", WEXITSTATUS(stat_val)); else if (WIFSIGNALED(stat_val)) printf("Child terminated abnormally, signal %d\n", WTERMSIG(stat_val)); } return 0; }
-
进程间通信的方式
首先必须明白进程间通信是什么?
两个进程,由于相互独立,因此如果要通信,就应该在内核区开辟空间,一个进程从用户态进入内核,将数据存入内核区,另一个进程从该内核区取数据,内核提供这种通信方式叫进程间通信,IPC(InterProcess Communication)。
管道通信,消息队列,FIFO,信号量,共享内存,socket套接字。
-
管道通信
#include <unistd.h> int pipe(int fileds[2]); 该函数在内核中开辟一段缓冲区用于通信,管道就是这段缓冲区,该缓冲区有两个端口: 读端和写端,它们的文件描述符存在fileds[2]中,fileds[0]是读端,fileds[1]是写端 read(fileds[0]); write[fileds[1]); 管道创建成功返回0,创建失败返回-1.
父进程创建管道,fork()创建子进程,子进程通过文件描述符读写数据;父进程还可以创建两个子进程,将文件描述符传给两个子进程,这样两个子进程就能通信了。父进程与子进程通过管道通信的方式如下:
#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h> #define MAXSIZE 100 int main(void) { pid_t pid; int fd[2]; int er=pipe(fd); if(er==-1){ perror("fail to create pipe"); exit(1); } char buffer[MAXSIZE]; pid = fork(); if (pid < 0) { perror("fork failed"); exit(1); } if (pid == 0) { close(fd[1]); read(fd[0],buffer,MAXSIZE); std::cout<<buffer<<std::endl; exit(3); } else { close(fd[0]); std::string str="liujinyang"; write(fd[1],&str,str.length()+1); wait(NULL); } return 0; }
注意:通过管道通信时,必须有一方关闭写文件描述符,另一端关闭读文件描述符,也就是,单工通信。上面是只能父进程写,子进程读;如果同时需要子进程写,父进程读,那么还需要建立一个管道。
使用管道需要注意以下四种情况:-
管道为空,如果写端没有关闭,继续读会导致阻塞,直到有数据;如果写端关闭,返回0。
-
管道满,如果读端关闭,继续写会产生SIGPIPE信号,导致进程异常终止;如果读端未关闭,继续写,会阻塞;
-
-
其他的不想写了,socket通信有一个专门的博客
-
当你回答进程时你应该回答什么?
最新推荐文章于 2020-04-30 08:55:45 发布