进程创建:有 pid_t fork(void) pid_t vfork() 两个函数
fork():它的返回值:子进程中返回0,父进程中返回进程id,出错返回-1。
进程调用fork,当进程转移到内核中的fork代码后,内核需要:
1)分配新的内存块和内核数据结构给子进程。
2)将父进程的部分数据结构内容拷贝给子进程
3)添加子进程到系统的进程列表中
4)fork返回,开始调度器调度。
这里要提到写时拷贝:通常,父子进程代码共享,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。
用fork()函数创建一个子进程
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int a=100;
6
7 int main()
8 {
9 pid_t pid;
10 pid=fork();
11 if(pid==-1){
12 perror("fork");
13 exit(1);
14 }
15 if(pid==0){
16 a=200;
17 printf("i am child %d \n",a);
18 exit(0);
19 }else{
20 sleep(2);
21 printf("i am parent %d \n",a);
22 }
23 return 0;
24 }
~
运行结果:
i am child 200
i am parent 100
可见子进程中不会改变父进程中的变量值。
pid_t vfork(void)中子进程可以改变父进程中变量的值,以为子进程在父进程的地址空间中运行。
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4
5 int a=100;
6
7 int main()
8 {
9 pid_t pid;
10 pid=vfork();
11 if(pid==-1){
12 perror("fork");
13 exit(1);
14 }
15 if(pid==0){
16 a=200;
17 printf("i am child %d \n",a);
18 exit(0);
19 }else{
20 sleep(2);
21 printf("i am parent %d \n",a);
22 }
23 return 0;
24 }
~
运行结果:
i am child 200
i am parent 200
调整进程的优先级: [-20,19] 优先级值越小,优先级越高
nice -n 优先级 程序名
renice 优先级 -p pid
进程终止
正常终止:① 从main函数中退出 ②调用 exit ③_exit
exit函数在程序退出时关闭所有打开的流,并刷新缓存,_exit不会刷新缓存
异常退出:1.ctrl+c
2.abort();
3.kill pid 让进程自己去死
killall 进程名 全部杀死 例 killall a.out
atexit(void fun(void)) //注册退出处理程序
return退出
return是一种更常见的退出进程的方式。执行return n等同于执行exit(n),因为调用main的运行时函数会将main的返回值当做exit的参数。
进程等待
wait方法
pid_t wait(int *status)
返回值:成功返回被等待进程的pid,失败返回-1
参数:输出型参数,获取子进程的退出状态,不关心可以设置为NULL。
waitpid方法
pid_t waitpid(pid_t pid, int *status, int options)
参数:
pid:
pid= -1,等待任意一个子进程。与wait等效。
pid>0:本进程的子进程
pid=0:调用者进程所在进程组的任何一个子进程死亡
pid<-1:|pid|进程组的任何一个子进程死亡
返回值:
正常返回时waitpid返回收集到的子进程的进程id;
如果设置了选项WONHANG,若没有子进程需要回收则直接返回0;
如果调用中出错,则返回-1,errno会被设置成相应的错误码。
●如果子进程已经退出,调用wait/waitpid时会立即返回并且释放资源,获得子进程的退出信息。
●如果在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞。
●如不存在子进程,则立即出错返回。