这周小组的任务是Linuc c这本书上的进程,所以我想对这周学的东西简单的做一个总结,主要是想写一下fork的for循环和守护进程,还有创建进程等一些基本的东西。
进程创建
系统调用fork是创建一个新进程的唯一方法,虽然vfork函数虽然也可以创建进程,但是它在创建进程时实际上还是调用了fork函数。
fork函数有2个返回值,即调用一次返回两次,当fork函数调用成功后,原来的父进程返回子进程的ID,而新创建的子进程返回0,可用返回值区别父子进程。
一般来说,fork之后是父进程还是子进程先执行是不确定的,这取决于内核的调度算法,关于父子进程的执行顺序,我觉得这篇博客写的挺好的,很适合我这只种小白,哈哈
:https://blog.csdn.net/msdnwolaile/article/details/51363854
vfork和fork的区别
vfork也可以用来创建一个新进程,使用fork创建一个子进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性,而使用vfork创建一个子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork创建的子进程共享父进程的地址空间,就是说子进程完全运行在父进程的地址空间上,子进程对改地址空间的任何修改都为父进程所见。
这就不难理解为什么用fork创建子进程时,不管是全局变量还是局部变量,子进程与父进程对他们的修改互不影响,而对vfork而言,子进程修改变量对父进程是可见的。
还有一点是,vfork保证子进程先运行,当它调用exec或exit之后,父进程才可能被调度运行。
创建守护进程
实际当我第一次接触到这个东西时,真的感觉它非常抽象,因为我理解不了它,我觉得我看书就是看书,根本无法思考,从网上找资料,看书,自己理解,终于差不多了。
守护进程是指在后台运行的,没有控制终端与之相连的进程,它独立于控制终端,通常周期性的执行某种任务。
创建守护进程
(1)让程序在后台执行。方法是调用fork()产生一个子进程,然后使父进程退出。
(2)调用setsid()创建一个新对话期。控制终端、登录会话和进程组通常是从父进程继承下来的,守护进程要摆脱它们,不受它们的影响,方法是调用setsid()使进程成为一个会话组长。setsid()调用成功后,进程成为新的会话组长和进程组长,并与原来的登录会话、进程组和控制终端脱离。
(3)禁止进程重新打开控制终端。经过以上步骤,进程已经成为一个无终端的会话组长,但是它可以重新申请打开一个终端。为了避免这种情况发生,可以通过使进程不再是会话组长来实现。再一次通过fork()创建新的子进程,使调用fork的进程退出。
(4)关闭不再需要的文件描述符。子进程从父进程继承打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。首先获得最高文件描述符值,然后用一个循环程序,关闭0到最高文件描述符值的所有文件描述符。
(5)将当前目录更改为根目录。
(6)子进程从父进程继承的文件创建屏蔽字可能会拒绝某些许可权。为防止这一点,使用unmask(0)将屏蔽字清零。
(7)处理SIGCHLD信号。对于服务器进程,在请求到来时往往生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie),从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。这样,子进程结束时不会产生僵尸进程。
部分代码(摘自linux c这本书)
pid=fork();
if(pid>0)
{
exit(0);//结束父进程,使得子进程成为后台进程
}
else if(pid<0)
{
return -1;
}
//建立一个新进程,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程脱离所有终端
setsid();
//再次建立一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法打开一个新的终端
pid=fork();
if(pid>0)
{
exit(0);
}
else if(pid|<0)
{
return -1;
}
//关闭所有从父进程继承的不再需要的文件描述符
for(i=0;i<NOFILE;close(i++))
//改变工作目录,使得进程不与任何文件系统联系
chdir("/");
//将文件屏蔽字设置为0
umask(0);
//忽略SIGCHLD信号
signal(SIGCHLD,SIG_IGN);
进程退出
正常退出
在main函数中执行return
调用exit函数
调用_exit函数
异常退出
调用about函数
进程收到某个信号,而该信号使程序终止
改变进程优先级
nice改变进程的优先级,getprioity函数返回一组进程的优先级,setprioity函数返回设置指定进程的优先级。
部分代码(摘自linux c)
oldpri=getprioity(PRIO_PROCESS,0);//返回该进程的优先级
newpir=nice(2);//直接设定优先级
fork的for循环
#include <unistd.h>
#include <stdio.h>
int main()
{
int i = 0;
pid_t pid;
for(i = 0; i < 2 ; i++)
{
pid = fork();
if(pid == 0)
printf("%d child pid:%d getpid:%d getppid:%d\n" ,i , pid, getpid(), getppid());
else if(pid == -1)
printf("the create process is fail\n");
else
printf("%d parent pid:%d getpid:%d getppid:%d\n" ,i , pid, getpid(), getppid());
}
}
当前的程序,我们称之为main进程
1,首先在for循环中,i = 0;第一次执行fork(这里会产生一个子进程)
输出结果(第一行):parent,pid为3750,说明此刻执行的是fork之后的父进程
(main进程),3750为新产生的子进程的pid,当前main进程的pid为3749,3052
为main进程的父进程。
2,for循环中,i = 1;第二次执行fork(这里还是会产生一个子进程)
输出结果(第二行):同上,此刻再次新产生了一个子进程, pid = 3751
3,输出结果(第三行):0, child,pid:0,(getpid:3750)说明这个是第一次fork执行时产生的子进程,
getppid:3749,也就是 说:它是main进程的第一个子进程
4,输出结果(第四行):1, child,pid:0,(getpid:3751)说明这个是第二次fork执行时产生的子进程,
getppid:3749,也就是说:它是main进程的第二个子进程
5,这是第三次执行fork函数(是第一次产生的fork子进程,进入for循环后,i = 1,调用fork的结果)
输出结果(第五行):1, parent,pid:3752,(getpid:3750),说明当前的进
程是main进程产生的第一个进程,然后,产生了一个新的进程,进程号为:3752
6,这是第三次执行fork,返回的子进程的内容
输出结果(第六行):1, child , pid:0,(getpid:3752),是第三次fork
后的子进程,也是main进程产生的子进程的子进程
需要注意的是当pid=0时是子进程在跑,当pid!=0时就不是子进程,这样就容易理解了。