进程间通信

目录

进程

程序与进程

程序包含内容:''

进程中包含内容

进程状态

linux下的进程管理

启动进程

调度进程

进程的系统调用

虚拟内存

进程退出与资源回收

资源回收函数

linux守护进程

linux守护进程编写步骤

exec函数族

写时拷贝技术

vfork()

线程

意义

线程资源问题

线程接口

pthread_create

pthread_join

pthread_detach

pthread_self

pthread_cancel

pthread_exit

线程同步与互斥

互斥锁接口

同步的接口

int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_post(sem_t *sem);

int sem_wait(sem_t sem)

进程间的通信

通信方式

传统通信方式

无名管道

有名管道

信号

信号相关接口

ipc通信

共享内存(快)

信号灯集(信号量)

消息队列


进程

程序与进程

程序:是一种静态的概念,表示有序指令的集合
进程:是一种动态的概念,表示程序运行的过程(运行过程包括:创建、调度、消亡)
​
    1、进程是一个独立可调度的任务:
        进程是一个抽象的实体。当系统在执行某个任务时,分配和释放的各种资源
    2、进程是一个程序的一次执行过程
    

程序包含内容:''

1、数据段(全局变量、静态变量、常量)
2、代码段(程序的指令)

objdump -d -s a.out > 1.txt(反汇编)二进制文件转化为汇编文件。

进程中包含内容

1、数据段
2、代码段
3、堆、栈
4、进程控制块(PCB)task_struct。维护进程的结构体

进程状态

运行态:R,R+(前台运行).正在运行
​
等待态:进程进入资源等待过程,(scanf(),getchar()),cpu会循环检测是否中断
        1、可中断(软件层面):s
        2、不可中断(软件层面,但是硬件可以):d,表示与硬件正在交互
​
停止态:t:表示停止,进程属于休眠状态(不占cpu),需要通过特定信号将其唤醒.
        可以使用信号(ctrl + z)使程序进入停止态,同样gdb断点调试时也处于停止态(追踪模式)
​
僵尸态:z,表示进程已经结束,但是资源没有被回收
​
空闲态:i,属于不可中断等待D的内核线程,但是该内核线程可能实际上没有任何负载,该线程就是空闲状态。
​
s< 高优先级
sN 低优先级
sl 进程后有线程

linux下的进程管理

启动进程

1、手动启动
    由用户输入命令直接启动进程
    前台运行和后台运行
2、调度启动
    系统根据用户事先设定自行启动进程
    at 在指定时刻执行相关进程
    cron 周期性执行相关进程

调度进程

ps 查看系统中的进程
top 动态显示系统中的进程
nice 按用户指定优先级运行进程
renice 改变整正在运行进程的优先级
kill 向进程发信号
bg 将挂起的进程在后台执行
fg 把后台运行进程放前台运行

进程的系统调用

fork()//进程创建函数
 pid_t fork(void);
​
返回值:
​
    1、成功:
            两个返回值(一个等于0,一个大于零)。
            
    
    2、失败:-1
​
    
    pid_t  pid = fork();
     
    if(-1 == p):错误发生,常见原因:资源不足开辟不了进程
    if(0 == p):表示子进程
    if(0 < p):表示父进程,p是子进程的进程号。
fork()一旦被调用,则子进程会拷贝父进程的资源(拷贝虚拟内存内容 :)。  
​
​

虚拟内存

每一个进程都有自己的虚拟内存空间(32位系统32g)
虚拟内存与物理内存有影射关系,但虚拟的大小和物理内存大小没有关系,而是与操作系统以及cpu有关系

进程退出与资源回收

进程退出函数:exit(),_exit()
 
exit();标准库(刷新缓存区)
 void exit(int status);

_exit();系统调用(不会刷新缓存区)
 void _exit(int status);

status:零(EXIT_SUCCESS,进程正常结束)和非零(EXIT_FAILURE进程异常结束)

    

资源回收函数

getpid()//获取当前进程id号
getppid()//获取父进程id


子进程资源需要被父进程回收
wait()

 pid_t wait(int *wstatus);//父进程阻塞,等待子进程退出函数,并回收资源。不关心转态传参NULL。
		调用该函数使进程阻塞,直到任意一个子进程结束或者该进程接收到一个信号为止。如果该进程没有子进程或者其子进程已经结束,该函数会立即返回
