Linux学习-进程


进程基本概念


1.进程:
    程序:存放在外存中的一段数据组成的文件
    进程:是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡

进程相关命令


    1.top 
      动态查看当前系统中的所有进程信息(根据CPU占用率排序)

        PID:唯一识别进程的ID号(>0)
        NI优先级:Linux系统中数值低,优先级高(-20 - 19)  Windows系统中数值高,优先级高
        进程状态: 
            R   运行态/就绪态
            S   睡眠态/可唤醒等待态
            D   不可唤醒等待态
            T   暂停态
            Z   僵尸态
            X   结束态

        q退出 

    2.nice
      以指定优先级来运行进程

      示例:
        nice -n 优先级 要执行的集成任务

      renice 
      重新设定一个正在运行的进程的优先级
      
      示例:
        renice -n 优先级 进程PID 

    3.kill
      杀死指定的进程任务

      示例:
        kill -9 进程PID 

      killall 
      杀死进程名对应的所有进程任务

      示例:
        killall -9 进程名 

    4.ps -ef 
      查看当前时刻所有进程的信息

      PPID:父进程的ID号 

      ps -ef | grep a.out  通过管道将前面命令的输出作为面命令的输入

    5.pstree
      查看进程树关系

    6.ps -aux 
      查看当前时刻的进程信息

    7.    ./a.out &
      将a.out任务放在后台执行
    
    8.jobs
      查看一个终端下后台执行的所有任务
    
    9.fg 编号
      将后台任务放到前台执

进程的创建
    

32bits:

    一个进程在运行时,操作系统会为该进程分配 0 - 4G 虚拟内存空间,分为文本段、数据段、系统数据段

    文本段:
      也称为文本区,存放代码和指令
    
    数据段:
      也称为数据区,可以细分为:
            1.字符串常量区
            2.未初始化全局变量/静态变量
            3.已初始化全局变量/静态变量 
    
    系统数据段:
      包含 堆区(大概几G):1,程序员手动管理,空间要自己malloc和free

                         2,堆的增长方向是自低向高

              栈区(基本就是8MB):1,局部变量存放在栈区

                        2,未经初始化的随机值

                        3,代码执行到变量定义的时候开辟空间

                        4,超过变量作用域回收空间

                        5,栈的增长方向自高向低

4.进程中虚拟地址和物理地址的关系
    1. 0 - 4G虚拟内存空间只有一个
    2. 实际物理地址中每个进程空间独立
    3. 通过MMU内存映射单元,单一个进程执行时,将物理地址中的数据加载到虚拟地址中运行

进程的调度


    1.常见的调度算法:
      1.先来先执行,后来后执行
      2.高优先级调度算法
      3.时间片轮转调度算法
      4.多级队列反馈调度算法
      5.负载均衡调度算法

      时间片:
        1.CPU在一个任务中的运行时间称为一个时间片

    2.宏观并行,微观串行

    3.进程的状态:
        R     运行态、就绪态 
        S     睡眠态/可唤醒等待态 
        D     不可唤醒等待态 
        T     暂停态
        Z     僵尸态
        X     结束态

进程相关函数接口


    1.进程的创建 
      fork 
      pid_t fork(void);
      功能:
        创建一个子进程,新创建的进程称为原来进程的子进程,原来的进程称为新进程的父进程
      参数:
        void 缺省
      返回值:
        成功子进程返回0 
        父进程返回子进程的PID 
        失败返回-1 

       父进程调用fork创建子进程,子进程拷贝父进程的文本段、数据段、系统数据段  ,除了内核不拷贝。

        宏观并行,微观是切换进行,具体根据情况。

      getpid
      pid_t getpid(void);
      功能:
        获得调用进程的PID号
      
      getppid
      pid_t getppid(void);
      功能:
        获得调用进程的PPID,获得父进程的PID

  2.exit
    void exit(int status);
    功能:
      让进程结束
    参数:
      status:进程结束的状态
    返回值:
      缺省

    exit在主函数中使用和return效果一致
    exit会刷新缓存区

    _exit
    void _exit(int status);
    功能:
      让进程直接结束,不会刷新缓存区。
    参数:
      status:进程结束的状态
    返回值:
      缺省

