您的位置:首页 > 运维架构 > Linux

Linux+C学习笔记之进程管理

2011-12-18 10:39 405 查看
1.进程的概念

进程的概念首先在20世纪60年代初期由MIT的Multics系统和IBM的TSS/360系统中引入的。

(1)进程是一个独立的可调度的活动(E.Cohen,D.Jofferson)。

(2)进程是一个抽象实体,当它执行某个任务时,将要分配和释放各种资源(P.Denning)。

(3)进程是可以并行执行的计算部分(S.E.Madnick,J.T.Donovan)。

进程和程序有本质的区别:程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念;而进程是一个动态的概念,它是程序执行的过程,包括了动态创建、调度和消亡的整个过程,它是程序执行和资源管理的最小单位。

进程是一个具有独立功能的程序的一次运行活动。

2.进程的特点

动态性,它是一次运行活动。

并发性(异步性),可以同时执行多个不同的进程,要与同步性区分开来。

独立性,每个进程都拥有自己独立的资源空间。

结构化,

3.进程的三个状态:就绪,执行,阻塞,进程刚开始是进入就绪态。

4.进程ID

进程ID(PID):标识进程的唯一数字

父进程的ID(PPID)

启动进程的用户ID(UID)

5.进程互斥

进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资源为止。

6.临界资源

操作系统中将一次只允许一个进程访问的资源称为临界资源。

7.临界区

进程中访问临界资源的那段程序代码称为临界区。为实现对临界资源的互斥访问,应保证各个进程互斥地进入各自的临界区。

8.进程同步

一组并发进程按一定的顺序执行的过程称为进程间的同步。具有同步关系的一组并发进程称为合作进程,合作进程间互相发送的信号称为消息或事件。

9.进程调度

概念:按一定算法,从一组待运行的进程中选出一个来占有CPU运行。

调度方式:

抢占式

非抢占式

10.调度算法

先来先服务调度算法

短进程优先调度算法

高优先级优先调度算法

时间片轮转法

11.死锁

多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不能再向前推进。

12.获取进程ID

#include <sys/types.h>

#include <unistd.h>

pid_t getpid(void)

获取本进程ID。

pid_t getppid(void)

获取父进程ID。

13.进程创建-fork

#include <unistd.h>

pid_t fork(void)

功能:创建子进程 fork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:

1. 在父进程中,fork返回新创建的子进程的PID;

2. 在子进程中,fork返回0;

3. 如果出现错误,fork返回一个负值

pid_t vfork(void)

功能:创建子进程。

注意:vfork 出来的子进程里,不能修改任何变量,子进程也不能通过 return/exit() 的方式来退出. vfork 出来的子进程唯一能做的事情有两件: 1. 通过 _exit() 来结束 2. 调用 exec 类系统调用(比如 execlp, execvp等)来载入和执行其他程序。 在vfork子进程中执行任何非上述两个以外的动作,得到的结果都是不可预测的。

14.处理僵尸进程的几种方法

(1)使用wait()函数

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait (int* status)

功能:

阻塞该进程,直到其某个子进程退出。

例:

/*
* wait.c
*  Created on: 2011-12-12
*      Author: Betamark
*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<unistd.h>

//子进程已死,父进程还在实为僵尸进程
//父进程先死,子进程就是孤儿进程,被1号进程收养
int main(){
pid_t child=fork();
if(child<0){
perror("fork");
exit(1);
}else if(child==0){
printf("child,pid=%d,ppid=%d\n",getpid(),getppid());
}else{
wait(NULL);
sleep(10);
printf("father,cpid=%d,pid=%d\n",child,getpid());
}

return 0;
}


缺点:父进程必须等到子进程结束才能做事,父进程没有自由。

(2)使用双fork()来实现

例:

/*
* double_fork.c
*
*  Created on: 2011-12-12
*      Author: Betamark
*/
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>

