进程与进程管理

清屏:system("clear");  //#include <signal.h>

进程环境与进程属性:

什么是进程:简单的说,进程就是程序的一次执行过程。

进程的状态:进程基本三态:运行态、就绪态、封锁态(等待态)——状态可在一定的条件下变化。(其中处于等待的进程分为:可中断进程可以被信号中断其等待;不可中断进程在任何情况下都不可被中断,一直等待条件的满足)。僵死进程:子进程先于父进程退出;从子进程终止到父进程调用wait()之前的时间段内,子进程被称为僵死进程zombie(因为只有父进程能回收,却没有回收其资源,所以父进程一定要wait,不然子进程就真的僵死了)/ 孤儿进程:父进程先于子进程退出,子进程被称为孤儿进程(由进程0/init进程来回收其资源)

进程的执行模式与类型:执行模式分为用户模式—中断/系统调用—>内核模式。进程类型分为用户进程—中断/系统调用—>系统进程。

进程的属性:查看进程的属性:非实时ps aux/auxf、实时top。每一列的含义(有空再看)。

会话(进程组的集合):一个会话可以没有控制终端,也可以与一个终端建立连接,建立连接的会话首进程称为控制进程;一个会话中的几个进程组,可分为一个前台进程组(当会话带有一个终端时,才有前台进程组)和多个后台进程组。其特点:无论何时在终端输入中断信号(ctrl+c)或程序退出信号(ctrl+z),就会有中断信号或退出信号发送给前台进程组的所有进程;进程组中,只要有一个进程存在,该进程组就存在。

通过进程的PID获取/设置进程组号(PGID):getpgid(pid) / int setpgid(pid, pgid)。

获取进程的会话号:pid_t getsid(sid),返回会话号;创建新的会话(而不是修改):pid_t setsid()返回进程号,该进程作为会话首进程。

终端控制函数(返回或操作对象是前台进程组号):pid_t tcgetpgrp(文件描述符)、pid_t tcsetpgrp(文件描述符,pid_t pgrpid)、pid_t tcgetsid(文件描述符)。文件描述符是终端的文件,即通过终端的文件描述符来获得/设置终端的前台进程组号(如果是设置,那么这个新的前台进程组也应该要是同一会话中的进程组),以及获得终端对应的会话ID。

获得进程用户属性:

真实用户号(RUID),即创建进程的用户ID:uid_t getuid()。

有效用户号(EUID),一些情况与UID相同:uid_t geteuid()。——>setuid?。

进程用户号(GID),即创建进程的用户所在组号GID:uid_t getgid()。

有效进程用户组号(EGID),一些情况与GID相同:uid_t getegid()。——>setgid?。

进程的管理:

创建进程:

pid_t fork();执行成功后,如果父进程抢到了执行权,则返回子进程的PID,如果子进程抢到了执行权,则返回0,否则执行失败,返回-1;这个函数是系统调用,对父进程复制,所以子进程几乎与父进程一样,代码相同,对文件采用共享方式,但是数据和文件描述符采用独占方式。

vfork()函数与父进程共享数据空间,这种方式执行效率较高,执行顺序是子先父后。

退出进程:

设置进程即将退出之前,要调用的函数:on_exit(test_exit, (void *)str);该函数有两个参数,第一个是:进程结束前,调用的test_exit函数,如void test_exit(int status, void *arg){...},test_exit函数的第一个参数必须是进程结束时exit(status)的status;第二个(应该不止示例中的参数)的参数就是:传递给test_exit函数的arg,的str。

进程的正常结束void exit(int status):进程的缓冲区数据自动写回、关闭未关闭的文件。#include <stdlib.h>

void _exit(int status):进程的缓冲区数据不会自动写回、不会自动关闭文件。#include <unistd.h>