进程的消亡

  1.僵尸进程:
    进程代码执行结束,空间没有被回收,称为僵尸进程
  2.如何避免产生僵尸进程?
    1.让父进程先结束
    2.让父进程回收子进程空间

  3.孤儿进程:
      进程的父进程先结束,此时该进程称为孤儿进程,被系统进程收养,进程再结束时,会被系统进程回收进程空间

8.wait 
  pid_t wait(int *wstatus);
  功能:
    回收子进程空间
  参数:
    wstatus:存放子进程结束状态空间的首地址
  返回值:
    成功返回回收到的子进程PID
    失败返回-1 

  1.wait函数具有阻塞功能
  2.wait函数具有同步功能

  WIFEXITED(wstatus)
  进程是否正常退出 

  WEXITSTATUS(wstatus)
  进程结束状态值

  WIFSIGNALED(wstatus)
  进程是否被信号杀死

  WTERMSIG(wstatus)
  获得杀死进程的信号编号

实例:创建九个子进程

exec函数族


    extern char **environ;

    int execl(const char *path, const char *arg, ...
                    /* (char  *) NULL */);  : 
    int execlp(const char *file, const char *arg, ...
                    /* (char  *) NULL */);
    int execle(const char *path, const char *arg, ...
                    /*, (char *) NULL, char * const envp[] */);
    int execv(const char *path, char *const argv[]);
    int execvp(const char *file, char *const argv[]);
    int execvpe(const char *file, char *const argv[],
                    char *const envp[]);

        执行结束子程序整个代码也会结束。

    功能:
        利用进程空间执行另外一份代码
    
    l:参数以列表形式传递
    v:参数以指针数组形式传递
    e:更新环境变量
    p:在系统指定目录下查找文件

    getenv
    char *getenv(const char *name);
    功能:
        获得环境变量名对应的值,也就是平时的PATH     
    
    setenv
    int setenv(const char *name, const char *value, int overwrite);
    功能:
        设置环境变量的值
    参数:
        name:环境变量名
        value:环境变量的值
        overwrite:非0 覆盖
                  0   不覆盖
    返回值:
        成功返回0 
        失败返回-1 

进程间通讯

1.进程间的通信:
    1.管道
    2.信号
    3.消息队列
    4.共享内存
    5.信号灯
    6.套接字

管道


    1.无名管道
        无名管道只能用于具有亲缘关系的进程间通信,先创建管道,在创建子进程,子进程就会复制父进程的管道。

        pipe
        int pipe(int pipefd[2]);
        功能:
            创建一个无名管道
        参数:
            pipefd[0]:读管道文件描述符
            pipefd[1]:写管道文件描述符
        返回值:
            成功返回0 
            失败返回-1 

      搭配:write(写管道,数组,数组大小);

                read(读管道,数组,数组大小);

        无名管道特性:
            1.管道中至少有一个写端: 
                读取数据时,如果管道中有数据直接读取,管道中没有数据阻塞等待直到有数据写入读出,继续向后执行
            2.管道中没有写端:
                读取数据时,如果管道中有数据直接读取,管道中没有数据不阻塞等待直接向下执行
            3.管道中至少有一个读端:
                写入数据时,如果管道中没有存满(64k),则直接写入,管道中如果存满,则阻塞等待直到有数据读出,才能继续写入
            4.管道中没有读端:
                写入数据时,会产生管道破裂错误,导致程序崩溃
       
    2.有名管道
        打开管道文件 -> 读写管道文件 -> 关闭管道文件

        注意:有名管道必须读写两端同时加入才能继续向下执行

        1.mkfifo 
          int mkfifo(const char *pathname, mode_t mode);
          功能:
            创建一个管道文件
          参数:
            pathname:管道文件路径
            mode:权限
          返回值:
            成功返回0 
            失败返回-1 

   

双管道通讯

信号


    信号用来实现内核层和用户层信息的交互,也可以用来实现进程间通信

    1.信号的种类:
        kill -l 

         1) SIGHUP     2) SIGINT     3) SIGQUIT     4) SIGILL     5) SIGTRAP
         6) SIGABRT     7) SIGBUS     8) SIGFPE     9) SIGKILL    10) SIGUSR1
        11) SIGSEGV    12) SIGUSR2    13) SIGPIPE    14) SIGALRM    15) SIGTERM
        16) SIGSTKFLT    17) SIGCHLD(有子进程结束,就会收到该信号)    18) SIGCONT    19) SIGSTOP    20) SIGTSTP
        21) SIGTTIN    22) SIGTTOU    23) SIGURG    24) SIGXCPU    25) SIGXFSZ
        26) SIGVTALRM    27) SIGPROF    28) SIGWINCH    29) SIGIO    30) SIGPWR
        31) SIGSYS    34) SIGRTMIN    35) SIGRTMIN+1    36) SIGRTMIN+2    37) SIGRTMIN+3
        38) SIGRTMIN+4    39) SIGRTMIN+5    40) SIGRTMIN+6    41) SIGRTMIN+7    42) SIGRTMIN+8
        43) SIGRTMIN+9    44) SIGRTMIN+10    45) SIGRTMIN+11    46) SIGRTMIN+12    47) SIGRTMIN+13
        48) SIGRTMIN+14    49) SIGRTMIN+15    50) SIGRTMAX-14    51) SIGRTMAX-13    52) SIGRTMAX-12
        53) SIGRTMAX-11    54) SIGRTMAX-10    55) SIGRTMAX-9    56) SIGRTMAX-8    57) SIGRTMAX-7
        58) SIGRTMAX-6    59) SIGRTMAX-5    60) SIGRTMAX-4    61) SIGRTMAX-3    62) SIGRTMAX-2
        63) SIGRTMAX-1    64) SIGRTMAX    

    2.信号处理方式:
        1.缺省:
            按照系统默认的方式处理
        2.忽略:
            不响应信号
        3.捕捉:
            按照自定义方式处理信号

        9号信号SIGKILL
        19号信号SIGSTOP 
        这两个信号不能被忽略和捕捉

        以下三个信号可以从键盘输入:
        SIGINT:ctrl + c 让进程结束
        SIGQUIT:ctrl + \  让进程退出
        SIGTSTP:ctrl + z        让进程挂起

    4.signal 
        typedef void (*sighandler_t)(int);

就等于 typedef void (*)(int) sighandler_t;因为c语言中函数必须和指针在一起写,所以写了过去

意思就是用sighandler_t代替前面那个函数指针。
        sighandler_t signal(int signum, sighandler_t handler);
        功能:
            改变信号的处理方式
        参数:
            signum:信号的编号
            handler:信号的处理方式
                SIG_IGN     忽略处理
                SIG_DFL     缺省处理
                函数首地址   捕捉处理
        返回值:
            成功返回之前处理函数的首地址
            失败返回SIG_ERR 

在handler函数中int signo代表的是信号的编号,所以就可以把这些西到一个函数里面。

收到信号,不去捕捉,信号大多就会杀死进程。

1.信号:
    1.pause
      int pause(void);
      功能:
        让进程睡眠,直到接收到信号(捕捉)才能继续向下执行

    2.alarm
      unsigned int alarm(unsigned int seconds);
      功能:
        定时seconds秒后给调用进程发送SIGALRM信号
      参数:
        seconds:定时的秒数
      返回值:
        成功返回之前设定剩余的秒数  

    3.kill
      int kill(pid_t pid, int sig);
      功能:
        给PID对应的进程发送sig信号
      参数:
        pid:进程ID号
        sig:信号的编号
      返回值:
        成功返回0 
        失败返回-1

消息队列


