C语言简单进程

进程:

进程是正在运行的程序的实例

进程是一个具有独立功能的程序关于某个数据集合的一次运行活动

是系统进行(资源分配和调度)的基本单位,是操作系统结构的基础

是一个动态的概念,是一个活动的实体

init是个有超级权限的用户进程


执行态(Running):包括运行和就绪

  睡眠(sleeping):

(S)浅睡眠 interruptable 进程在等待一个事件的发生或者某种系统资源,可响应异步信号

(D)深睡眠 uninterruptable 无法响应异步信号, kill -9 是杀不死的,

停止态(sTopped)进程被暂停或者跟踪状态(pause/trace)  ctrl+Z 

僵尸态(Zombie) 进程处于退出状态,不能被调度,但是PCB还没有被释放. 

 如子进程退出,父进程没有回收资源而且还在继续运行,这样就会造成子进程变成僵尸进程


实例一:进程创建

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int global = 10;
int main(void)
{
	//printf("pid = %d , ppid = %d \n", (int)getpid(), (int)getppid() );
	//while(1);

	pid_t id;//注意创建子进程返回的类型
	int val = 100;
	
	printf("before fork \n");
	id = fork();//创建子进程
	if(id < 0 )
	{
		printf("Fork failed \n"); // perror
		exit(1);
	}
	else if(id == 0)
	{	
		val--;
		global--;
		printf("in Child process %d, %d\n", val,global);
		printf("pid = %d , ppid = %d \n ", getpid(), getppid() );
	}	
	else if(id > 0)
	{
		//sleep(1);
		/*
		1、fork创建后,不保证两进程的调用顺序,
		也就是根据系统当前环境不同,可能先调用父进程也可能先调用子进程
		2、在这不加sleep,先执行父进程,然后父进程结束之后就执行父进程的父进程即是bash
		也就会先输出root@xx:,然后再输出子进程的内容,
		这时的子进程的父进程就不是此程序了,是被遗弃给了init。
		3、加上sleep就是先让子进程先显示,然后再显示出父进程的。
		子进程的父进程就也是此程序了,可以看pid。
		4、父、子进程共享正文段,不共享数据空间、堆、栈;
		子进程也是由父进程调用FORK后的代码开始执行,变量有独立的拷贝
		所以2个进程的val和global都不互相影响
		*/
		val --;	
		global--;
		printf("in Parent process %d, %d\n", val,global);
		printf("pid = %d , ppid = %d \n ", (int)getpid(), (int)getppid() );
	}
	
	
//	return 0;
	exit(0);
//	_exit(0);
}

2个进程执行输出。然后开另外一个终端,kill掉父进程,

子进程会继续执行,但父进程会变为1,不是此程序的父进程了。

在console中按ctrl+c,那是会给当前所有的会话中的进程发信号。

所以要用kill,但kill掉父进程后,再用ctrl+c也kill不掉子进程的。

也要用kill。但如果先kill掉子进程,再ctrl+c就可以kill掉父进程。



实例二:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
/*
创建一个子进程, 该子进程会执行一个程序
用exec族的函数
*/
int main()
{
	//printf("start of main\n");	
	pid_t id = fork();
	if(id < 0 )
	{
		printf("Fork failed \n"); // perror
		exit(1);
	}
	else if(id == 0)
	{	
		//printf("child pid = %d \n", getpid());
		
		if(execl("./homework","homework",NULL)<0)
		{
			perror("execl error!");
			exit(1);
		}
	}
	else if(id > 0)
	{
		int status;
		wait(&status);
		//sleep(1);
		printf("parent pid = %d \n", getpid());
	}
	exit(0);
}


实例三:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> 
/*
在父进程中创建两个子进程
子进程分别执行不同的内容
然后再回收
*/
void fun(void)
{
	printf("all die ~~~\n");
}

int main(int argc, char *argv[])
{
	int i, status;
	pid_t id, pid1, pid2;

	printf("%s's pid = %d\n", argv[0], (int)getpid());

	pid1 = fork();
	if (pid1 < 0)
	{
		perror("error to fork pid1\n");
		exit(0);
	}
	else if (pid1 == 0)
	{
		for (i = 0; i < 5; i++)
		{
			printf("child 1: pid = %d, ppid = %d\n", (int)getpid(), (int)getppid());
			sleep(1);
		}
		exit(0);
	}
	wait(&status);
	/*有特殊回收需求的,可以用waitpid<span style="white-space:pre">															</span>wait(&status);等价于waitpid(-1, &status, 0);<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>&status 可用NULL 代替。
	如果一个子进程结束时候没有被父进程回收资源,在父进程存在期间,它是作为僵尸进程;
	只有父进程结束时候,它由1号进程init收养后,init会回收该僵尸进程资源
	references:http://c.biancheng.net/cpp/html/289.html*/
	/*id = waitpid(pid1, &status,0);// wait pid1 
	printf("%d\n", i);*/
	/*这里回收资源,然后再fork一个子进程。如果放到最后和第2个子进程一起回收
	那输出显示的时候是交替的*/
	pid2 = fork();
	if (pid2 < 0)
	{
		perror("error to fork pid2\n");
		exit(0);
	}
	else if (pid2 == 0)
	{
		for (i = 0; i < 5; i++)
		{
			printf("child 2: pid = %d, ppid = %d\n", (int)getpid(), (int)getppid());
			sleep(1);
		}
		exit(0);
	}

	wait(&status);
	/*id = waitpid(pid2, &status,0);// wait pid2 
	printf("%d\n", i);*/
	//atexit 注册进程正常退出时候的处理程序, 注册的顺序和处理的顺序是相反的
	if(atexit(fun))
			perror("error1 ");
	/*
	上面思路是,fork一个,父进程处理函数后,再fork一个。
	OR:这个思路是先fork一个,然后进入到父进程处理函数里再fork一个。
	id=fork();
	if(id < 0 )//error
	else if(id == 0)//child process
	else if(id > 0)//parent process create fork again
		id = fork();
		if(id < 0 ) // perror
		else if(id == 0)//child process
		else if(id > 0)//parent process 
	*/
	exit(0);
}

