您的位置:首页 > 编程语言 > C语言/C++

C语言简单进程

2015-09-12 22:08 459 查看
进程:

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

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

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

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

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 , 等待ID为pid绝对值的进程组里的任意一个子进程

option

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

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

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

进程的终止:

5种正常

从main返回;

return 0;

调用exit函数;

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

调用_exit或_Exit函数;立即进入内核。

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

从最后一个线程调用pthread_exit;

3种异常

调用abort; SIGABRT

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

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

实例二:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: