Linux--多进程

进程

正在运行的程序及其占用的资源(CPU、内存、系统资源等)叫做进程
进程这个概念是针对系统而不是针对用户的,对用户来说,面对的概念是程序。

  • 系统在创建新的子进程成功后,会将父进程的文本段数据段堆栈都复制一份给子进程,
  • 子进程有自己独立的空间,子进程对这些内存的修改并不会影响父进程空间的相应内存。
  • 两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。
  • 要实现进程运行按照一定的顺序,必须用代码进行控制(进程间通信)

Linux下有两个基本的系统调用可以用于创建子进程:fork()和vfork()

fork函数

pid_t fork( void);

返回值: 若成功调用一次则返回两个值,子进程返回0,父进程返回子进程ID;否则,出错返回-1

  • getpid()可以获取到当前进程ID;
  • 可以使用wait或waitpid函数等待子进程的结束并获取结束状态

fork 函数调用失败的原因主要有两个:

  1. 系统中已经有太多的进 程;
  2. 该实际用户 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)

守护进程的步骤

  1. 调用fork(),创建新进程,它会是将来的守护进程.
  2. 在父进程中调用exit,保证子进程不是进程组长
  3. 调用setsid()创建新的会话区
  4. 将当前目录改成跟目录(如果把当前目录作为守护进程的目录,当前目录不能被卸载他作为守护进程的工作目录)
  5. 将标准输入,标注输出,标准错误重定向到/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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值