进程介绍
Process
- 计算机中的程序关于某数据集合上的一次运行活动。
- 它是操作系统进行资源分配和调度的基本单位。
进程的内容
- 逻辑内存
- 文件/网络句柄
- 所有进程共有的。
- 线程
进程的特征
- 动态性
进程是程序的执行,同时进程有生命周期。 - 并发性
多个进程可同存在与内存中,能在一段时间内同时执行。 - 独立性
资源分配和调度的基本单位。 - 制约性
- 并发进程间存在制约关系,造成程序速度不可预期性,必须对进程的并发执行次序、相对执行速度加以协调。
进程的状态
- 运行态
当进程得到处理机,并且进程的执行程序在处理机上运行时的状态成为运行状态。 - 就绪态
当一个进程已经准备就绪,一旦得到CPU,就可立即运行,这时进程所处的状态成为就绪状态。
系统中有一个就绪进程队列,处于就绪状态的进程按照某种调度策略存在于该队列中。 - 等待态(阻塞态)
若一个进程正等待某一件事发生(比如等待输入或输出操作的完成什么的)而暂时停止执行的状态称为等待状态。
处于等待状态的进程,不具备运行的条件,即使给它CPU,也无法执行。
系统中有几个等待进程队列,这个队列时按照所等待的事件组成的相应的等待队列。
进程间通信的方式
管道-无名管道
- 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
- 它只能用于具有亲缘关系的进程之间的通信(即父子进程之间、兄弟进程之间)。
- 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read,write等函数。但它不是普通的文件,并不属于其他任何文件系统,并且只用于内存中。
原型:
12#include <unistd.h>int pipe(int fd[2]);//成功返回0,失败返回-1示例:
数据流从父进程流向子进程,关闭父进程读端(fd[0])与子进程写端(fd[1]);反之亦然。123456789101112131415161718192021222324#include <stdio.h>#include <unistd.h>int main(){int fd[2];//文件描述符pid_t pid;char buff[20];if(pipe(fd) < 0)//创建管道printf("create pipe failed");if((pid == fork()) < 0)//创建子进程printf("fork failed");else if(pid > 0)//父进程{close(fd[0]);//关闭读端write(fd[1],"hello world\n",12);}else{close(fd[1]);read(fd[0],buff,20);printf("%s",buff);}return 0;}
管道-有名管道
- FIFO,是一种文件类型
- FIFO可以在无关的进程之间交换数据,区别与无名管道。
- FIFO有路径名与之相关联,它以一种特殊设备文件形式存在与文件系统中。
原型:12#include <sys/stat.h>int mkfifo(const char * pathname,mode_t mode);//成功返回0,出错返回-1 - 一旦创建了一个FIFO,就可以用一般的I/O函数操作它
- 当open一个FIFO时,如果设置了非阻塞标志(0_NONBLOCK),则只读open立即返回。而只写open将出错返回-1,如果没有进程为了读而打开该FIFO,其errno置ENXIO。
- 如果没有设置非阻塞标志,只读open要阻塞到某个其他进程为写而打开此FIFO。类似,只写open要阻塞到某个其他进程为读而打开它。
示例:
write_fifo.c123456789101112131415161718192021222324252627282930313233#include <stdio.h>#include <stdlib.h>//exit#include <fcntl.h>//O_WRONLY#include <sys/stat.h>#include <time.h>int main(){int fd;int n,i;char buf[1024];time_t tp;printf("process %d",getpid());//进程IDif(fd == open("fifo1",O_WRONLY) < 0)//以写打开一个FIFO{perror("open fifo error");exit(1);}for(i = 0;i < 10;++i){time(&tp);//取系统当前时间n = sprintf(buf,"process %d is %s",getpid(),ctime(&tp));printf("send message %s",buf);if(write(fd,buf,n+1) < 0){perror("write fifo error");close(fd);exit(1);}sleep(1);}close(fd);return 0;}read_fifo.c
12345678910111213141516171819202122#include <stdio.h>#include <stdlib.h>#include <errno.h>#include <fcntl.h>#include <sys/stat.h>int main(){int fd;int len;char buf[1024];if(mkfifo("fifo1",0666) < 0 && errno != EEXIST)//创建FIFO管道perror("create fifo failed");if((fd = open("fifo1",O_RDONLY)) < 0)//以读打开FIFO{perror("open fifo failed");exit(1);}while((len = read(fd,buf,1024)) > 0)//读取FIFO管道printf("read message %s",buf);close(fd);return 0;}
消息队列
消息队列,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。
特点:
- 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。
- 消息队列独立于发送或接收进程。进程终止时,消息队列及其内容并不会被删除。
- 消息队列可以实现消息的随机查询,消息不一定要先进先出的次序读取,也可以按消息的类型读取。
原型:
123456789#include <sys/msg.h>//创建或打开消息队列,成功返回队列ID,失败返回-1int msgget(key_t key,int flag);//添加消息,成功返回0,失败返回-1int msgsnd(int msqid,const void * ptr,size_t size,int flag);//读取消息,成功返回消息数据的长度,失败返回-1int msgrcv(int msqid,void * ptr,size_t size,long type,int flag);//控制消息队列,成功返回0,失败返回-1int msgctl(int msqid,int cmd,struct msqid_ds * buf);什么时候msgget将创建一个新的消息队列?
- 如果没有与键值key相对于的消息队列,并且flag中包含了IPC_CREATE标志位
- key参数为IPC_PRIVATE
msgrcv在读取消息队列时,type参数有以下情况: - type == 0,返回消息队列中的第一个消息
- type > 0,返回队列中消息类型为type的第一个消息
- type < 0,返回队列中消息类型值小于或等于type绝对值的消息。如果这样的消息有多个,则取类型值最小的消息。
msg_server.c
1234567891011121314151617181920212223242526272829303132333435363738394041424344#include <stdio.h>#include <stdlib.h>#include <sys/msg.h>//用于创建一个唯一的key#define MSG_FILE "/etc/password"//消息结构struct msg_form{long mtype;char mtext[256];}msg_from;int main(){int msqid;key_t key;msg_form msg;//获取key值if((key = ftok(MSG_FILE,'Z')) < 0){perror("ftok error");exit(1);}//打印key值printf("message queue:key is %d",key);//创建消息队列if((msqid = msgget(key,IPC_CREATE|0777)) == -1){perror("msgget error");exit(1);}//打印消息队列ID及进程IDprintf("mssage's id is %s",msqid);getpid("pid is %d",getpid());//循环读取消息for(;;){msgrcv(msqid,&msg,256,888,0);//返回类型为888的第一个消息printf("server.receive msg.mtext is %s",msg.mtext);printf("server.receive msg.mtype is %d",msg.mtype);msg.mtype = 999;sprintf(msg.mtext,"hello,i'm server %d",getpid());msgsnd(msqid,&msg,sizeof(msg.mtext),0);}return 0;}msg_client.c
12345678910111213141516171819202122232425262728293031323334#include <stdio.h>#include <stdlib.h>#include <sys/msg.h>#define MSG_FILE "/etc/password"struct msg_form{long mtype;char mtext[256];}msg_form;int main(){int msqid;key_t key;msg_form msg;if((key = ftok(MSG_FILE,'Z')) < 0){perror("ftok error");exit(1);}printf("message queue-client key is %d",key);if((msqid = msgget(key,IPC_CREATE|0777)) == -1){perror("message error");exit(1);}printf("message's id is %d",msqid);printf("pid is %d",getpid());msg.mtype = 888;sprintf(msg.mtext,"hello,i'm client %d",getpid());msgsnd(msqid,&msg,sizeof(msg.mtext),0);msgrcv(msqid,&msg,256,999,0);printf("client.receive msg.mtext is %s",msg.mtext);printf("client.receive msg.mtype is %d",msg.mtype);return 0;}
信号量
信号量是一个计数器,用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点:
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
- 支持信号量组。
原型:
最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量。
而可以取多个正整数的信号量被称为通用信号量。
12345671 #include <sys/sem.h>2 // 创建或获取一个信号量组:若成功返回信号量集ID,失败返回-13 int semget(key_t key, int num_sems, int sem_flags);4 // 对信号量组进行操作,改变信号量的值:成功返回0,失败返回-15 int semop(int semid, struct sembuf semoparray[], size_t numops);6 // 控制信号量的相关信息7 int semctl(int semid, int sem_num, int cmd, ...);当
semget
创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems
),通常为1; 如果是引用一个现有的集合,则将num_sems
指定为 0 。123456struct sembuf{short sem_num; // 信号量组中对应的序号,0~sem_nums-1short sem_op; // 信号量值在一次操作中的改变量short sem_flg; // IPC_NOWAIT, SEM_UNDO} - 若
sem_op > 0
,表示进程释放相应的资源数,将 sem_op 的值加到信号量的值上。如果有进程正在休眠等待此信号量,则唤醒它们。 - 若
sem_op < 0
,请求 sem_op 的绝对值的资源。如果相应的资源数可以满足请求,则将该信号量的值减去sem_op的绝对值,函数成功返回;当相应的资源数不能满足请求时,这个操作与sem_flg
有关。如sem_flg 指定IPC_NOWAIT
,则semop函数出错返回EAGAIN
。sem_flg 没有指定IPC_NOWAIT
,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
1-当相应的资源数可以满足请求,此信号量的semncnt值减1,该信号量的值减去sem_op的绝对值。成功返回;
2-此信号量被删除,函数smeop出错返回EIDRM;
3-进程捕捉到信号,并从信号处理函数返回,此情况下将此信号量的semncnt值减1,函数semop出错返回EINTR - 若
sem_op == 0
,进程阻塞直到信号量的相应值为0:
当信号量已经为0,函数立即返回。如果信号量的值不为0,则依据sem_flg
决定函数动作:
sem_flg指定IPC_NOWAIT
,则出错返回EAGAIN
;sem_flg没有指定IPC_NOWAIT
,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:
1-信号量值为0,将信号量的semzcnt的值减1,函数semop成功返回;
2-此信号量被删除,函数smeop出错返回EIDRM;
3-进程捕捉到信号,并从信号处理函数返回,在此情况将此信号量的semncnt值减1,函数semop出错返回EINTR
在semctl
函数中的命令有多种,这里就说两个常用的: SETVAL
:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。IPC_RMID
:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。
示例:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697#include <stdio.h>#include <stdlib.h>#include <sys/sem.h>union semun{int val;struct semid_ds * buf;unsigned short * array;};// 初始化信号量int init_sem(int sem_id,int value){union semun tmp;tmp.val = value;if(semctl(sem_id,0,SETVAL,tmp) == -1){perror("init semaphore failed");return -1;}return 0;}// P操作:// 若信号量值为1,获取资源并将信号量值-1// 若信号量值为0,进程挂起等待int sem_p(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = -1;//p操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("p operation failed");return -1;}return 0;}//V操作//释放资源并将信号量+1//如果有进程正在挂起等待,则唤醒它们int sem_v(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = 1;//V操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("v operation failed");return -1;}return 0;}//删除信号量int del_sem(int sem_id){union semun tmp;if(semctl(sem_id,0,IPC_RMID,temp) == -1){perror("delete semaphore failed");return -1;}return 0;}int main(){int sem_id;//信号量IDkey_t key;pid_t pid;if((key = ftok(".",'Z')) < 0){perror("ftok failed");exit(1);}//创建信号量集,其中只有一个信号量if((sem_id = semget(key,1,IPC_CREATE|0666)) == -1){perror("semget failed");exit(1);}//初始化if((pid = fork()) == -1)perror("fork failed");else if(pid == 0)//子进程{sleep(2);printf("process child pid is %d",getpid());sem_v(sem_id);//释放资源}else//父进程{sem_p(sem_id);//等待资源printf("process father pid is %d",getpid());sem_v(sem_id);//释放资源del_sem(sem_id);//删除信号量}return 0;}
共享内存
指两个或多个进程共享一个给定的存储区
特点:
- 共享内存是最快的一种IPC,因为进程是直接对内存进行存取的。
- 因为多个进程可以同时操作,所以需要进程同步。
- 信号量和共享内存通常结合在一起使用,信号量用来同步对共享内存的访问。
原型:
123456789#include <sys/shm.h>//创建或获取一个共享内存,成功返回共享内存ID,失败返回-1int shmget(key_t key,size_t size,int flag);//连接共享内存到当前进程的地址空间,成功返回指向共享内存的指针,失败返回-1void * shmat(int shm_id,const void * addr,int flag);//断开与共享内存的连接,成功返回0,失败返回-1int shmdt(void * addr);//控制共享内存的相关信息,成功返回0,失败返回-1int shmctl(int shm_id,int cmd,struct shmid_ds * buf); - 当用shmget创建一段共享内存时,必须指定size;而如果引用一个已存在的共享内存,则将size指定为0。
- 当一段共享内存被创建以后,它不能被任何进程访问。必须使用shmat函数连接该共享内存到当前进程的地址空间,连接成功后把共享内存区对象映射到调用进程的地址空间,随后便可像本地空间一样访问。
- shmdt函数是用来断开shmat建立的连接的。需要注意的是,这并不是从系统中删除该共享内存,只是当前进程不能再房屋内该共享内存而已。
- shmctl函数可以对共享内存执行多种操作,根据cmd参数执行相对的操作,常用的是IPC_RMID(从系统中删除该共享内存)
示例:
server.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141#include <stdio.h>#include <stdlib.h>#include <sys/shm.h>//共享内存#include <sys/sem.h>//信号量#include <msg.h>//消息队列#include <string.h>//消息队列结构struct msg_form{long mtype;char mtext;};//联合体,用于semctrl初始化union semun{int val;struct semid_ds *buf;unsigned short * array;};// 初始化信号量int init_sem(int sem_id,int value){union semun tmp;tmp.val = value;if(semctl(sem_id,0,SETVAL,tmp) == -1){perror("init semaphore failed");return -1;}return 0;}// P操作:// 若信号量值为1,获取资源并将信号量值-1// 若信号量值为0,进程挂起等待int sem_p(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = -1;//p操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("p operation failed");return -1;}return 0;}//V操作//释放资源并将信号量+1//如果有进程正在挂起等待,则唤醒它们int sem_v(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = 1;//V操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("v operation failed");return -1;}return 0;}//删除信号量int del_sem(int sem_id){union semun tmp;if(semctl(sem_id,0,IPC_RMID,temp) == -1){perror("delete semaphore failed");return -1;}return 0;}//创建一个信号量集int create(key_t key){int sem_id;if((sem_id = semget(key,1,IPC_CREATE|0666)) == -1){perror("semget failed");exit(-1);}init_sem(sem_id,1);return sem_id;}int main(){key_t key;int shmid,semid,msqid;char * shm;char data[] = "this is server";struct shmid_ds buf1;//用于删除共享内存struct shmid_ds buf2;//用于删除消息队列struct msg_form msg;//消息队列用于通知对方更新了共享内存//获取key值if(key = ftof(".",'Z') < 0){perror("ftok error");exit(1);}//创建共享内存if((shmid = shmget(key,1024,IPC_CREATE|0666)) == -1){perror("create shared memory failed");exit(1);}//连接共享内存shm = (char*)shmat(shmid,0,0);if((int)shm == -1){perror("attach shared memory error");exit(1);}//创建消息队列if((msqid = msgget(key,IPC_CREATE|0777)) == -1){perror("msgget failed");exit(1);}//创建信号量semid = create_sem(key);while(1){msgrcv(msqid,&msg,1,888,0);//读取类型为888的消息if(msg.mtext == 'q')break;if(msg.mtext == 'r')//读取共享内存{sem_p(semid);printf("%s",shm);sem_v(semid);}}//断开连接共享内存shmdt(shm);//删除共享内存,消息队列,信号量shmctl(shmid,IPC_RMID,&buf1);shmctl(shmid,IPC_RMID,&buf2);del_sem(semid);return 0;}client.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120#include <stdio.h>#include <stdlib.h>#include <sys/shm.h>//共享内存#include <sys/sem.h>//信号量#include <msg.h>//消息队列#include <string.h>//消息队列结构struct msg_form{long mtype;char mtext;};//联合体,用于semctrl初始化union semun{int val;struct semid_ds *buf;unsigned short * array;};// P操作:// 若信号量值为1,获取资源并将信号量值-1// 若信号量值为0,进程挂起等待int sem_p(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = -1;//p操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("p operation failed");return -1;}return 0;}//V操作//释放资源并将信号量+1//如果有进程正在挂起等待,则唤醒它们int sem_v(int sem_id){struct sembuf sbuf;sbuf.sem_num = 0;//序号sbuf.sem_op = 1;//V操作sbuf.sem_flg = SEM_UNDO;if(semop(sem_id,&sbuf,1) == -1){perror("v operation failed");return -1;}return 0;}int main(){key_t key;int shmid,semid,msqid;char * shm;struct msg_form msg;int flag = 1;if(key = ftok(".",'Z') < 0){perror("ftok failed");exit(1);}//获取共享内存if((shmid = shmget(key,1024,0)) == -1){perror("shmget failed");exit(1);}//连接共享内存shm = (char*)shmat(shmid,0,0);if((int)shm == -1){perror("attach shared memory failed");exit(1);}//创建消息队列if((msqid = msgget(key,0)) == -1){perror("msgget failed");exit(1);}//写数据printf("sssssssssssssssssssssssssssss\n");printf(" ipc \n");printf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n");printf("sssssssssssssssssssssssssssss\n");while(flag){char c;printf("input command:");scanf("%c",&c);switch(c){case 'r':printf("data to send");sem_p(semid);//访问资源scanf("%s",shm);sem_v(semid);//释放资源//清空标准输入缓冲区while((c = getchar()) != '\n' && c != EOF);msg.mtype = 888;msg.mtext = 'r';//发消息通知服务器读数据msgsnd(msqid,&msg,sizeof(msg.mtext),0);break;case 'q':msg.mtype = 888;msg.mtext = 'q';msgsnd(msqid,&msg,sizeof(msg.mtext),0);flag = 0;break;default:printf("wrong input\n");while((c = getchar()) != '\n' && c != EOF);break;}}//断开连接shmdt(shm);return 0;}Socket(支持不同主机上的两个进程通信)
Streams(支持不同主机上的两个进程通信)
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 2020_04_2204/23
- ♥ Linux下调试dump记录08/11
- ♥ Python编程从入门到实践 三05/29
- ♥ 时区显示不正常04/18
- ♥ 【华东师大版八年级下册】05/16
- ♥ C++编程规范101规则、准则与最佳实践 一01/05