Linux 进程

来源:互联网 发布:移动加强网络信息安全 编辑:程序博客网 时间:2024/06/05 18:51
Linux进程
进程的四个要素
1.一段代码供该进程运行
2.专用的系统堆栈空间
3.一个由 task_struct 结构实现的进程控制块
4.独立的存储空间


进程的关系和分类
进程号为1的进程是初始化进程(init)
进程间关系:
p_opptr(祖先)
p_pptr(父进程)
p_cptr(子进程)
p_ysptr(弟进程)
p_osptr(兄进程)

进程的类型
交互式进程:由一个shell启动
批处理进程:和终端没有关系
守护进程:Linux启动时启动的进程,在后台运行

进程状态:
进程的状态是互斥的,同一进程在同一时刻只能处于其中一个状态
TASK_RUNNING表示进程要么正在执行,要么正要准备执行。
TASK_INTERRUPTIBLE表示进程被阻塞(睡眠),直到某个条件变为真。条件一旦达成,进程的状态就被设置为TASK_RUNNING。
TASK_UNINTERRUPTIBLE的意义与TASK_INTERRUPTIBLE类似,除了不能通过接受一个信号来唤醒以外。
    TASK_STOPPED表示进程被停止执行。
    TASK_TRACED表示进程被debugger等进程监视。
    EXIT_ZOMBIE表示进程的执行被终止,但是其父进程还没有使用wait()等系统调用来获知它的终止信息。
    EXIT_DEAD表示进程的最终状态。
EXIT_ZOMBIE和EXIT_DEAD也可以存放在exit_state成员中。
状态对用户是透明的,切换过程成为进程调度


进程描述符和标识地址