1.IPC对象:
    内存文件

    1.ipcs 
      查看系统重的消息队列、共享内存、信号灯的信息
    2.ipcrm
      删除消息队列、共享内存、信号灯
      ipcrm -Q/-M/-S key
      ipcrm -q/-m/-s 消息队列ID/共享内存ID/信号灯ID
      
    3.操作流程:
        创建消息队列 -> 发送消息 -> 接收消息

    4.函数接口:
        1.ftok
          key_t ftok(const char *pathname, int proj_id);
          功能:
            根据pathname和proj_id生成一个key_t类型的key值,将来可以用来创建消息队列、共享内存、信号灯
          参数:
            pathname:文件路径
            proj_id:8位非0值
          返回值:
            成功返回key_t类型的IPC对象的key值
            失败返回-1 

        2.msgget 
          int msgget(key_t key, int msgflg);
          功能:
            根据key值对象的IPC对象创建一个消息队列
          参数:
            key:IPC对象名字 
            msgflg:IPC_CREAT    对象不存在就创建
                   IPC_EXCL     对象存在报错
                   IPC_CREAT | 0664 
          返回值:
            成功返回消息队列ID
            失败返回-1 

        3.msgsnd 
          int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
          功能:
            向消息队列中发送消息
          参数:
            msqid:消息队列的ID号
            msgp:发送消息空间的首地址
                struct msgbuf {
                    long mtype;       /* message type, must be > 0 */
                    char mtext[1];    /* message data */
                };
            msgz:发送消息内容的大小(不包含发送消息类型)
            msgflg:属性,默认为0
          返回值:
            成功返回0 
            失败返回-1 

        4.msgrcv 
          ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
          功能:
            从消息队列中接收消息
          参数:
            msqid:消息队列的ID号 
            msgp:存放接收到消息空间的首地址
            msgsz:最多接收消息的空间的大小
            msgtyp:想要接收消息的类型,就是 long mtype;写哪个就可以接收那个消息有两个相同的,谁先发就先接谁
            msgflg:属性,默认为0 
          返回值:
            成功返回实际接收的字节数
            失败返回-1 

        5.msgctl 
          int msgctl(int msqid, int cmd, struct msqid_ds *buf);
          功能:
            向消息队列发送一条cmd命令
          参数:
            msqid:消息队列的ID号
            cmd:IPC_RMID    删除消息队列   还有其他消息队列命令,具体可以查手册。配合buf使用
            buf:默认传NULL
          返回值:
            成功返回0 
            失败返回-1 

共享内存


    进程间通信最高效的形式

    1.操作方式:
        创建共享内存 -> 映射到共享内存中 -> 共享内存操作 -> 解除映射 -> 删除共享内存 

    2.函数接口:
        1.ftok 
        
        2.shmget 
          int shmget(key_t key, size_t size, int shmflg);
          功能:
            创建一个共享内存,大小都是4k分配,所以尺寸大小都是4096的整数倍。
          参数:
            key:IPC对象名称
            size:共享内存的大小
            shmflg:
                IPC_CREAT 
                IPC_EXCL 
          返回值:  
            成功返回共享内存ID
            失败返回-1 
        
        3.shmat
          void *shmat(int shmid, const void *shmaddr, int shmflg);
          功能:
            将一个地址映射到共享内存中 内存不能直接访问,所以需要映射访问。
          参数:
            shmid:共享内存ID号
            shmaddr:NULL     让系统选择一个合适的地址映射
                    不为NULL shmflg 设定为SHM_RND 选择离给定地址最近的能够映射的地址进行映射
                             否则传递地址为4k的整数倍
          返回值:
            成功返回映射到共享内存空间中的地址
            失败返回NULL

        4.shmdt 
          int shmdt(const void *shmaddr);
          功能:
            解除映射 
          参数:
            shmaddr:映射的地址
          返回值:
            成功返回0 
            失败返回-1 

        5.shmctl
          int shmctl(int shmid, int cmd, struct shmid_ds *buf);
          功能:
            向共享内存发送命令
          参数:
            shmid:共享内存ID号
            cmd:IPC_RMID    删除共享内存,看手册产看其他命令
            buf:NULL 
          返回值:
            成功返回0 
            失败返回-1 


    利用消息队列实现clientA和clientB两个进程任务的全双工聊天功能

其实可以用一个队列就行了,因为他的数据类型可以设置不同,所以接收不会进行资源竞争。发送也是thread_safety,所以不会出现发送资源竞争。

信号量(有名信号量)

  

例子

