扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
这篇“linux可不可以创建多个进程”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“linux可不可以创建多个进程”文章吧。
专注于为中小企业提供成都网站设计、网站建设服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业罗江免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上千余家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
linux可以创建多个进程。linux支持多进程,可以同时处理多个任务,实现系统资源的最大利用。linux进程间的通信方式:1、利用无名管道pipe;2、利用有名管道(FIFO);3、利用信号single;4、利用共享内存;5、利用消息队列;6、利用信号量。
linux可以创建多个进程。
linux 支持多进程。多进程系统的一个好处,就是可以同时处理多个任务,实现系统资源的最大利用。
1.1 概述
linux中把正在运行的程序称作进程。
程序:静态的概念,它是一个编译好的二进制文件
进程:动态的概念,当程序运行的时候,系统会自动运行一个对应进程
进程包含了进程控制块(PCB),代码段,数据段三个部分
进程控制块:在linux中是用一个结构体来表示的,记录进程的状态信息
僵尸进程:父进程优先于子进程退出
如果你创建了子进程,但是在父进程中没有回收该子进程的资源,那么该子进程就会变成僵尸进程,僵尸进程最终会由系统中一个叫做INIT的进程回收。
init进程(1号进程)是系统启动的时候运行的第一个进程,是所有进程的祖进程。
top查看动态的进程信息
ps -ef查看进程的详细信息
pstree以树状的形式显示进程的信息
bg将挂起的进程放到后台运行
1.2 进程运行状态
执行态( RUNNING):进程正在占有CPU。
就绪态( RUNNING):进程处于等待队列中等待调度。
浅睡眠( INTERRUPTABLE):此时进程在等待一个事件的发生或某种系统资源,可响应信号。
深睡眠( UNINTERRUPTABLE): 此时进程在等待一个事件的发生或某种系统资源, 无法响应信号。
停止态( STOPPED): 此时进程被暂停。
僵尸态( ZOMBIE): 此时进程不能被调度,但是PCB未被释放。
死亡态( DEAD): 这是一个已终止的进程,且PCB将会被释放
内核态:也叫内核空间,是内核进程/线程所在的区域。主要负责运行系统、硬件交互。
用户态:也叫用户空间,是用户进程/线程所在的区域。主要用于执行用户程序。
1、区别
内核态:运行的代码不受任何限制,CPU可以执行任何指令。
用户态:不能调度CPU,不能直接访问硬件。运行的代码需要受到CPU的很多检查,不能直接访问内核数据和程序,也就是不可以像内核态线程一样访问任何有效地址。
操作系统在执行用户程序时,主要工作在用户态,只有在其执行没有权限完成的任务时才会切换到内核态。
2、区分用户态和内核态原因
保护机制,防止用户进程误操作或者是恶意破坏系统
保证资源的集中管理,减少资源的使用冲突。
3、用户态切换到内核态方式
(1)系统调用(主动)
系统调用(system call)是操作系统提供给用户进程请求操作系统做一些特权操作的接口,即为用户进程提供服务的窗口。在Linux下可以通过man syscalls命令查看Linux提供的所有系统调用API接口。
由于用户态无法完成某些任务,用户态会请求切换到内核态,内核态通过为用户专门开放的中断完成切换。
(2)外围设备中断(被动)
外围设备发出中断信号,当中断发生后,当前运行的进程暂停运行,并由操作系统内核对中断进程处理,如果中断之前CPU执行的是用户态程序,就相当于从用户态向内核态的切换。
中断用于保证CPU控制权交给操作系统,从而让操作系统可以执行某些操作。
(3)异常(被动)
在执行用户程序时出现某些不可知的异常,会从用户程序切换到内核中处理该异常的程序,也就是切换到了内核态。
1.3 进程接口函数
1、fork()、vfork()
(1)新建的进程称作子进程,它复制了父进程的所有资源(只在创建的那个时间复制一次,以后全局变量值是不同),父子进程谁先谁后是不确定。
#include
(2)**vfork()**子进程共享了父进程的所有资源,它一定是子进程先运行,然后才是父进程运行(即使你加上sleep()人为去干扰也是没有用的)
(3)注意
子进程中使用了exit()跟没有使用结果完全不一样
父子进程中是否使用sleep()来让出cpu时间片也是不一样的
父进程中是否使用wait(),waitpid()结果也是不一样的
(4)进程切换执行
1、exit()、_exit()
#include
1、wait()
#include
2、 waitpid()
pid_t waitpid(pid_t pid, int *stat_loc, int options); 回收子进程/进程组
参数: pid ----》你指定要回收的那个子进程的id
<-1 等待进程组号为-pid中的某个子进程退出
-1 等待任意一个子进程
==0 等待本进程组中的某个子进程退出
> 0 等待PID为pid的进程
stat_loc-----》存放子进程退出状态(可为NULL)
options ----》一般设置为0
WNOHANG 当没有子进程时立即返回
WUNTRACED 当有子进程被暂停时立即返回
WCONTINUED 当有子进程收到SIGCONT立即返回
返回值:-1 执行失败
> 0 成功 返回值为被回收的进程的PID
0 指定了WNOHANG,且没有已退出的子进程
(1)获取自己的id getpid()
#include
不管是进程间的通信,还是线程间的通信。无非都是为了解决一个问题:就是共享资源的分配(协调不同的进程/线程对于共享的资源的访问)
2.1 进程间的通信方式
1、传统的进程间通信方式
无名管道
有名管道
信号
2、System V IPC对象
共享内存
消息队列
信号量
3、BSD
网络套接字(socket)
1、特点:最原始的进程间的通信方式
它只能在具有亲缘关系的进程间通信(父子进程,兄弟进程);
它没有名字(是存在的);
可以在linux和windows之间的共享中创建(根本就不会生成管道文件),但是有名管道就不可以了(生成管道文件);
半双工通信。
2、无名管道的使用
(1)创建pipe()
#include
(2)pipe信息收发
myid = fork(); //创建子进程
if(myid == 0)
{
write(fd[1],"dad,thanks!",20); //子进程向父进程发送消息
close(fd[1]);
close(fd[0]);
exit(0);
}
else if(myid > 0)
{
read(fd[0],buf,20); //父进程阻塞接受子进程消息
printf("buf is:%s\n",buf);
close(fd[1]);
close(fd[0]);
}
1、特点:随便两个进程之间都行
不能在linux和windows之间的共享中创建;
保证写入的原子性(原子性:要么不做,要做就一口气做完不受外界的干扰);
有名管道不能够覆盖着创建(一般代码中使用access()函数来判断是否存在,如果已经存在同名的管道,就不能再次创建);
使用完毕记得关闭;
当管道以只读的方式打开,会阻塞,直到有另外一个进程以只写的方式打开这个管道,那么就不阻塞了;如果是以可读写的方式打开,就不会阻塞了。
全双工通信,半双道。
2、有名管道的使用
(1)创建mkfifo()
#include
(2)FIFO进程信息收发
fifo_read.c :-----------》
#define FIFO1 "myfifo1"
#define FIFO2 "myfifo2"
int main(void) {
int my_fd,fd1,fd2;
char r_buff[30];
char w_buff[30];
bzero(r_buff,30);
if(access(FIFO1,F_OK)==-1) {
my_fd = mkfifo(FIFO1,0664); //创建管道1
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
if(access(FIFO2,F_OK)==-1) {
my_fd = mkfifo(FIFO2,0664); //创建管道2
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
fd1 = open(FIFO1,O_RDONLY); //只读打开管道1,获取管道文件描述符
if(fd1==-1) {
printf("open fifo1 file failed!\n");
exit(0);
}
fd2 = open(FIFO2,O_WRONLY); //只写打开管道2,获取管道文件描述符
if(fd2==-1) {
printf("open fifo2 file failed!\n");
exit(0);
}
while(1) {
bzero(r_buff,30);
read(fd1,r_buff,sizeof(r_buff)); //读取管道1的消息
printf("client receive message is: %s\n",r_buff);
printf("client please input a message!\n");
fgets(w_buff,30,stdin);
write(fd2,w_buff,30); //发送信息给管道2
}
close(fd2);
close(fd1);
return 0;
}
fifo_write.c :-----------》
#define FIFO1 "myfifo1"
#define FIFO2 "myfifo2"
int main(void)
{
int my_fd,fd1,fd2;
char w_buff[30];
char r_buff[30];
bzero(w_buff,30);
if(access(FIFO1,F_OK)==-1) {
my_fd = mkfifo(FIFO1,0664);
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
if(access(FIFO2,F_OK)==-1) {
my_fd = mkfifo(FIFO2,0664);
if(my_fd == -1) {
perror("failed!\n");
return -1;
}
}
fd1 = open(FIFO1,O_WRONLY);
if(fd1==-1) {
printf("open fifo1 file failed!\n");
exit(0);
}
fd2 = open(FIFO2,O_RDONLY);
if(fd2==-1) {
printf("open fifo2 file failed!\n");
exit(0);
}
while(1) {
bzero(w_buff,30);
printf("server please input a message!\n");
fgets(w_buff,30,stdin);
write(fd1,w_buff,strlen(w_buff)); //写入消息到管道1文件
read(fd2,r_buff,30); //读取信息从管道2
printf("server receive message is:%s\n",r_buff);
}
close(fd1);
close(fd2);
return 0;
}
程序(进程)在运行过程中,外界不定时会发信号给该程序,这个时候该程序面临着两种选择:
不理它(阻塞/忽略)
阻塞:是指将信号挂起,等到程序运行完了再去响应
忽略:舍弃这个信号
响应它
1、linux当中有哪些信号: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
(1)1到31号信号称作非实时信号:不支持队列(如果同时来了多个信号,响应是没有规律)
(2)用户自定义的信号 10) SIGUSR1 12) SIGUSR2
(3) 34到64号信号叫做实时信号:支持队列,是linux系统中后面添加进来的信号
信号类似于中断: 硬件 软件
以上信号有两个很特殊:SIGKILL,SIGSTOP不能够被忽略,也不能被阻塞
2、信号相关的操作函数
(1)发送信号kill()
#include
(2)信号的捕捉 signal()
#include
(3)等待信号 pause()
#include
(4)信号的阻塞
每个进程都有属于它自己的一个信号掩码(也就是该进程在运行的过程中会阻塞掉的那些信号就被称作信号掩码)。
关于信号掩码操作的一系列函数:
#include
(5)配置信号掩码 sigprocmask()—阻塞或解除阻塞信号
#include
(6)捕捉指定信号并获取信号携带信息sigaction()
#include
查看共享内存: ipcs -m
删除共享内存: ipcrm -m 共享内存的id
SYSTEM-V ipc通信方式:共享内存、信号量、消息队列。
1、共享内存特点:跟mmap()思想上有些类似
在进程通信方式中共享内存是效率最高的,进程可以直接读写内存,而不需要任何数据的拷贝;
如果代码不人为地删除共享共享内存,那么程序退出的时候它还在;
多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
2、共享内存对应的一系列操作函数
(1)创建共享内存:shmget()
#include
(2) 映射共享内存到用户空间 shmat()
#include
(3)解除映射:shmdt()
#include
(4)删除共享内存:shmctl()
#include
3、共享内存简单示例
shm_write.c :----------》
int main() {
int shmid;
int *p;
// 创建共享内存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享内存到进程
p = (int *)shmat(shmid,NULL,0);
*p = 10;
// 解除映射
shmdt(p);
// 删除内存
//shmctl(shmid,IPC_RMID,NULL);
return 0;
}
shm_read.c :----------》
int main() {
int shmid;
int *p;
// 创建共享内存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享内存到进程
p = (int *)shmat(shmid,NULL,0);
printf("p is :%d\n",*p);
// 解除映射
shmdt(p);
// 删除内存
shmctl(shmid,IPC_RMID,NULL);
return 0;
}
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列由消息队列ID来唯一标识
消息队列可以按照类型来发送/接收消息
消息队列的操作包括创建或打开消息队列、添加消息、读取消息和控制消息队列
1、消息队列的特点
写入消息队列的信息,在编写程序的时候会人为的去设置消息的类型(用整数来表示),目的是为了其它进程在读取信息的时候能够准确地通过消息的类型判断要读取的信息。
2、消息队列操作的系列函数
(1)消息队列的创建 msgget()
#include
(2)消息队列的收发信息msgsnd()msgrcv()
#include
(3)消息队列的删除 msgctl()
#include
3、消息队列通信简单示例
pthread1.c :-----------》
#define SIZE 64
//数据接收结构体
struct msg_rv
{
int mtype;
char msg[50];
};
//数据发送结构体
struct msg_snd
{
int mtype;
char msg[50];
};
int main(void) {
int msgid;
struct msg_rv data;
struct msg_snd snddata;
char buff[50];
//获取msgid
msgid = msgget((key_t)123,IPC_CREAT|0666);
if(msgid == -1) {
printf("msgid failed!\n");
return -1;
}
data.mtype = 88;
snddata.mtype = 89;
while(1) {
bzero(buff,50);
printf("please input data!\n");
fgets(buff,50,stdin);
strcpy(snddata.msg,buff);
if(strncmp(snddata.msg,"end",3)==0) {
break;
}
msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值发送出去
usleep(20);
printf("run here!\n");
if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1) {
printf("msgsnd failed!\n");
return -1;
}
printf("receive data:%s\n",data.msg);
if(strncmp(data.msg,"end",3)==0) {
break;
}
}
//撤消消息队列
msgctl(msgid,IPC_RMID,0);
return 0;
}
pthread2.c :------------------------》
#define SIZE 64
//数据接收结构体
struct msg_rv
{
int mtype;
char msg[50];
};
//数据发送结构体
struct msg_snd
{
int mtype;
char msg[50];
};
int main(void)
{
int msgid;
struct msg_rv data;
struct msg_snd snddata;
char buff[50];
data.mtype = 89;
snddata.mtype = 88;
//获取msgid
msgid = msgget((key_t)123,IPC_CREAT|0666);
if(msgid == -1) {
printf("msgid failed!\n");
return -1;
}
while(1) {
//接受
if(msgrcv(msgid,(void *)&data,sizeof(struct msg_rv),data.mtype,0)==-1)
{
printf("msgsnd failed!\n");
return -1;
}
printf("receive data:%s\n",data.msg);
if(strncmp(data.msg,"end",3)==0) {
break;
}
//发送
printf("please input data:\n");
bzero(buff,50);
fgets(buff,50,stdin);
strcpy(snddata.msg,buff);
printf("data = %s\n",snddata.msg);
if(strncmp(snddata.msg,"end",3)==0) {
break;
}
msgsnd(msgid,(void *)&snddata,strlen(buff)+4,0);//得到的值发送出去
printf("run here!\n");
}
//撤消消息队列
msgctl(msgid,IPC_RMID,0);
return 0;
}
信号量协调不同进程对于共享资源的访问,它是不同进程间或一个给定进程内部不同线程间同步的机制。
1、信号量概述
(1)二值信号量
值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0
(2)计数信号量
值在0到n之间。用来统计资源,其值代表可用资源数
(3)对信号量的操作
P操作:即申请资源,亦即将信号量值减1,可能引起进程睡眠。
V操作:即释放资源,亦即将信号量值加1,V操作从不会睡眠。
等0操作:不申请也不释放资源,而是令进程阻塞直到信号量的值为0为止
2、信号量相关的接口函数
(1) 创建信号量集合semget()
#include
(2)设置/删除信号量集 semctl()
#include
(3)信号量的PV操作 semop()
核心:信号量为 <=0 时进行p操作,会阻塞程序,直到另一进程中是该信号进行了v操作后,本程序才会继续运行------》key值相同,信号量共通
p 减一操作
v 加一操作
#include
3、信号量协同共享内存示例代码
pthread1.c :-----------》
int main()
{
int semid;
int shmid;
char *p;
struct sembuf mysembuf1,mysembuf2;
mysembuf1.sem_num = 0;
mysembuf1.sem_flg = SEM_UNDO;
mysembuf1.sem_op = 1;
mysembuf2.sem_num = 1;
mysembuf2.sem_flg = SEM_UNDO;
mysembuf2.sem_op = -1;
// 创建信号量集合
semid = semget((key_t)789,2,IPC_CREAT|0666);
if(semid == -1) {
perror("creat sem failed!\n");
return -1;
}
// 创建共享内存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享内存到进程
p = (char *)shmat(shmid,NULL,0);
while(1) {
semop(semid,&mysembuf2,1); // 对信号量2进行p操作(减一)
printf("the message I recv is:%s\n",p);
printf("please input a message!\n");
scanf("%s",p);
printf("message is %s\n",p);
semop(semid,&mysembuf1,1); // 对信号量1进行v操作(加一)
}
//解除映射
shmdt(p);
//删除共享内存
shmctl(semid, IPC_RMID, NULL);
}
pthread2.c :-----------》
int main() {
int semid;
int shmid;
char *p;
struct sembuf mysembuf1,mysembuf2;
mysembuf1.sem_num = 0; // 信号集合中的第一个信号
mysembuf1.sem_flg = SEM_UNDO;
mysembuf1.sem_op = -1; //p操作
mysembuf2.sem_num = 1; // 信号集合中的第二个信号
mysembuf2.sem_flg = SEM_UNDO;
mysembuf2.sem_op = 1; // v操作
// 创建信号量集合
semid = semget((key_t)789,2,IPC_CREAT|0666);
if(semid == -1) {
perror("creat sem failed!\n");
return -1;
}
// 设置信号量的值
semctl(semid,0,SETVAL,1); //第一个信号量初值为1
printf("sem num is:%d\n",semctl(semid,0,GETVAL));
semctl(semid,1,SETVAL,0); //第二个信号量初值为0
printf("sem num is:%d\n",semctl(semid,1,GETVAL));
// 创建共享内存
shmid = shmget((key_t)456,1024,IPC_CREAT|IPC_EXCL|0666);
if((shmid == -1)&&(errno == EEXIST)) {
shmid = shmget((key_t)456,1024,0666);
}
// 映射共享内存到进程
p = (char *)shmat(shmid,NULL,0);
while(1) {
semop(semid,&mysembuf1,1); // 对信号量1进行p操作(减一)不阻塞,因为初值为1
// 执行完这句话以后信号量的值就立马变成1
printf("the message I recv is:%s\n",p);
printf("please input a message!\n");
scanf("%s",p);
printf("message is %s\n",p);
semop(semid,&mysembuf2,1); // 对信号量2进行v操作(加一)不阻塞,因为初值为0
}
//解除映射
shmdt(p);
//删除共享内存
shmctl(semid, IPC_RMID, NULL);
}
2.3 IPC shell命令操作
ipcs -q 查看消息队列
ipcrm -q MSG_ID 删除消息队列
ipcs -m 查看共享内存
ipcrm -m SHM_ID 删除共享内存
ipcs -s 查看信号量
ipcrm -s SEM_ID 删除信号量
2.2 进程间通讯方式比较
pipe: 具有亲缘关系的进程间,单工,数据在内存中
fifo: 可用于任意进程间,双工,有文件名,数据在内存
signal: 唯一的异步通信方式
msg:常用于cs模式中, 按消息类型访问 ,可有优先级
shm:效率最高(直接访问内存) ,需要同步、互斥机制
sem:配合共享内存使用,用以实现同步和互斥
以上就是关于“linux可不可以创建多个进程”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注创新互联行业资讯频道。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流