进程基础(C语言版)

进程

概念

程序:

        编译好的可执行文件

        存放在磁盘上的指令和数据的有序集合

        程序时静态的,没有任何执行的概念

进程:

         一个独立的可调度的任务

        执行一个程序所分配的资源的总称

        进程是程序的一次执行过程

        进程是动态的,包括创建、调度、执行和消亡

特点

系统会为每个进程分配0~4G的虚拟空间,用户空间是每个进程所独有的,内核空间是所有进程共有的

处理器调度进程会给进程分配时间片,当时间片用完以后,CPU再调度其他进程,实现进程的轮转,从而实现多任务的操作

进程段:数据段、正文段、堆栈段

进程分类:交互进程,批处理进程,守护进程

进程状态:运行态、睡眠态、可中断睡眠态、不可中断睡眠态、暂停态、死亡态、僵尸态

函数接口

#include <unistd.h>
pid_t fork(void);
//功能:创建子进程
/*
	参数:
		无
	返回值:
		成功:
			在父进程中,返回子进程的进程号>0
			在子进程中,返回值为0
        失败:-1并设置errno
*/

特点

子进程几乎拷贝了父进程的全部内容,主要包括代码、数据、系统数据段中的PC值、栈中的数据、父进程中国打开的文件等,但是,他们的PID、PPID是不同的

父子进程有独立的地址空间,互不影响,当在响相应进程中改变全局变量,静态变量,都不受影响

若父进程先结束,子进程成为孤儿进程,被init进程收养,子进程变成后台进程

若子进程先结束,父进程没有及时回收,子进程变成僵尸进程(要尽可能避免僵尸进程的产生)

回收进程资源

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
//功能:回收子进程资源(阻塞)
/*
	参数:
		status:子进程退出状态,不接受子进程状态设为NULL
	返回值:
		成功:子进程的ID号
		失败:-1
*/

pid_t waitpid(pid_t pid,int *status,int options);
//功能:回收子进程资源
/*
	参数:
		pid:
			>0指定子进程的id号
			=-1任意子进程
			=0等待其组ID等于调用进程的组ID的任一子进程
			<-1等待其组ID等于pid的绝对值的任一子进程
		status:子进程的退出状态
		option:
			0:阻塞
			WNOHANG:非阻塞
	返回值:
		成功:
			结束的子进程的进程号
			当使用WNOHANG且没有子进程结束时:0
		失败:-1
*/

结束进程

void exit(int status);
//功能:结束进程时,刷新缓存
void _exit(int status);
//功能:结束进程时,不刷新缓存
/*
	参数:
		status:是一个整型的参数,可以利用这个参数传递进程结束时的状态
			通常0表示正常结束
			其他数值表示出现了错误,进程非正常结束
*/

在实际编程时,子进程中调用exit函数,会将参数值传递给父进程 ,父进程可以进行相应的处理

exit和return的区别

exit:不管在子函数还是主函数,都可以结束进程

return:当子函数中有return时返回到函数调用位置,并不结束进程

exit(EXIT_FAILURE);//失败
exit(EXIT_SUCCESS);//成功

进程间通信

1.早期的进程间通信 无名管道、有名管道、信号

2.system V IPC 共享内存、消息队列、信号灯集

3.BSD 套接字

无名管道

特点

只能用于具有亲缘关系的进程间通信

半双工的通信方式、具有固定的读端和写端

管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO

管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1],其中fd[0]固定用于读管道,fd[1]固定于写管道 ‘’

函数接口

#include <unistd>
int pipe(int fd[2]);
//功能:创建无名管道
/*
	参数:
		文件描述符  读端:fd[0]  写端:fd[1]
	返回值:
		成功:0
		失败:-1
*/

注意事项

a.当管道中无数据时,读操作会阻塞;管道中无数据,将写端关闭,读操作会立即返回

b.管道中装满(管道大小64K)读数据阻塞,一旦有4K空间,写继续

c.只有在管道读端存在时,向管道中写数据才有意义,否则将会导致管道破裂,向管道中写入数据的进程将收到内核传来的SIGPIPE信号,通常Broken Pipe错误

有名管道

特点

a.有名管道可以使互不相关的两个进程通信

b.有名管道可以通过路径名来指出,并且在文件系统中可见,但内容存放在内存中

c.进程通过IO来操作有名管道

d.有名管道遵循先进先出的规则

e.不支持如lseek()操作

函数接口

int mkfifo(const char * filename,mode_t mode);
//功能:创建有名管道
/*
	参数:
		filename:有名管道文件名
		mode:权限
	返回值:
		成功:0
		失败:-1并设置errno
*/
//注意:
//对错误的处理方式:如果错误时file_exist时,注意加判断,如:if(errno==EEXIT)

注:函数只是在路径下创建管道文件,往管道中写的数据依然写在内核空间

注意事项

a.只写方式,写阻塞,一直到另一个进程把读打开

b.只读方式,读阻塞,一直到另一个进程把写打开

c.可读可写,如果管道中没有数据,读阻塞

有名管道和无名管道的区别