成功:
	返回子进程id
错误:-1

pid_t waitpid(pid_t pid, int *wstatus, int options);
	功能和wait类似,可以指定等待某个子进程结束以及等待方式。
pid:
    pid > 0 只等待进程ID等于pid的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,waitpid就会一直等下去
						
    pid == -1 等待任何一个子进程退出,没有任何限制,此时waitpid和wait的作用一模一样
			  
    pid == 0  等待同一个进程组中的任何子进程,如果子进程已经加入了别的进程组,waitpid不会对它做任何理睬
						
				ps axj 可以查看组进程.
						
  pid < -1 等待一个指定进程组中的任何子进程,这个进程组的ID等于pid的绝对值

status:  与wait一致
		
options:  为 0 与wait函数一样,需要一直等待进程退出(阻塞).为WNOHANG  不需要等待(不阻塞).		
		
     返回值: 
            正常返回子进程的进程号
            使用选项WNOHANG且没有子进程结束时:0
		
            错误 -1



linux守护进程

1、守护进程,也就是通常所说的Daemon进程,是linux中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性的执行某种任务或等待处理某些发生的时间。不需要与用户进行交互。
2、守护进程常常在系统启动时开始运行,在系统关闭时终止。
3、在linux中,每一个系统与用户进行交流的界面叫终端。从该终端开始运行的进程都会依附于这个终端,这个终端称为这些进程的控制终端。当控制终端被关闭时,相应的进程都会被自动关闭。

4、守护进程可以突破这个限制,它从开始运行,直到整个系统关闭才会退出。如果想让某个进程不会因为用户或终端的变化而受影响,就必须把这个进程变成一个守护进程。

查看系统中的守护进程:
	ps -axj

linux守护进程编写步骤

1、创建子进程,父进程退出:形式上脱离终端,但还处在一个会话期
2、在子进程中创建新会话:
	setsid()
	进程组是一个或多个进程集合。进程组由进程组ID来唯一标识。每个进程组都有一个组长进程,进程组ID就是组长进程的进程号
	会话期:
		会话组是一个或多个进程组集合
		
3、改变当前目录为根目录
	chdir(”/“)
4、重设文件权限掩码
	umask(0)
5、关闭文件描述符



exec函数族

提供了在进程中启动另一个程序执行的方法。新的可执行程序把子进程原来的数据段、代码段和堆栈段,全部替换,处理进程号。
execl("/bin/ls","ls","-l",".",NULL);
execlp(“ls”,"-l",".","NULL")(需要改环境变量PATH)


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[]) 

l: 代表后面的参数的传入是 list 形式(依次传入).
v: 代表后面的参数的传入是以指针数组传入的. 
例如: char *arv[] = {"ls", "-l", NULL}; 
p: 第一个参数不需要指明路径,只需要传入可执行文件名就可以了. (在系统默认PATH路径下寻 找) 
e: 最后一个参数可以传入环境变量. 除了第一个参数以外后面参数所以形式都需要以NULL结尾!!

例如:
char *arg[] = {"ls", "-l", NULL} 
execvp("ls", arg);


int execve(const char *path, char *const argv[], char *const envp[]) 
    
    可以将环境变量以命令行参数形式传递给目标程序,接受后需要(调函数)将字符串转化为环境变量。
 

写时拷贝技术

如果子进程完整拷贝父进程资源,但是并不会修改拷贝后的数据,只会使用,那就没必要拷贝空间,直接共享。
当子进程或父进程需要修改数据时,才实现拷贝。

vfork()

1、也是创建子进程
2、内存资源共享
3、子进程结束后才运行父进程。

线程

意义

与进程一样,为了让多个程序能够并发执行。
定义:进程是资源分配的最小单位,线程是程序执行的最小单位(轻量级进程)。

线程资源问题

一个进程里的所有线程共享一片虚拟内存。
数据可共享:
	1、可执行的指令
	2、静态数据
	3、进程中打开的文件描述符
	4、信号处理函数
	5、当前工作目录
	6、用户ID
	7、用户组ID