int main() {
pid_t child = fork();

if (child < 0) {
perror("fork");
exit(1);
} else if (child == 0) {
pid_t child2 = fork();
if (child2 < 0) {
perror("fork2");
exit(1);
} else if (child2 > 0) {
exit(0);//一开始就退出,避免第二代成为僵尸,且给其父进程自由
} else {
printf("child2 working ...\n");
}
} else {
wait(NULL);//火化第一代的子进程
sleep(10);
printf("parent working ...\n");
}
return 0;
}


15.exec函数族

(1)exec函数族说明

fork函数是用于创建一个子进程,该子进程几乎拷贝了父进程的全部内容。

exec函数族就提供了一个在进程中启动另一个程序执行的方法。它可以根据指定的路径和文件名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,在执行完之后,原调用进程的内容除了进程号外,其他全部被新的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是Linux下任何可执行的脚本文件。

进程的创建

exec函数族

(2)exec函数族语法

实际上,在Linux中并没有exec()函数,而是以exec开头的函数族,它们之间语法有细微差别。

exec函数族语法格式如下所示。

头文件

#include <unistd.h>

函数原型

int execl(const char *path, const char *arg, ...);

int execv(const char *path, char *const argv[]);

int execle(const char *path, const char *arg, ..., char *const envp[]);

int execve(const char *path, char *const argv[], char *const envp[]);

int execlp(const char *file, const char *arg, ...);

int execvp(const char *file, char *const argv[]);

函数返回值

出错1:-1

16.进程的退出

exit和_exit

(1)exit和_exit函数说明

exit和_exit函数都是用来终止进程的。当程序执行到exit或_exit时,进程会无条件地停止剩下的所有操作,清除包括PCB在内的各种数据结构,并终止本进程的运行。

exit()函数与_exit()函数的区别就在于exit()函数在调用exit系统调用之前要检查进程中文件的打开情况,把文件缓冲区中的内容写回文件。

如果用_exit()函数直接将进程关闭,缓冲区中的数据就会丢失。因此,若想保证数据的完整性,就要使用exit()函数。

(2)exit和_exit函数语法

exit和_exit函数的语法如下所示。

头文件

exit:#include <stdlib.h>

_exit:#include <unistd.h>

函数原型

void exit/_exit(int status); /*利用该参数传递进程结束时的状态。一般来说,0表示正常结束;其他的数值表示出现了错误,进程非正常结束*/

(3)exit和_exit使用实例

exit和_exit函数的调用很简单,输入状态参数即可,如下所示:

exit(0);

_exit(-1);

17. #include<stdlib.h>

int system(const char *command)

18.守护进程

(1)守护进程的概念和应用原因

就是通常所说的Daemon进程,是Linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导装入时启动,在系统关闭时终止。

如果想让某个进程不因为用户或终端或其他的变化而受到影响,那么就必须把这个进程变成一个守护进程。

(2)编写规则

1.创建子进程,父进程退出

2.在子进程中创建新会话

进程组

进程组是一个或多个进程的集合。进程组由进程组ID来惟一标识

会话期

会话组是一个或多个进程组的集合。

3.改变当前目录为根目录

4.重设文件权限掩码

5.关闭文件描述符

例:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#define MAXFILE 65535
int main() {
pid_t pc;
int i, fd, len;
char *buf = "This is a Dameon\n";
len = strlen(buf);
/*父进程退出*/
pc = fork();
if (pc < 0) {
printf("error fork\n");
exit(1);
} else if (pc > 0)
exit(0);
/*在子进程中创建*/
setsid();
/*改变当前目录为根目录*/
chdir("/");
/*重设文件权限掩码*/
umask(0);
/*关闭文件描述符*/
for (i = 0; i < MAXFILE; i++)
close(i);
/*这时创建完守护进程,以下开始正式进入守护进程工作*/
while (1) {
if ((fd = open("/tmp/dameon.log", O_CREAT | O_WRONLY | O_APPEND, 0600))
< 0) {
perror("open");
exit(1);
}
write(fd, buf, len + 1);
close(fd);
sleep(5);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: