进程控制

网友投稿 792 2022-09-22

进程控制

进程控制

几个基本概念

进程

进程的经典定义就是一个执行中的程序的实例,系统中的每一个程序都是运行在某个进程的上下文中的,上下文由程序正确运行所需要的状态组成的,这个状态包括存放载存储器中的程序的代码和数据,它的栈,通用目的寄存器的内容,程序计数器,环境变量以及打开文件描述符的集合。

关键抽象

1.一个独立的逻辑控制流,他提供一个假象,好像我们的程序独占使用使用处理器。

2.一个私有的空间地址,它提供一个假象,好像我们的程序独占地使用存储器系统。

调度

在程序执行的某些时刻,内核可以决定抢占当前进程,并重新开始一个先前被抢占的进程。

上下文切换

1)保存当前进程的上下文;2)回复某个先前被抢占的进程被保存的上下文;3)将控制传递给这个新回复的进程。

fork()的封装

fork函数用于在进程中开启一个子进程。fork函数被调用一次会有两次返回:一个在调用父进程,返回子进程的PID,一个在子进程,返回0.

当Unix系统函数遇到错误时,他们典型地会返回-1,并设置全局整数变量erron来表示出了什么错,常用检错方法如下:

#include #include #include #include #include #include int main() { pid_t pid; if(pid=fork()<0){ printf("fork error:%s\n",strerror(errno)); exit(0); } printf("Hello!\n"); return 1; }

下面对fork进行封装。

#include #include #include #include #include #include pid_t Fork(); void unix_error(const char *msg); int main() { int x=1; pid_t pid; pid=Fork(); if(pid==0){ printf("child:x=%d\n",++x); printf("ppid=%d, pid=%d, i=%d \n", getppid(), getpid(), x); exit(0); } else{ printf("father:x=%d\n",--x); printf("ppid=%d, pid=%d, i=%d \n", getppid(), getpid(), x); } return 1; } pid_t Fork() { pid_t pid; if(pid=fork()<0) unix_error("Fork error"); return pid; } void unix_error(const char *msg) { printf("%s:%s\n",msg,strerror(errno)); exit(0); }

这样,对fork的调用只用一行代码就可以搞定。编译运行,得到的结果

并不是我们预想的结果,发现都是打印的child,但实际上,pid显示 为两个不同的进程,所以封装出现了问题。

仔细查看,发现这一行

if(pid=fork()<0) unix_error("Fork error");

有问题,根据c语言的符号运算顺序,赋值运算是放在最后的,所以应该改在pid=fork() 上添加一组括号。如下:

if((pid=fork())<0) unix_error("Fork error");

编译运行:

得到正确结果。

例子解析

在语句fpid=fork()之前,只有一个进程在执行这段代码,但在这条语句之后,就变成两个进程在执行了,这两个进程的几乎完全相同,将要执行的下一条语句都是if(fpid<0)…… 为什么两个进程的fpid不同呢,这与fork函数的特性有关。fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值: 1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值; 在fork函数执行完毕后,如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。我们可以通过fork返回的值来判断当前进程是子进程还是父进程。 引用一位网友的话来解释fpid的值为什么在父子进程中不同。“其实就相当于链表,进程形成了链表,父进程的fpid(p 意味point)指向子进程的进程id, 因为子进程没有子进程,所以其fpid为0. fork出错可能有两种原因: 1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。 2)系统内存不足,这时errno的值被设置为ENOMEM。 创建新进程成功后,系统中出现两个基本完全相同的进程,这两个进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。 每个进程都有一个独特(互不相同)的进程标识符(process ID),可以通过getpid()函数获得,还有一个记录父进程pid的变量,可以通过getppid()函数获得变量的值。

还有下面值得注意的地方:

并发执行:父进程和子进程是并发执行的独立进程。内核能以任何形式替换执行他们的逻辑控制流中的指令。

相同的但是独立的地址空间:每个进程有相同的用户栈,相同的本地变量值,相同的堆,相同的全局变量值以及相同的代码,同时,他们会有自己私有的地址空间,父进程和子进程对x做的任何改变都是独立的,不会反映在另一个进程的存储器中。

关于进程描述符(task_struct)

在linux 中每一个进程都由task_struct 数据结构来定义. task_struct就是我们通常所说的PCB.她是对进程控制的唯一手段也是最有效的手段. 当我们调用fork() 时, 系统会为我们产生一个task_struct结构。然后从父进程,那里继承一些数据, 并把新的进程插入到进程树中, 以待进行进程管理。

结构体中一些关键的字段有:

state:记录进程状态;

pid:进程标识符;

binfmt:可执行文件的格式;

exit_signal :终止信号;

pdeath_singnal:父进程消亡的时候发出的信号;

comm:调用可执行程序创建进程时的程序名;

ptrace:调用ptrace()时设置的字段。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:你知道Python中a += b和a = a + b的结果是不一样的吗?(Python中!)
下一篇:谈谈个人对linux和unix的不同角度
相关文章

 发表评论

暂时没有评论,来抢沙发吧~