私有的内容:
	1、线程号(id)
	2、程序计数器(pc):记录当前线程运行到什么位置
	3、堆、栈

线程接口

这些函数的实现属于第三方库函数,所以编译时需要链接库:-lpthread

pthread_create()线程创建函数
pthread_jion() 等待线程退出函数(并且回收资源) 阻塞函数,等待指定函数退出
pthread_detach()线程分离函数(使线程不受其他线程影响)
pthread_self()获取线程id
pthread_cancel()结束指定线程
pthread_exits()线程退出(相当于普通函数的return)

线程可以分为两种属性:结合属性、分离属性
	默认线程是选取结合属性:线程之间可能会相互造成影响
	
	
	分离属性:线程独立执行,相互之间不会造成影响,并且线程可以自动回收资源
		1、pthread——create()第二个参数:
		pthread_attr_t*    attr = NULL;
		pthread_attr_init(attr);
		
		2、pthread_datach()函数也可以设置分离属性
		
		
		
		
		

pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
thread:线程id,需要传递变量地址
attr:设置线程属性,如果选择默认的传 NULL
start——routine:是一个指针函数的指针
arg:传递给线程函数(start——routine)的参数,不需要传参NULL

返回值:
	成功:0
	错误:非0

pthread_join

等待线程退出函数(并且回收资源)
int pthread_join(pthread_t thread, void **retval); 
{ 
	thread: 线程ID 
	retval: 接收线程退出状态的参数. 不关心状态就为NULL 
}

pthread_detach

线程分离函数 (使线程不受其他线程影响)
线程可以分为: 结合属性 与 分离属性  
默认线程是结合属性 
	结合属性: 线程可能会相互之间造成影响(有一定联系) 

分离属性: 使线程完全独立执行,相互之间不会造成影响,并且线程可以自动回收资源了 int pthread_detach(pthread_t thread);

pthread_self

获取线程ID
pthread_t pthread_self(void); 
{ 
	返回线程id
}

pthread_cancel

 结束指定线程
 int pthread_cancel(pthread_t thread); 
 {
 	结束指定线程. 但是不能指定分离属性的线程 
 }
 

pthread_exit

 线程退出函数 (相当于普通函数return)
void pthread_exit(void *retval); 
{ 
	reatval: 设置线程退出状态.
}

线程同步与互斥

同步:
	使得线程之间按照某一种顺序来执行。实现方式:信号量,条件变量。


互斥:
	对临界资源的一种保护措施,当线程访问该资源时其他线程不能访问。实现方式:互斥锁。
	注意:互斥锁不能直接用于锁程序代码,只能锁临界资源


其实信号量与条件变量也能实现互斥

互斥锁接口

1、初始化锁
pthrepthread_mutex_init(pthread_mutex_t * mutex,NULL)

初始化锁:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutex- attr_t *mutexattr);
{ 
		mutex: 锁变量的地址 
		mutexattr: 为NULL使用默认属性 
}

加锁: pthread_mutex_lock();

解锁:pthread_mutex_unlock();

同步的接口

信号量

信号量:信号量代表一类资源,其值表示系统中该资源的数量。
	类似于生产者与消费者的关系,信号量的值可以表示产品的数量,如果为0代表没有产品可以消费(消费者程序不能运行)。

消费者:
if(信号量为0)
{
	消费者程序阻塞
}else if{
	消费者程序运行
	信号量 -1;
}

生产者
{
	生产者程序运行
	信号量加一
}

int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_init(sem_t *sem, int pshared, unsigned int value);
 
初始化信号量:
    sem :信号量类型地址
    pshared:选择信号量是在进程中还是线程中:线程:0. 进程:非零
    value:设置信号量中value的初始值
    
   返回值:
    正确: 0
    错误: -1
    

int sem_post(sem_t *sem);

int sem_post(sem_t *sem);
释放资源:(生产产品),v操作,信号量加一
返回值:
正确:0
错误: -1

int sem_wait(sem_t sem)

int sem_wait(sem_t *sem);
申请资源(消费产品),p操作 信号量减一
返回值:
正确 :0
错误:-1

进程间的通信

通信方式

	1、管道:无名管道、有名管道、信号
	2、IPC通信:共享内存、消息队列、信号灯集
	3、BSD通信:套接字