head.h

#ifndef __HEAD_H__
#define __HEAD_H__

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <dirent.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>

union semun {
	int              val;    /* Value for SETVAL */
	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short  *array;  /* Array for GETALL, SETALL */
	struct seminfo  *__buf;  /* Buffer for IPC_INFO
								(Linux-specific) */
};

extern int init_sem(int semid, int *parray, int len);
extern int sem_p(int semid, int num);
extern int sem_v(int semid, int num);

#endif

sem.c

#include "head.h"

int init_sem(int semid, int *parray, int len)
{
	union semun myun;
	int i = 0;
	int ret = 0;

	for (i = 0; i < len; i++)
	{
		myun.val = parray[i];
		ret = semctl(semid, i, SETVAL, myun);
		if (-1 == ret)
		{
			perror("fail to semctl");
			return -1;
		}
	}

	return 0;
}

int sem_p(int semid, int num)
{
	int ret = 0;
	struct sembuf mybuf;

	mybuf.sem_num = num;
	mybuf.sem_op = -1;
	mybuf.sem_flg = SEM_UNDO;

	ret = semop(semid, &mybuf, 1);
	if (-1 == ret)
	{
		perror("fail to semop");
		return -1;
	}

	return 0;
}

int sem_v(int semid, int num)
{
	int ret = 0;
	struct sembuf mybuf;

	mybuf.sem_num = num;
	mybuf.sem_op = +1;
	mybuf.sem_flg = SEM_UNDO;

	ret = semop(semid, &mybuf, 1);
	if (-1 == ret)
	{
		perror("fail to semop");
		return -1;
	}

	return 0;
}

write.c

#include "head.h"

int main(void)
{
	key_t key;
	int shmid = 0;
	int semid = 0;
	char *pshmaddr = NULL;
	int val[2] = {0, 1};

	key = ftok(".", 'a');
	if (-1 == key)
	{
		perror("fail to ftok");
		return -1;
	}

	semid = semget(key, 2, IPC_CREAT | 0664);
	if (-1 == semid)
	{
		perror("fail to semget");
		return -1;
	}

	init_sem(semid, val, 2);

	shmid = shmget(key, 4096, IPC_CREAT | 0664);
	if (-1 == shmid)
	{
		perror("fail to shmget");
		return -1;
	}

	pshmaddr = shmat(shmid, NULL, 0);
	if (NULL == pshmaddr)
	{
		perror("fail to shmat");
		return -1;
	}

	while (1)
	{
		sem_p(semid, 1);
		gets(pshmaddr);
		sem_v(semid, 0);
		if (!strcmp(pshmaddr, ".quit"))
		{
			break;
		}
	}

	shmdt(pshmaddr);

	shmctl(shmid, IPC_RMID, NULL);

	return 0;
}

read.c

#include "head.h"

int main(void)
{
	key_t key;
	int shmid = 0;
	int semid = 0;
	char *pshmaddr = NULL;
	int val[2] = {0, 1};

	key = ftok(".", 'a');
	if (-1 == key)
	{
		perror("fail to ftok");
		return -1;
	}

	semid = semget(key, 2, IPC_CREAT | 0664);
	if (-1 == semid)
	{
		perror("fail to semget");
		return -1;
	}

	init_sem(semid, val, 2);

	shmid = shmget(key, 4096, IPC_CREAT | 0664);
	if (-1 == shmid)
	{
		perror("fail to shmget");
		return -1;
	}

	pshmaddr = shmat(shmid, NULL, 0);
	if (NULL == pshmaddr)
	{
		perror("fail to shmat");
		return -1;
	}

	while (1)
	{
		sem_p(semid, 0);
		printf("SHMADDR:%s\n", pshmaddr);
		if (!strcmp(pshmaddr, ".quit"))
		{
			break;
		}
		sem_v(semid, 1);
	}

	shmdt(pshmaddr);

	shmctl(shmid, IPC_RMID, NULL);

	return 0;
}

makefile

all:read write

read:read.c sem.c 
	gcc $^ -o $@
write:write.c sem.c 
	gcc $^ -o $@

 


        

  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值