使用场景无名管道有名管道
具有亲缘关系的进程间通信不相关的两个进程间可以使用
特点无名管道有名管道
半双工的通信方式在文件系统中会存在管道文件
固定读端fd[0]和写端fd[1]通过文件IO进行操作
可以看做一种特殊的文件,通过文件IO操作不支持lseek操作,遵循先进先出
数据存放在内核空间

信号

概念

1.信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式

2.信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用户空间进程发生了哪些系统事件

3.如果该进程当前并未处于执行状态,该信号就由内核保存起来,直到该进程恢复执行,再传递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才被传递给进程

信号的响应方式:忽略信号,捕捉信号,执行缺省操作

信号种类

信号中文文字说明
SIGKILL结束进程不能被忽略,不能被捕捉
SIGSTOP结束进程不能被忽略,不能被捕捉
SIGCHLD传递进程子进程状态改变时给父进程发的信号
SIGINT结束进程ctrl+c
SIGTSTP暂停信号ctrl+z
SIGQUIT退出信号ctrl+\
SIGALRM闹钟信号alarm函数设置定时,当到设定的时间时,内核会向进程发送此信号结束进程
SIGTERM结束终端kill使用时不加数字默认是此信号

函数接口

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid,int sig);
//功能:信号发送
/*
	参数:
		pid:指定进程
		sig:要发送的信号
	返回值:
		成功:0
		失败:-1
*/

int raise(int sig);
//功能:进程向自己发送信号
/*
	参数:
		sig:信号
	返回值:
		成功:0
		失败:-1
*/

int pause(void);
//功能:用于将调用进程挂起,直到收到信号为止

unsigned int alarm(unsigned int seconds);
//功能:在进程中设置一个定时器
/*
	返回值:
		如果调用此alarm()前,进程中已经设置了闹钟时间,则返回上一个闹钟时间的剩余时间,否则返回0
*/
//注意:一个进程只能有一个闹钟时间,如果在调用alarm时,已设置过闹钟时间,则之前的闹钟时间会被新值代替

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
//修改形式
sighandler_t signal(int signum,void (*handler)(int));
//功能:信号处理函数
/*
	参数:
		signum:要处理的信号
		handler:信号处理方式
			SIG_IGN:忽略信号
			SIG_DFL:执行默认操作
			handler:捕捉信号
				void handler(int sig){}//函数名可以自定义
	返回值:
		成功:设置之前的信号处理方式
		失败:-1
*/

共享内存

特点

1.共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据拷贝

2.为了在多个进程之间交换信息,内核专门留出了一块内存区域,可以有需要访问的进程将其映射到自己的私有地址空间即可

3.进程就可以直接读写这一内存区域而不需要进行数据的拷贝,从而大大提高效率 由多个进程共享这一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

步骤

a.创建key值

b.创建或打开共享内存

c.映射共享内存到用户空间

d.撤销映射

e.删除共享内存

函数接口

#include <sys/types.h>
#include <sys/ipc.h>

key_t ftok(const char *pathname,int proj_id);
//功能:产生一个独一无二的key值
/*
	参数:
		pathname:已经存在的可访问文件的名字
		proj_id:一个字符(因为只用低8位)
	返回值:
		成功:key值
		失败:-1
*/

int shmget(key_t key,size_t size,int shmflg);
//功能:创建或者打开共享内存
/*
	参数:
		key:键值
		size:共享内存的大小
		shmflg:IPC_CREAT|IPC_EXCL|0777
	返回值:
		成功:shmid
		失败:-1
*/

int *shmat(int shmid,const void *shmaddr,int shmflg);
//功能:映射共享内存.即把指定的共享内存映射到进程的地址空间用于访问
/*
	参数:
		shmid:共享内存的id号
		shmaddr:一般为NULL,表示由系统自动完成映射
		shmflg:SHM_RDONLY就是对该共享内存只进行读操作   0:可读可写
	返回值:
		成功:完成映射后的地址
		失败:-1的地址
	使用方法:
	if((p=(char*)shmat(shmid,NULL,0))==(char *)-1)
*/

int shmdt(const void *shmaddr);
//功能:取消映射
/*
	参数:
		shmaddr:要取消的地址
	返回值:
		成功:0
		失败:-1
*/

int shmctl(int shmid,int cmd,struct shmid_ds *buf);
//功能:(删除共享内存),对共享内存进行各种操作
/*
	参数:
		shmid:共享内存id号
		cmd: 
			IPC_STAT:获得shmid属性信息,存放在第三个参数
			IPC_SET:设置shmid属性信息,要设置的属性存放在第三参数
			IPC_RMID:删除共享内存,此时第三个参数为NULL即可
	返回值:
		成功:0
		失败:-1
*/

关于共享内存的一些命令

ipcs -m:查看系统中的共享内存

ipcrm -m shmid:删除共享内存

信号灯集

信号灯:也叫信号量。它是不同进程之间或一个给定进程内部不同线程间同步的机制;System V信号灯是一个或者多个信号灯的集合。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯

通过信号灯集实现共享内存的同步操作

步骤

1.创建key值

2.创建或打开信号灯集

3.初始化信号灯集

4.PV操作

5.删除信号灯集

关于信号灯集的一些命令

ipcs -s:查看信号灯集

ipcrm -s semid :删除信号灯集

函数接口

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key,int nsems,int semflg);
//功能:创建/打开信号灯
/*
	参数:
		key:ftok产生的key值
		nsems:信号灯集中包含的信号灯数目
		semflg:信号灯集的访问权限,通常为IPC_CREAT|0666
	返回值:
		成功:信号灯集ID
		失败:-1
*/

int semop(int semid,struct sembuf *opsptr,size_t nops);
//功能:对信号灯集合中的信号量进行PV操作
/*
	参数:
		semid:信号灯集ID
		opsptr:操作方式
		nops:要操作的信号灯个数
	返回值:
		成功:0
		失败:-1
*/

struct sembuf{
    short sem_num;	//要操作的信号灯编号
    short sem_op;	//0:等待,直到信号灯的值变成0
    	  			//1:释放资源,v操作
    				//-1:分配资源,p操作
    short sem_flg;	//0(阻塞),IPC_NOWAIT,SEM_UNDO
};
/*
	使用方法:
		申请资源P操作:
			mysembuf.sem_num=0;
			mysembuf.sem_op=-1;
			mysembuf.sem_flg=0;
			semop(semid,&mysembuf,1);
			
		释放资源V操作:
			mysembu.sem_num=0;
			mysembuf.sem_op=1;
			mysembuf.sem_flg=0;
			semop(semid,&mysembuf,1);
*/

int semctl(int semid,int semnum,int cmd/*union semun arg*/);
//功能:信号灯集合的控制(初始化/删除)
/*
	参数:
		semid:信号灯id
		semnum:要操作的集合中的信号灯编号
		cmd:
			GETVAL:获取信号灯的值,返回的是获得的值
			SETVAL:设置信号灯的值,需要用到第四个参数共用体
	返回值:
		成功:0
		失败:-1
*/
/*
	用法:初始化
	union semun{
		int val;
	}mysemun;
	mysemun.val=10;
	semctl(semid,0,SETVAL,mysemun);
	获取信号灯值,函数semctl(semid,0,GETVAL)的返回值
	删除信号灯集:semctl(semid,0,IPC_RMID);
*/

消息队列

特点

消息队列是IPC对象的一种

消息队列由消息队列ID来唯一标识

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等

消息队列可以按照类型来发送/接收消息

步骤

1.产生key值 2.创建或打开消息队列 3.添加消息:按照类型吧消息添加到已经打开的消息队列末尾 4.读取消息:可以按照类型把消息从消息队列中取走 5.删除消息队列

关于消息队列的一些命令

ipcs -q:查看消息队列

ipcrm -q msgid:删除消息队列

函数接口

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key,int flag);
//功能:创建或打开一个消息队列
/*
	参数:
		key:key值
		flag:创建消息队列的权限 IPC_CREAT|IPC_EXEC|0666
	返回值:
		成功:msgid
		失败:-1
*/

int msgsnd(int msgid,const void *msgp,size_t size,int flag);
//功能:添加消息
/*
	参数:
		msqid:消息队列的id
		msgp:指向消息的指针。常用消息结构msgbuf如下:
			struct mgbuf{
				long mtype;		//消息类型
				char mtext[N]	//消息正文
			};
		size:发送的消息正文的字节数
		flag:
			IPC_NOWAIT:消息没有发送完成函数也会立刻返回
			0:直到消息发送完成函数才返回
	返回值:
		成功:0
		失败:-1
*/
/*
	使用:msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);
	注意:消息结构除了第一个成员函数必须为long类型以外,其他成员函数可以根据应用的需求自行定义
*/

int msgrcv(int msgid,void *msgp,size_t size,long msgtype,int flag);
//功能:读取消息
/*
	参数:
		msgid:消息队列的ID
		msgp:存放读取消息的空间
		size:接受的消息正文的字节数
		msgtype:
			0:接收消息队列中第一个消息
			>0:接收消息队列中第一个类型为msgtyp的消息
			<0:接收消息队列中类型值不小于msgtyp的绝对值且类型值又最小的消息
		flag:
			0若无消息函数会一直阻塞
			IPC_NOWAIT:若没有消息,进程会立刻返回ENOMSG
		返回值:
			成功:接收到的消息长度
			失败:-1
*/

int msgctl(int msgqid,int cmd,struct msqid_ds *buf);
//功能:对消息队列的操作,删除消息队列
/*
	参数:
		msgqid:消息队列的队列ID
		cmd:
			IPC_STAT:读取消息队列的属性,并将其保存在buf指向的缓冲区中
			IPC_SET:设置消息队列的属性,这个值取自buf参数
			IPC_RMID:从系统中删除消息队列
		buf:消息队列缓冲区
	返回值:
		成功:0
		失败:-1
*/
//使用方法:msgctl(msgid,IPC_RMID,NULL);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值