传统通信方式

无名管道

1、是“半双工”工作方式:同一时间,只能由一端写,另一端读取
2、无名管道不属于文件系统,数据交互在内核内存完成。(访问内核,比较费时间)
3、无名管道只能用于亲缘进程间通信。

函数接口:pipe
{
	int pipe(int pipefd【2】)

}


写端关闭,读端会出现什么情况:
{
	read()在读取管道时,如果管道关闭,且没有内容可读则返回0.
	当写端关闭,如果管道中还有内容继续读取,直到读完返回0.
}

读端关闭:
{
	读端关闭,内核发送管道会破裂信号,进程。写端存在没有意义。
}

有名管道

1、是半双工工作方式
2、又名管道属于文件系统,数据交互在“内核内存”完成
3、有名管道可以用于非亲进程。

mkfifo()是shell指令也是函数:创建管道。


写端关闭,读端会出现什么情况:
{
	read()在读取管道时,如果管道关闭,且没有内容可读则返回0.
	当写端关闭,如果管道中还有内容继续读取,直到读完返回0.
}

读端关闭:
{
	读端关闭,内核发送管道会破裂信号,进程。写端存在没有意义。
}

信号

概念:在软件层面对中断的模拟方式(软中断),内核进程与应用进程也可以使用信号进行通信。
查看信号种类:kill -l。应用进程能使用的是前31个信号。


进程处理信号的方式:
		默认:几乎都是终止程序
		忽略处理:(不能忽略的信号:SIGKILL SIGSTOP)
		自定义处理:使用函数处理信号
		
		SIGUSR1 SIGUSR2 默认无特殊意义

信号相关接口

1、可发送信号接口
	kill()//
	raise()//发自己
	arlarm()//发自己


2、可接受信号函数接口
signal() 
{ 
    typedef void (*sighandler_t)(int); 
    sighandler_t signal(int signum, sighandler_t handler); 
 { 
    	 signum: 信号值
         handler: 处理函数. ;SIG_IGN: 忽略 ;SIG_DFL: 默认处理 
         错误返回: SIG_ERR 
 }
}


pause()
{ 
    int pause(void); 
    	阻塞进程,等待任意信号来临. 
        返回值: 
        错误 -1 
}

ipc通信

共享内存(快)

概念:内核会寻找一片内存空间,然后将空间进行映射操作,能够把内存空间映射3到用户空间。提升了通信效率。
操作流程:
	1、创建共享内存空间   shmget()
		

	
	2、映射内核空间到用户空间
	3、读或写该共享内存
	4、取消映射
	5、删除共享内存
	
	相关指令:
		ipcs -m 查看共享内存
		ipcrm -m id:删除共享内存

	

key_t ftok(const char *pathname, int proj_id); 
{ 
获取key值:标识共享内存,以及用于区分共享内存用于亲缘 还是 可以用于非亲缘. 
     key_t ftok(const char *pathname, int proj_id) 
     pathname: 文件名 
     proj_id: 组合成key 需要用到的id 
     返回值:错误返回-1 
           正确返回 获取到的key值. 
           key 值是通过两个参数(文件的节点号 与 proj_id的值)组合形成的
}


int shmget(key_t key, size_t size, int shmflg); 
{ 
创建共享内存. 
    key: IPC_PRIVATE 或 ftok的返回值 IPC_PRIVATE只能用于亲缘进程
    size: 共享内存区大小 
    shmflg: 相当于open函数的权限位,也可以用8进制表示法 
    shmflg如包含IPC_CREAT,表明如果指定的共享内存不存在,则新建一个对象 
    返回值: 
    	正确返回 共享内存段标识符(共享内存id) 
    	错误 -1 
}


void *shmat(int shmid, const void *shmaddr, int shmflg); 
{ 
    共享内存映射. 
        shmid:要映射的共享内存区标识符 
        shmaddr: 将共享内存映射到指定地址(若为NULL,则表示由系统自动完成映射) 
        shmflg: SHM_RDONLY:共享内存只读 默认0:共享内存可读 
            成功:映射后的地址 
            错误 -1 }

    
 int shmdt(const void *shmaddr); 
{ 
    取消映射 
        shmaddr: 共享内存映射后的地址 
            成功 0 
            错误 -1 }


