linux下的多进程学习探究
入门
1. 要点
1.进程相当于自我复制,将自己复制到另一个内存区域执行。复制出来的进程叫做子进程,进程之间相互独立(全局变量也是独立拥有的)。
2.Fork创建的新进程是和父进程(除了PID和PPID)一样的副本,包括真实和有效的UID和GID、进程组合会话ID、环境、资源限制、打开的文件以及共享内存段。
3.如果fork执行成功,就向父进程返回子进程的PID,并向子进程返回0。这就一起这即使你只调用fork一次,他也会返回两次。
4.子进程与父进程没有绝对的先后执行顺序。
5.子进程死后就会变成僵尸进程,需要父进程处理尸体(waitpid)。否则子进程要等到父进程结束才会退还资源。
2. 相关函数
创建一个子进程
- pid_t fork(void)
头文件 #include<unistd.h>
返回值:向父进程返回子进程的PID,并向子进程返回0。错误返回-1。
示例:
pid_t PID;
PID = fork();
执行进程
- exec族
int execl(const char path, const chararg, ...) //execl没有继承环境变量
int execlp(const char path, const chararg, ...) //execlp继承了环境变量
头文件 #include<unistd.h>
const char *path:要执行的二进制文件或脚本的路径(绝对路径,相对路径都可以)
const char *arg :
是要传递给程序的完整参数
返回值:
失败返回-1,成功就跳转到新进程了。
示例:
execl("/bin/ls", "-a","/etc/passwd",(char*)0);
execl("ls","-a","/etc/passwd",(char*)0);
清理子进程尸体
- 非阻塞:pid_t waitpid(pid_t pid,int * status,int options)
头文件 #include<sys/wait.h>
pid:pid>0,只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去。
pid=-1,等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样。
pid=0:等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬。
pid<-1:等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值。status:
传递出进程结束时的状态。不在意可使用NULL
返回值:
函数若成功,返回进程PID,若出错则返回-1;
- 阻塞:pid_t wait(int * status)
头文件 #include<sys/wait.h>
status:传递出进程结束时的状态。不在意可使用NULL
返回值:
函数若成功,返回进程PID,若出错则返回-1;
获取PID
- 获取自身pid:pid_t getpid()
头文件 #include<unistd.h>
返回值:函数若成功,返回自身进程PID,若出错则返回-1;
- 获取父进程pid:pid_t getppid()
头文件 #include<unistd.h>
返回值:函数若成功,返回父进程PID,若出错则返回-1;
代码示例
- mult_pid.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid > 0) //父进程
{
printf("\n in parent \n");
printf("tparent pid = %d\n",getpid());
printf("tparent ppid = %d\n",getppid());
wait(NULL);
}
else if(pid == 0) //子进程中
{
printf("\n in child \n");
printf("tchild pid = %d\n",getpid());
printf("tchild ppid = %d\n",getppid());
if(execl("ls", "-a",(char*)0) < 0)
{
perror("execl");
exit(1);
}
}
else
{
perror("fork");
exit(1);
}
}
进阶
1. 进程间通信
说明
为什么进程需要额外的通信手段呢?
因为进程的创建时,从父进程克隆所有资源后就相对独立了。所有的资源都是独立开来的,不会影响到父进程的资源。因此需要通信机制来使得进程间协同工作,例如:进程同步,数据传输等等。
线程?进程?
这里我就喜欢比较线程了,线程虽然也是克隆而来,有自己的资源,但相对没有那么独立,比如全局变量还是同一个。独立的只有局部变量。这和单片机的结构非常相似。
通信机制
- 内存共享( shared memory )
- 无名管道( pipe )
- 有名管道 (named pipe)
- 套接字( socket )
- 消息队列( message queue )
- 信号量( semophore )
- 信号( signal )
2. 进程的探索
代码
- ./main.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
int main(void)
{
pid_t pid;
pid = fork();
if(pid > 0) //父进程
{
printf("in main\n");
wait(NULL); //主进程程序会一直卡在这,直到子进程结束
printf("hello\n");
if(execlp("pwd", "",(char*)0) < 0)
{
perror("execl");
exit(1);
}
//后面所有的代码不会再执行
}
else if(pid == 0) //子进程中
{
printf("in main child \n");
if(execlp("./bin/bin", "",(char*)0) < 0) //这个相对路径是shell当前路径
{
perror("execl");
exit(2);
}
//后面所有的代码不会再执行
}
else
{
perror("fork");
exit(1);
}
}
- ./bin/bin.c
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
sleep(3);
printf("\nin bin\n");
if(execlp("pwd", "",(char*)0) < 0) //跳转到系统进程pwd
{
perror("execl");
exit(1);
}
//后面所有的代码不会再执行
}
结论
相对路径?
这个相对路径是shell当前路径,而不是主进程的所在路径。
exec簇执行,新进程执行?直接跳转到目标进程?
跳转到目标进程。也就是说,相当于将该fork出来的进程转变成了目标进程。就算目标进程结束也不会再回来。