进程描述符
struct task_struct {volatile long state; /*说明了该进程是否可以执行,还是可中断等信息 -1代表不可运行 0代表可运行 >0代表已停止*/unsigned long flags; /*Flage 是进程号,在调用fork()时给出*/int sigpending; /*进程上是否有待处理的信号*/unsigned int rt_protity;//优先级pid_t pid;//进程标识号pid_t tgid;//进程组号/*.....*/};


进程标识符 process ID 是进程描述符中最重要的组成部分,是唯一的非负整数
pid_t 标识  32位的无符号整形数据
与16位系统兼容  内核上允许的进程标识符为0-32767


特殊的进程
0 对应交换进程 执行多进程调用
1 初始化进程init 负责linux的启动工作  不会终止
2 页守护进程 虚拟存储系统的分页操作


得到当前的进程标识符
#include <sys/types.h>#include <unistd.h>pid_t getpid(void);pid_t getppid(void);//父进程的进程标识符

uid_t getiid(void);//实际用户标识符uid_t geteuid(void);//有效用户标识符gid_t getgid(void);//实际组标识符gid_t getegid(void)//调用进程的有效组标识符

进程调度
两种模式:用户模式 系统模式
//@@Linux中进程不能被抢占,只要能运行就不会停止
采用预加载策略:每个进程只运行很短的时间 200ms 过后系统选择另一个进程运行
基于优先级的简单调度算法
/*@@处理中的寄存器及上下文状态保存在task_struct 中
为了将处理器的时间合理地分配给系统的每个执行过程,调度管理器将时间信息保存在task_struct中*/


进程的创建
#include <unistd.h>pid_t fork(void);RETURN VALUE       On success, the PID of the child process  is  returned  in  the  parent,  and  0  is       returned  in  the child.  On failure, -1 is returned in the parent, no child process       is created, and errno is set appropriately.
子进程的返回值为 0
fork返回后,父子进程的返回值是随机的.


/*@@进程:子进程会从父进程拷贝数据空间 堆栈 堆 和父进程共享正文段拷贝的仅是一个副本 和父进程的部分是完全独立的子进程的变量修改不会影响父进程@@子进程对变量的修改不会影响到父进程子进程会复制父进程的打开的文件描述符此时父子进程的每个打开文件描述符会共享一个同一个文件表项fork创建的子进程和父进程共享一个文件的偏移量,此时如果父进程和子进程同时对一个文件进行操作且没有任何形式的同步操作,会出现写文件的混乱采取两种方法处理fork后,处理文件描述符1.父进程等待子进程执行完.2.父进程和子进程各自执行其相应的程序段.在这种情况下父子进程关闭掉其不需要的文件描述符,防止互相干扰.@@父子进程的区别:1.fork的返回值不同2.进程ID不同3.子进程的tms_utims,tms_atims,tms_cutime,及tms_ustime均被设置为04.子进程不会继承父进程的文件锁5.子进程的未处理闹钟会被清除6.子进程的未处理信号即为空*/

vfork() 创建一个进程后,自动实现exec系列函数的功能
#include <sys/types.h>#include <unistd.h>pid_t vfork(void);

fork和vfork的区别:
1.vfork完全不需要拷贝父进程的数据段,在子进程没有调用exec系列函数之前,子进程与父进程共享数据段
2.vfork会自动调用exec函数去执行另外一个程序
3.fork不会对父子进程的执行次序进行限制,vfork调用中字进程先执行
父进程挂起,直到执行exec 或exit之后.父子进程的次序不再限制


使用fork函数后,往往需要调用exec函数执行另一个程序
执行时,进程执行的程序立刻替换为新的程序,新程序则从main函数开始执行,进程描述符不变


#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg,, char * const envp[]);int execv(const char *path, char *const argv[]);//真正意义的系统调用int execvp(const char *file, char *const argv[]);


//进程的退出
#include<stdlib.h>#include<unistd.h>void exit(int stats)void _exit(int stats)void _Exit(int stats)无返回值可以通过wait 或 waitPid 来获得终止状态字父进程通过检查终止状态字来获得子进程的状态
以下三种状态认为是位定义的:
1.调用时未带任何终止状态
2.main函数执行了一个无返回值的return语句
3.main函数的返回值不是一个整数


exit与_exit的区别,前者调用之前检查文件状态 将缓冲区的内容写回文件,后者直接停止运行,清除内存空间


Linux内核为每个已经结束的进程保留一定的信息,至少包含 进程标识符 终止状态字 等系统调用来获知它的终止信息。
通常把已经结束父进程尚未检查其终止的状态的进程叫做僵尸进程.


父进程先于子进程结束,init进程自动成为该子进程的父进程


//result 父进程执行一次 子进程执行多次
[zhjh@localhost code]$ g++ fork.cpp 
[zhjh@localhost code]$ ./a.out 
0 pid:7699 ppid: 4876Parent:7
0 pid:7700 ppid: 7699 Child:0
1 pid:7700 ppid: 1 Child:0
2 pid:7700 ppid: 1 Child:0


//进程的销毁


父进程对于退出的子进程默认是不进行处理的,会导致僵尸进程过多浪费系统资源

#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);//调用成功返回进程的标识符,失败返回-1pid_t waitpid(pid_t pid, int *status, int options);int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);

调用之后的情况:
1.父进程的所有子进程都在运行,阻塞父进程自身,等待子进程结束
2.子进程结束父进程取得子进程的终止状态,立即返回
3.父进程无任何子进程立即出错返回

wait 在一个子进程终止前,wait函数让父进程阻塞以等待子进程退出


waitpid 有一个参数可以让父进程不阻塞,而且多个子进程的情况下,一个子进程退出返回该子进程的标识符
waitpid 
wait时父进程的任何一个子进程返回,wait都会返回,waitpid 可以指定等待的进程
pid>0  等待特定的进程
pid=-1 等待任何一个进程
pid<-1 等待指定进程组中的任何子进程

//更改用户ID和组ID
#include <sys/types.h>#include <unistd.h> int setuid(uid_t uid);//设置 实际用户ID 有效ID int setgid(gid_t gid);//设置 组实际ID 有效ID zero is returned.  On error, -1 is returned//更改有效用户ID和有效用户组ID seteid()setegid()


//进程组
每一个进程除了有一个进程ID外,还隶属于一个进程组(唯一组ID)


#include <unistd.h>int setpgid(pid_t pid, pid_t pgid);pid_t getpgrp(void);                /* POSIX.1 version */


每个进程组都有个组长进程leader   组长进程leader的标识进程组ID等于进程ID
组长进程可以创建进程组


只要进程组中有一个进程存在,则进程组就存在 与组长进程是否终止无关
进程组中的最后一个进程可以终止也可以加入其他的进程组

#include <unistd.h>int setpgid(pid_t pid, pid_t pgid);//加入现存的组或创建新的进程组eturn zero.  On error, -1 is returned

将pid进程的进程组设置为pgid 相等则为组长

//会话
会话是若干进程组的集合,可包含多个进程组,但只有一个前台进程组

进程调用setsid
pid_t setsid(void);
成功返回进程组ID 错误返回-1


会话和进程组的特性
每个会话都有一个控制终端,这是会话最重要的特性之一


对控制终端进行读写的方法是打开文件 /dev/tty


//system函数
Linux内核提供了大量现成的命令 用户调用
#include <stdlib.h>
int system(const char *command);

#include<sys/types.h>#include<sys/wait.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<iostream>using namespace std;int main(int argc,char* argv[]){    int status;    if(  (status=system("date"))<0 )    {           perror("error");        exit(0);    }       cout<<status<<endl;    return 0;}/*//结果:zhjh@localhost code]$ ./a.out Sat Oct 18 22:13:18 CST 20140*/

//进程统计 accton

和函数
#include <unistd.h>int acct(const char *filename);

//进程时间
测量进程的执行时间
时钟时间:进程运行的时间总量,值与系统中同时运行的进程数有关
用户CPU时间:执行用户指令所用的时间
系统CPU时间:内核程序经历的时间

/*[zhjh@localhost code]$ time ./a.out this is input.real    0m0.002suser    0m0.001ssys     0m0.001s*/

//取得系统的时钟滴答数#include <unistd.h>long sysconf(int name);//time 函数#include<sys/times.h>clock_t times(struct tms *buf);参数tms结构体指针  韩慧进程执行话费是时钟时间(滴答数) 失败-1


0 0
原创粉丝点击