int shmctl(int shmid, int cmd, struct shmid_ds *buf); 
{ 
    共享内存控制函数. 
        shmid:要操作的共享内存标识符 
        cmd : IPC_STAT (获取对象属性) IPC_SET (设置对象属性) 
        IPC_RMID (删除对象)
}

信号灯集(信号量)

信号灯
{ 
    查看系统信号灯指令: ipcs -s 
    删除 ipcrm -s + semid 
    System V的信号灯是一个或者多个信号灯的一个集合。 其中的每一个都是单独的计数信号灯。
    而Posix信号灯指的是单个计数信号灯(线程用到的信号量) 
  

int semget(key_t key, int nsems, int semflg) 
    { 
     创建信号灯集函数
        key: 和信号灯集关联的key值 
        nsems: 信号灯集中包含的信号灯数目 
        semflg: 信号灯集的访问权限,通常为IPC_CREAT | 0666 
            返回值: 
            正确:信号灯集ID 
            错误: -1 
    }
    
  
  int semctl (int semid, int semnum, int cmd…/*union semun arg*/)
    {
    信号灯集控制函数
        semid: 信号灯集ID 
        semnum: 要修改的信号灯编号 
        cmd: 
        	GETVAL:获取信号灯的值 
            SETVAL:设置信号灯的值
            IPC_RMID:从系统中删除信号灯集合
            
        union semun: 
        semctl是可变参数函数:
            cmd为GETVAL或SETVAL时, 需要传递第四个参数。参数类型为union semun(联合体) 
            返回值: 
            正确 0 
            错误 -1 
    }
    
   
 int semop(int semid, struct sembuf *opsptr, size_t nops) 
    { 
        信号灯集资源控制函数(p v操作) 
            semid:信号灯集ID 
                opsptr: struct sembuf 
                {
                 short sem_num; // 要操作的信号灯的编号 
                 short sem_op; 
         		  		0 : 等待,直到信号灯的值变成0 
                    	 1 : 释放资源,V操作  
                         -1 : 分配资源,P操作
                         short sem_flg; // 0, IPC_NOWAIT, SEM_UNDO 
                }; 
                 nops: 要操作的信号灯的个数 返回值: 正确 0 错误 -1 
                                                           
                                                             }

消息队列

相关接口函数:
      1、打开或者创建消息队列
            
int msgget(key_t key, int flag);
            头文件:
                #include <sys/types.h>
                #include <sys/ipc.h>
                #include <sys/msg.h>
            参数:
                key:IPC_PRIVATE 或者是  ftok函数返回值
                flag:消息队列访问权限(IPC_CREAT|0644)
            返回值:
                成功:消息队列ID号
                失败:-1
		
	
			
2、发送消息(添加消息)
           
 int msgsnd(int msgid, const void *msgbuf, size_t size, int flag);
            参数:
                msgid:消息队列ID号
                msgbuf:消息的缓冲区首地址,消息类型必须如下所示:
                    struct msgbuf{
                        long type;        //消息类型
                        char buf[32];    //消息正文
                    };
                size: 消息正文的长度
                flag:
                    0:阻塞等待发送消息完成
                    IPC_NOWAIT:非阻塞
            返回值:
                成功:0
                失败:-1

3、接收消息(读取消息)
      

      int msgrcvd(int msgid, void *msgbuf, size_t size, long msgtype, int flag);

参数:
                msgid:        消息队列ID号
                msgbuf:    存储消息的缓冲区
                size:         消息正文的长度
                msgtype:    要读取的消息的类型
                flag:
                    0:若无数据,一直阻塞下去
                    IPC_NOWAIT:非阻塞
            返回值:
                成功:实际读取的字节数
                失败:-1

4、消息队列的控制
  

          int msgctl(int msqid, int cmd, struct msqid_ds *buf);


            参数:
                msqid:消息队列的 ID号
                cmd:    
                    IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中。
                    IPC_SET:设置消息队列的属性。这个值取自buf参数。
                    IPC_RMID:从系统中删除消息队列。
                buf:消息队列缓冲区
            返回值:
                成功:0
                失败:-1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值