相关函数介绍:

fork: 用fork创建子进程, 

返回值为-1的时候表示没有创建成功

创建成功的话,0是给子进程; 子进程ID返回给父进程

父、子进程共享正文段,不共享数据空间、堆、栈;

子进程也是由父进程调用FORK后的代码开始执行,变量有独立的拷贝

IO缓冲区也有拷贝

 

写时复制COW ( COPY ON WRITE)

 

fork创建后,不保证两进程的调用顺序,

也就是根据系统当前环境不同,可能先调用父进程也可能先调用子进程

 

vfork: 可以保证子进程先调, 直到子进程调用的exit或者是exec后,父进程才运行

        vfork产生的子进程是和父进程共用资源,数据和代码都共享

 

 

 

 

exec 函数族:(令子进程“脱胎换骨”)

 调用的exec后,如果启动成功,子进程就被新执行程序替换了,只保留了进程号其它都被替换;

 启动失败就返回-1 

 

man 3 environ

execl: exec list list挨个写cmd

execv: exec v 指针数组写cmd

 

execlp: execl path 使用系统环境变量PATH对应的路径中去寻找对应的可执行文件 

execvp: exec vp

 

execle: execl env 环境变量,也是用指针数值去写env

execve: exec ve ---- 内核调用

 

 

 

 

 

进程的退出:

_exit(0);  必须是自带有刷写io缓存的动作才能显示出来,这个退出函数是不会关闭IO缓冲区的

exit();  是会处理退出处理函数的,而且会关闭IO缓冲区

 

孤儿进程:父进程先于子进程结束了, 

这个时候子进程就变成孤儿进程,会由init进程接管;

该孤儿进程结束时候,init会自行回收资源

bash里面调用一个可执行文件./func ,  func执行完了就会有一个信号SIGCHLD返回给bash

bash 收到信号后,就会显示gec@ubuntu:/mnt/hgfs/94/系统编程/0910/code$

即使现在还有孙子进程,bash也是不管,但是如果孙子进程要打印数据,依旧是在bash窗口里面显示

 

atexit 注册进程正常退出时候的处理程序, 注册的顺序和处理的顺序是相反的

atexit(func1) in atexit.c

 

 

进程同步(回收资源)

在进程结束的时候回收对应资源

如:之前在虚拟内存里面的申请到的堆数据, 

    如果进程是一直运行并且不断申请内存而不释放, 就会导致内存泄漏;

 

如果一个子进程结束时候没有被父进程回收资源,在父进程存在期间,它是作为僵尸进程;

    只有父进程结束时候,它由1号进程init收养后,init会回收该僵尸进程资源

 

wait ():

等待子进程结束并回收资源 

 等待任意一个子进程

waitpid()

有所扩展的wait 

 #include <sys/wait.h>

 

        pid_t wait(int *stat_loc); 

         pid_t waitpid(pid_t pid, int *stat_loc, int options);

 

int status ;

pid_t id;

id = wait(&status); < == > id = waitpid(-1, &status,0);

 

waitpid里面的

pid > 0  就是等待具体的一个进程,其进程号为PID

    = 0  等待本进程组里任意子进程

    = -1   等待任意一个子进程

    < -1 , 等待IDpid绝对值的进程组里的任意一个子进程

option

WNOHANG, 不需要有已结束进程,立刻返回,不会挂起,也就是不等待

WUNTRACED 若进程被暂停,sTop了,立即返回

WCONTINUED :子进程收到SIGCONT立刻返回

 

 

 进程的终止:

5种正常

main返回;

   return 0

调用exit函数;

   会先执行一些清理处理,包括调用执行处理终止程序,关闭所有标准I/O流等,然后返回内核

调用_exit_Exit函数;立即进入内核。

最后一个线程从其启动例程返回;

从最后一个线程调用pthread_exit

 

3种异常

调用abort; SIGABRT

接到一个信号并终止;ctrl+c  ctrl +\ 

最后一个线程对取消请求作出响应;



实例二:
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值