等待进程(等待子进程中断或结束):pid_t wait(int *status);停止当前进程的执行,直到有信号到来或者其子进程结束。如果子进程已结束,执行wait时会立即返回子进程id和结束状态(保存在参数status中)。status也可以不写,还有一些测试宏(使用需要#include <stdlib.h>,具体有哪些暂时就不管了,考试考到了一看就知道)。有错误(没有子进程)则返回-1。一发现终止的子进程,就根据其pid释放子进程占用的task_struct和系统空间堆栈(就是资源),然后把该子进程的CPU使用时间加到其父进程上。

替换当前进程:#include <unistd.h>

默认情况下,新的代码(替换后的代码)仍可以使用,原来的文件描述符(因为并没有关闭),但是可以用fcntl(fd,SETFD,CLOEXE),设置为在执行完exec系列函数后,自动关闭原来打开的文件描述符。多数的exec函数,最后一个参数都是NULL,除了。而且对于所有的exec,若成功替换执行,则此函数不返回,否则返回-1。

int execl(const char *path, const char *arg, ...),参数:另一个可执行文件(执行这个文件,相当于就是这个进程执行另一个进程的内容,就达到了替换的效果),另一个进程(替换进程)的命令行参数。

int execlp(const char *file, const char *arg, ...):第一个参数:从PATH环境变量中,查找file这个文件,找到便执行。

int execv(const char *path, const char *argv[]):与execl不同,execv只有两个参数,因为所有的命令行参数,都放在argv这个二维数组中(其实就是一个字符串数组)。

int execvp(const char *file, const char *argv[]):execlp ' 1 + execv ' 2 = execvp。

execve(const char *path, const char *argv[], const char *envp[]):envp成为新的环境变量,即如果替换进程用到环境变量,则以envp为准,如char *envp[] = {"PATH=/bin", 0}。

进程的调度算法(暂时不管)。

进程间通信

一些基本概念:

进程同步:一个功能未完成,就不返回(一件事做完,才做下一件)
进程异步:各做各的,每当有一件事做完后就通知调用者。
IPC:进程间通信。每个IPC机制都有唯一ID,通过KEY创建。同一种机制用ID区分。创建key:ftok(文件名,相同的值(如0xFF)),返回Key。

三种继承自UNIX的IPC机制:信号、PIPE、FIFO。
三种继承自System V的IPC机制:信号量、消息队列、共享内存。

有很多地方的地址都设置为NULL,表示由系统自动确定内存起始地址。

同主机,进程间数据同步交互机制:无名管道(PIPE)、有名管道(FIFO)、消息队列、内存映射、共享内存,管道和消息队列都自带同步机制,内存映射、共享内存则无同步。

同主机,进程间异步机制:信号(signal)

信号安装:

signal(信号,函数)——>函数可以不带参数,也可以带一个参数(参数为信号,信号就是一堆int值)。

sigaction(信号,信号结构体,信号结构体)。——>第二个参数安装现在的信号(如果为空指针,则保持原来的信号不变),第三个为之前的信号结构体。

信号结构体:1、.sa_handler信号处理函数。2、.sa_mask信号处理函数执行过程中,阻塞掉的其他信号。3、.sa_flags用于更改指定信号的行为。

管道的创建都不用头文件。

无名管道PIPE:每个管道只有一个内存页面做环形缓冲区,用两个管道可以实现全双工。
管道的创建:int pipe(int files[2]);参数为文件描述符,0为读,1为写;成功返回0,否则-1;写入的数据被读出就消失。然后就可以通过两个文件描述符files[0]、files[1]进行进程间的数据交互。

命名管道可保存为文件系统中,的特殊的设备文件,所以进程随时都可以通过命名管道进行I/O操作。
命名管道创建:通过命令:mkfifo fileName(还可以通过命令使用、删除FIFO);系统调用:int mkfifo(文件名, 管道文件权限),成功则返回文件描述符,否则返回-1。用法:先创建文件(用vi创建都可以,反正共同的文件一定要有),然后就可以得到文件描述符(就和无名一样了,却可读可写)

内存映射:

将文件或其他对象映射进内存(进程的空间):void *mmap(void *start,length,int prot,int flags,int fd,offset),内存起始地址(一般为NULL),长度(从偏移量offset开始算起),内存访问权限(就是PROT_WRITE、PROT_READ),映射对象的类型(匿名访问适用于亲缘进程,而非亲缘进程一般就选MAP_SHARED),文件描述符,偏移量(一般为0);成功执行则返回映射区的指针,否则返回-1。

               (内存访问权限prot)                           (映射对象的类型flags)

让信息立即写回到文件中:int msync(内存起始地址,长度,flag),这个flag和前面的不一样:MS_ASYNC让内核尽快将信息写回文件、MS_SYNC在此函数返回之前将信息写回。

解除进程空间中的映射关系(释放内存段):int munmap(起始地址,长度),起始地址(mmap返回的指针的值);成功则返回0(解除后内存中的更改才会写回文件),否则返回-1,成功解除之后,如果再访问该映射地址,将会产生段错误。

使用方法:非亲缘进程:用open打开文件,得到文件描述符fd,再结合mmap建立内存映射,得到共有的内存地址之后就可以通信了(最后一定记到解除映射,关闭文件);亲缘进程就直接匿名访问内存,不用打开文件。

共享内存:

双方必须要用相同的起始地址的共享内存,否则交互内容不一样
共享内存使用步骤:
创建共享内存int shmget(key,size,flag);成功则返回ID号,否则返回-1。
映射共享内存,将共享内存映射到具体的进程空间void *shmat(id,addr,flag),addr通常为NULL,flag为映射地址访问权限;成功则返回共享内存起始地址,否则返回-1。
解除映射int shmdt(shmat返回的地址),如果失败则返回-1;两边要同时解除映射。
共享内存控制(用来删除共享内存就行了)int shmctl(ID,IPC_RMID,0),这一步只需要消费者来扫尾就行了

shmget函数的flag:

  1. IPC_CREAT:如果共享内存不存在则创建,否则打开。
  2. IPC_EXCL:如果共享内存不存在则创建,否则报错。

消息队列:
创建和打开消息队列int msgget(key,IPC_CREAT),第二个参数为flag和shmget的flag一样;成功则返回ID号。
添加/发送消息int msgsnd(ID,结构体缓冲区,大小,0),成功则返回0,否则返回-1。
读取/接收消息int msgrcv(ID,缓冲区,大小,类型,0),其中类型可以为0,表示可以接收任何类型的消息;成功则返回0,否则返回-1。
控制消息队列int msgctl(ID,IPC_RMID,0),一般直接用来删除消息队列就行了,所以和删除共享内存的shmctl一样,设置得比较简单。

//msgsnd函数的第二个参数void *buf :表示自定义的msgbuf结构体
struct msgbuf {
long type; //一般要求接收和发送不一样就行,如send=1,recieve=2
char msg[自定义大小];
};

实际使用(前面说明,后面例子):
首先使用ftok,创建key,mqkey = ftok(fileName, 0xFF);
然后用msgget创建消息队列,mqid = msgget(mqkey, IPC_CREAT | IPC_EXCL | 0666);
设置好缓冲区:struct msgbuf msg; msg.mtype = 随便但一致; 。。。
然后发送消息msgsnd,msgsnd(mqid, &msg, strlen(msg.mtext) + 1, 0);或者接收消息msgrcv,msgrcv(mqid, &msg, 256, 2, 0);
最后删除消息队列(至于谁来扫尾),msgctl(mqid, IPC_RMID, NULL);

同主机,进程间同步机制:信号量(semaphore)

信号量(互斥和同步):

//PV操作(原语):P为等待(-),V为信号(+)
procedure p(var s:samephore){
s.value=s.value-1;
if (s.value<0) sleep(s.queue);
} procedure v(var s:samephore){
s.value=s.value+1;
if (s.value<=0) wakeup(s.queue);
}

创建一个信号量 / 取得一个信号量的ID:int semget(key,信号量数,flag),信号量数量,一般为1,flag 访问权限 | IPC_CREAT | IPC_EXCL。

改变信号量的值:int semop(ID,struct sembuf *sops,nsops),第二个参数为系统结构数组,第三个为数组元素个数。

//函数semop的第二个参数struct sembuf *sops :解释这个结构体
struct sembuf {
unsigned short sem_num; //no:信号量编号
short sem_op; //对信号量的操作,若sem_op>0,则信号量+sem_op;若sem_op<0,则信号量-sem_op;若sem_op=0,表示判断信号量是否=0。
short sem_flg; //建议选择SEM_UNDO(进程退出时,执行 信号量解除(undo) 操作);IPC_NOWAIT(指定操作若还未完成,进程也不等待,立即返回-1)
};

直接控制信号量信息:int semctl(ID,semnum,cmd,。。。),该函数最多只有4个参数:

  1. semnum为信号量编号,若为设置为0,则表示整个信号量的集合(此时第四个参数无效)。
  2. 如果对整个信号量集合控制,则须#include <ipc.h>,此时cmd的含义与msgctl相同,有如下值:
    IPC_RMID:删除整个信号量集合
    IPC_SET:设置ipc_perm参数
    IPC_STAT:获取ipc_perm参数
    IPC_INFO:获取系统信息 
  3. 若对单个。。。(今后再议)

网络中,主机间数据交互机制:套接字(socket)、RPC。

Linux进程学习的更多相关文章

  1. Linux 进程学习

    1.linux进程间通讯         继承unix进程间通讯:管道 信号         AT&T :system V IPC 通讯进程只能在单个计算机 :信号量  消息队列 共享内存   ...

  2. Linux进程学习(孤儿进程和守护进程)

    孤儿进程和守护进程 通过前面的学习我们了解了如何通过fork()函数和vfork()函数来创建一个进程.现在 我们继续深入来学习两个特殊的进程:孤儿进程和守护进程 一.孤儿进程 1.什么是 孤儿进程如 ...

  3. Linux进程学习 - 孤儿进程和守护进程

    孤儿进程和守护进程 通过前面的学习我们了解了如何通过fork()函数和vfork()函数来创建一个进程.现在 我们继续深入来学习两个特殊的进程:孤儿进程和守护进程 一.孤儿进程 1.什么是 孤儿进程如 ...

  4. linux进程学习笔记

    学习了linux下的进程,觉得应该整理一下,忘得差不多了,顺便回顾一下. 学而时习之,不亦说乎~~ 进程笔记 ,什么是进程? The Single UNIX Specification, Versio ...

  5. Linux 进程学习笔记

    1.什么是程序?什么是进程?它们有什么区别? 定义: 程序:程序(Program)是一个静态的命令集合,程序一般放在磁盘中,然后通过用户的执行来触发.触发后程序会加载到内存中成为一个个体,就是进程. ...

  6. linux 进程学习笔记-进程ID,PID

    PID,进程号 , 范围在2~(??为什么需要这么多),而一个名为idle (或swapper)的进程占据的编号0,init进程占据了编号1. 进程0和进程1 : 系统启动时会从无到有地创建进程0,它 ...

  7. linux进程学习-进程描述符的存储

    当进程被新建时,内核会给进程分配一个8K的空间作为进程的内核堆栈.同时我们知道task_struct结构体也会被创建,但有意思的是,内核不会给task_struct单独分别空间,而是直接将其扔到8k的 ...

  8. linux 进程学习笔记-进程调度

    在分时系统中,系统将CPU时间划分成无数个时间片(quantum)分配给不同的进程,一个时间片只执行一个进程,并且不停地切换,以让用户感觉到各个进程是在“同时运行”,这中间所需要的策略和算法便是进程调 ...

  9. linux 进程学习笔记-进程跟踪

    进程跟踪 long ptrace(enum __ptrace_request request, pid_t pid, void *addr, void *data); Linux用ptrace来进行进 ...

  10. linux 进程学习笔记-进程信号sigal

    信号(或软中断)是在软件层次上对中断的一个模拟,其运行在“用户空间”,一个进程对另外一个或几个进程通过发送信号来实现异步通信.当接收进程接收到信号后,其可以注册一下处理函数来说对这些信号进行处理(也可 ...

随机推荐

  1. [SQL] SQL 基础知识梳理(二) - 查询基础

    SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...

  2. 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)

    在前两篇博客中详细的介绍了"策略模式"和“观察者模式”,今天我们就通过花瓶与鲜花的例子来类比一下“装饰模式”(Decorator Pattern).在“装饰模式”中很好的提现了开放 ...

  3. spring ioc

    spring ioc是spring的核心之一,也是spring体系的基础,那么spring ioc所依赖的底层技术是什么的?反射,以前我们开发程序的时候对象之间的相互调用需要用new来实现,现在所有的 ...

  4. 利用Python进行数据分析(2) 尝试处理一份JSON数据并生成条形图

    一.JSON 数据准备 首先准备一份 JSON 数据,这份数据共有 3560 条内容,每条内容结构如下: 本示例主要是以 tz(timezone 时区) 这一字段的值,分析这份数据里时区的分布情况. ...

  5. 一步一步开发Game服务器(四)地图线程

    时隔这么久 才再一次的回归正题继续讲解游戏服务器开发. 开始讲解前有一个问题需要修正.之前讲的线程和定时器线程的时候是分开的. 但是真正地图线程与之前的线程模型是有区别的. 为什么会有区别呢?一个地图 ...

  6. Spark 入门

    Spark 入门 目录 一. 1. 2. 3. 二. 三. 1. 2. 3. (1) (2) (3) 4. 5. 四. 1. 2. 3. 4. 5. 五.         Spark Shell使用 ...

  7. Basic Tutorials of Redis(3) -Hash

    When you first saw the name of Hash,what do you think?HashSet,HashTable or other data structs of C#? ...

  8. sqlserver 游标的使用

    declare @temp_temp uniqueidentifier--临时变量 DECLARE aaa CURSOR for select Id from A ------------------ ...

  9. C# 之 EXCEL导入导出

    以下方式是本人总结的一些经验,肯定有很多种方法,在此先记下,留待以后补充... 希望朋友们一起来探讨相关想法,请在下方留言. A-1:EXCEL模板导出 非常简单,将EXCEL模板上传到项目中后,将其 ...

  10. Aspose.Words简单生成word文档

    Aspose.Words简单生成word文档 Aspose.Words.Document doc = new Aspose.Words.Document(); Aspose.Words.Documen ...