进程
正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进程。
进程这个概念是针对系统而不是针对用户的,对用户来说,面对的概念是程序。
- 系统在创建新的子进程成功后,会将父进程的文本段、数据段、堆栈都复制一份给子进程,
- 但子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程空间的相应内存。
- 两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
- 要实现进程运行按照一定的顺序,必须用代码进行控制(进程间通信)
Linux下有两个基本的系统调用可以用于创建子进程:fork()和vfork()
fork函数
pid_t fork( void);
返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1
- getpid()可以获取到当前进程ID;
- 可以使用wait或waitpid函数等待子进程的结束并获取结束状态
fork 函数调用失败的原因主要有两个:
- 系统中已经有太多的进 程;
- 该实际用户 ID 的进程总数超过了系统限制。
fork的时候发生什么
执行到fork这一句的时候,一个进程被创建了,这个进程与父进程一样,拥有一套与父进程相同的变量,相同的一套代码,这里可以粗浅的理解为子进程又复制了一份main函数。这里返回一个子进程的进程号,大于0。
理论上的原因是子进程是父进程的一份副本(包括数据空间,堆,栈等)拷贝,当然也包括PC寄存器(PC的全称是program counter,程序计数器,是用来计数的,指示指令在存储器的存放位置,也就是个地址信息)如果没有拷贝PC寄存器的值,那么子进程又会从头开始执行,直到一直fork到耗尽资源
所以出现如果多次fork会发生什么呢?
多次fork
首先先来分析一个程序
#include<stdio.h>
#include<unistd.h>
int main()
{
int i;
for(i=0;i<2;i++)
{
fork();
printf("-\n");
}
return 0;
}
会产生几个进程,输出多少个“-”呢
程序fork以后从子进程从下一条语句开始执行
产生4个进程,输出六次
对程序稍作修改
#include<stdio.h>
#include<unistd.h>
int main()
{
int i;
for(i=0;i<2;i++)
{
fork();
printf("-");
}
printf("\n");
return 0;
}
同样还是产生4个进程,但是改了“\n”的位置,会输出几个“-”呢
看到有8个“-”。因为Linux下的printf函数是带缓冲的输出函数,到“\n”处或者进程退出时清理缓冲,与前一个程序相比,这个程序在fork出4个进程时都没有清除缓冲区内容,所以子孙进程都复制了主进程的缓冲区
僵尸进程、孤儿进程与守护进程
孤儿进程
如果父进程先退出,子进程还没退出那么子进程将被 托孤给init进程,这是子进程的父进程就是init进程(1号进程)
僵尸进程
一个进程exit时,其父进程没有调用wait,waitpid等进行处理,并且父进程一直运行不退出,那么这个进程将会变成僵尸进程,占用进程号等系统资源。
要解决僵尸进程,必须要杀死其父进程使其变成孤儿进程由init进程接管或者在父进程中调用wait等函数对exit的子进程进行收尸。
守护进程
守护进程就是在后台运行,不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务.习惯上守护进程的名字通常以d结尾(sshd)
守护进程的步骤
- 调用fork(),创建新进程,它会是将来的守护进程.
- 在父进程中调用exit,保证子进程不是进程组长
- 调用setsid()创建新的会话区
- 将当前目录改成跟目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
- 将标准输入,标注输出,标准错误重定向到/dev/null或者日志系统
在Linux中,daemon函数能快速创建守护进程
用一个多进程服务器demo练习代码
多进程服务器框架
代码实现:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define PORT 8848
#define BUFFSIZE 1024
int main (int argc,char *argv)
{
int socket_fd;
int listen_fd;
char buf[BUFFSIZE];
pid_t fpid;
struct sockaddr_in servaddr;
int rd;
socklen_t len;
socket_fd = socket(AF_INET,SOCK_STREAM,0);
if (socket_fd < 0)
{
perror("create socket fail!");
return -1;
}
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons (PORT);
servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
if (bind(socket_fd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
{
perror("bind fail!");
return -2;
}
if (listen(socket_fd,10) < 0)
{
perror("listen fail!");
return -3;
}
while (1)
{
printf("the PID is %d\n",getpid());
listen_fd = accept(socket_fd,(struct sockaddr *)&servaddr,&len);
if (listen_fd < 0)
{
perror("listen fail!");
continue;
}
fpid = fork();
if (fpid < 0)
{
perror("fork fail!");
close(listen_fd);
continue;
}
if (0 == fpid) //pid==0,子进程
{
while(1)
{
printf("the parent process PID is %d\n",getppid());
printf("the child procsee PID is %d\n",getpid());
memset(buf,0,sizeof(buf));
rd = read(listen_fd,buf,sizeof(buf));
if (rd < 0)
{
perror("read fail !");
close(listen_fd);
continue;
}
if (write(listen_fd,buf,rd) < 0)
{
perror("write fail!");
close(listen_fd);
}
printf("receive the message:%s\n",buf);
sleep(10);
}
return 0;
}
if (fpid > 0) //父进程
{
sleep (5);
printf("I am in the parent process now !\n");
printf("the parent process PID is %d\n",getpid());
close(listen_fd);
printf("the parent process close the listen_fd!\n");
continue;
}
}
return 0;
}