C语言简单进程

来源:互联网 发布:热量计算软件 编辑:程序博客网 时间:2024/04/20 03:25

进程:

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

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

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

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

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"); // perrorexit(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"); // perrorexit(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 )//errorelse if(id == 0)//child processelse if(id > 0)//parent process create fork againid = fork();if(id < 0 ) // perrorelse if(id == 0)//child processelse 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 0