linux可不可以创建多个进程

linux可以创建多个进程。Linux可以同时处理多个任务,支持多进程,以实现系统资源的最大化利用。linux进程间的通信方式:1、利用无名管道pipe;2、利用有名管道(FIFO);3、利用信号single;4、利用共享内存;5、利用消息队列;6、利用信号量。

linux可以创建多个进程。

linux 支持多进程。多进程系统的一个好处,就是可以同时处理多个任务,实现系统资源的最大利用。

第1章 linux多进程介绍

1.1 概述

1.1.1 进程概念

linux中把正在运行的程序称作进程。
程序:静态的概念,它是一个编译好的二进制文件
进程:动态的概念,当程序运行的时候,系统会自动运行一个对应进程
进程包含了进程控制块(PCB),代码段,数据段三个部分
进程控制块:在linux中是用一个结构体来表示的,记录进程的状态信息
僵尸进程:父进程优先于子进程退出
如果你创建了子进程,但是在父进程中没有回收该子进程的资源,那么该子进程就会变成僵尸进程,僵尸进程最终会由系统中一个叫做INIT的进程回收。
init进程(1号进程)是系统启动的时候运行的第一个进程,是所有进程的祖进程。

1.1.2 进程查看shell命令

top 查看动态的进程信息

ps -ef 查看进程的详细信息

pstree 以树状的形式显示进程的信息

bg 将挂起的进程放到后台运行

1.2 进程运行状态

1.2.1 运行状态

执行态( RUNNING):进程正在占有CPU。

就绪态( RUNNING):进程处于等待队列中等待调度。

浅睡眠( INTERRUPTABLE):此时进程在等待一个事件的发生或某种系统资源,可响应信号。

深睡眠( UNINTERRUPTABLE): 此时进程在等待一个事件的发生或某种系统资源, 无法响应信号。

停止态( STOPPED): 此时进程被暂停。

僵尸态( ZOMBIE): 此时进程不能被调度,但是PCB未被释放。

死亡态( DEAD): 这是一个已终止的进程,且PCB将会被释放

linux可不可以创建多个进程

1.2.2 用户态/内核态

内核态:也叫内核空间,是内核进程/线程所在的区域。主要负责运行系统、硬件交互。
用户态:也叫用户空间,是用户进程/线程所在的区域。主要用于执行用户程序。

linux可不可以创建多个进程

1、区别
内核态:运行的代码不受任何限制,CPU可以执行任何指令。
用户态:不能调度CPU,不能直接访问硬件。运行的代码需要受到CPU的很多检查,不能直接访问内核数据和程序,也就是不可以像内核态线程一样访问任何有效地址。

操作系统在执行用户程序时,主要工作在用户态,只有在其执行没有权限完成的任务时才会切换到内核态。

2、区分用户态和内核态原因

保护机制,防止用户进程误操作或者是恶意破坏系统

保证资源的集中管理,减少资源的使用冲突。

3、用户态切换到内核态方式
(1)系统调用(主动)
系统调用(system call)是操作系统提供给用户进程请求操作系统做一些特权操作的接口,即为用户进程提供服务的窗口。在Linux下可以通过man syscalls命令查看Linux提供的所有系统调用API接口。
由于用户态无法完成某些任务,用户态会请求切换到内核态,内核态通过为用户专门开放的中断完成切换。

(2)外围设备中断(被动)
外围设备发出中断信号,当中断发生后,当前运行的进程暂停运行,并由操作系统内核对中断进程处理,如果中断之前CPU执行的是用户态程序,就相当于从用户态向内核态的切换。
中断用于保证CPU控制权交给操作系统,从而让操作系统可以执行某些操作。

(3)异常(被动)
在执行用户程序时出现某些不可知的异常,会从用户程序切换到内核中处理该异常的程序,也就是切换到了内核态。

1.3 进程接口函数

1.3.1 进程创建 fork/vfork

1、fork()、vfork()
(1)新建的进程称作子进程,它复制了父进程的所有资源(只在创建的那个时间复制一次,以后全局变量值是不同),父子进程谁先谁后是不确定。

#include pid_t fork(void);  返回值:  > 0表示处于父进程中 这个时候的返回值就是**子进程进程id号**                  ==0 表示处于子进程中                  < 0 创建进程出错                  #include    #include pid_t vfork(void);

(2)**vfork()**子进程共享了父进程的所有资源,它一定是子进程先运行,然后才是父进程运行(即使你加上sleep()人为去干扰也是没有用的)
(3)注意
子进程中使用了exit()跟没有使用结果完全不一样
父子进程中是否使用sleep()来让出cpu时间片也是不一样的
父进程中是否使用wait(),waitpid()结果也是不一样的

linux可不可以创建多个进程

(4)进程切换执行

linux可不可以创建多个进程

1.3.2 进程的退出 exit/_exit

1、exit()、_exit()

    #include     void exit(int status); void _exit(int status);参数:          status --->进程退出时的状态          status在实际编写程序中是可以自己约定的:          比如:  exit(2)----》出现打开文件错误                  exit(3)----》出现段错误(逻辑错误)                  exit(0)----》正常退出  返回:  void区别:          exit()在退出的时候会刷新IO缓冲区,然后才退出(负责任的退出)          _exit() 直接退出(不负责任的退出)

1.3.3 等待子进程退出(父进程回收资源)wait/waitpid

1、wait()

#include     pid_t wait(int *stat_loc);   返回值:你回收的那个子进程的id号   参数:stat_loc --》保存子进程退出时的状态信息(不仅仅只是返回值)   stat_loc里面不仅仅只是保存了exit退出时的数值,它还保存了子进程退出时是哪个信号让它退出的,      出错了是什么原因导致的。

linux可不可以创建多个进程

2、 waitpid()

pid_t waitpid(pid_t pid, int *stat_loc, int options); 回收子进程/进程组    参数: pid ----》你指定要回收的那个子进程的id            0 等待PID为pid的进程stat_loc-----》存放子进程退出状态(可为NULL)             options ----》一般设置为0WNOHANG  当没有子进程时立即返回WUNTRACED 当有子进程被暂停时立即返回                        WCONTINUED  当有子进程收到SIGCONT立即返回返回值:-1 执行失败> 0 成功 返回值为被回收的进程的PID0  指定了WNOHANG,且没有已退出的子进程

1.3.4 获取进程的id–getpid

(1)获取自己的id    getpid()#include pid_t getpid(void);  返回值:就是该进程的id号(2) 获取父进程id    getppid()         #include          pid_t getppid(void);  返回值:就是父进程的id号

第2章 linux多进程间通信方式

不管是进程间的通信,还是线程间的通信。这句话可以重写为:解决共享资源的分配问题是其根本目的,即协调多个进程/线程对共享资源的访问

2.1 进程间的通信方式

1、传统的进程间通信方式

无名管道

有名管道

信号

2、System V IPC对象

共享内存

消息队列

信号量

3、BSD

网络套接字(socket)

2.1.1 无名管道pipe

1、特点:最原始的进程间的通信方式
它只能在具有亲缘关系的进程间通信(父子进程,兄弟进程);
它没有名字(是存在的);
可以在linux和windows之间的共享中创建(根本就不会生成管道文件),但是有名管道就不可以了(生成管道文件);
半双工通信。

2、无名管道的使用
(1)创建pipe()

    #include    int pipe(int fildes[2]);   参数:fildes[2]里面放的是两个文件描述符fildes[0],fildes[1]         fildes[0] 读端         fildes[1] 写端   返回值:成功返回0    失败返回-1

(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:%sn",buf);close(fd[1]);close(fd[0]);}

2.1.2 有名管道(FIFO)

1、特点:随便两个进程之间都行

不能在linux和windows之间的共享中创建;

保证写入的原子性(原子性:要么不做,要做就一口气做完不受外界的干扰);

有名管道不能够覆盖着创建(一般代码中使用access()函数来判断是否存在,如果已经存在同名的管道,就不能再次创建);

使用完毕记得关闭;

当管道以只读的方式打开,会阻塞,直到有另外一个进程以只写的方式打开这个管道,那么就不阻塞了;如果是以可读写的方式打开,就不会阻塞了。

全双工通信,半双道。

2、有名管道的使用
(1)创建mkfifo()

   #include    #include    int mkfifo(const char *pathname, mode_t mode);  参数:pathname 有名管道的路径名       mode:权限  0666  返回值:0 成功  -1 失败

(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);//创建管道1if(my_fd == -1) {perror("failed!n");return -1;}}if(access(FIFO2,F_OK)==-1) {my_fd = mkfifo(FIFO2,0664);//创建管道2if(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: %sn",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);  //读取信息从管道2printf("server receive message is:%sn",r_buff);}close(fd1);close(fd2);return 0;}

2.1.3 信号 single

程序(进程)在运行过程中,外界不定时会发信号给该程序,这个时候该程序面临着两种选择:
不理它(阻塞/忽略)
阻塞:是指将信号挂起,等到程序运行完了再去响应
忽略:舍弃这个信号
响应它

1、linux当中有哪些信号:kill -l 查看

 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL10) SIGUSR111) SIGSEGV12) SIGUSR213) SIGPIPE14) SIGALRM15) SIGTERM16) SIGSTKFLT17) SIGCHLD18) SIGCONT19) SIGSTOP20) SIGTSTP21) SIGTTIN22) SIGTTOU23) SIGURG24) SIGXCPU25) SIGXFSZ26) SIGVTALRM27) SIGPROF28) SIGWINCH29) SIGIO30) SIGPWR31) SIGSYS34) SIGRTMIN35) SIGRTMIN+136) SIGRTMIN+237) SIGRTMIN+338) SIGRTMIN+439) SIGRTMIN+540) SIGRTMIN+641) SIGRTMIN+742) SIGRTMIN+843) SIGRTMIN+944) SIGRTMIN+1045) SIGRTMIN+1146) SIGRTMIN+1247) SIGRTMIN+1348) SIGRTMIN+1449) SIGRTMIN+1550) SIGRTMAX-1451) SIGRTMAX-1352) SIGRTMAX-1253) SIGRTMAX-1154) SIGRTMAX-1055) SIGRTMAX-956) SIGRTMAX-857) SIGRTMAX-758) SIGRTMAX-659) SIGRTMAX-560) SIGRTMAX-461) SIGRTMAX-362) SIGRTMAX-263) SIGRTMAX-164) SIGRTMAX

(1)1到31号信号称作非实时信号:不支持队列(如果同时来了多个信号,响应是没有规律)
(2)用户自定义的信号 10) SIGUSR1 12) SIGUSR2
(3) 34到64号信号叫做实时信号:支持队列,是linux系统中后面添加进来的信号
信号类似于中断: 硬件 软件
以上信号有两个很特殊:SIGKILL,SIGSTOP不能够被忽略,也不能被阻塞

2、信号相关的操作函数
(1)发送信号kill()

 #include  int kill(pid_t pid, int sig); 参数:   pid ----》进程的id正数:要接收信号的进程的进程号0:信号被发送到所有和pid进程在同一个进程组的进程-1:信号发给所有的进程表中的进程(除了进程号最大的进程外)   sig ----》信号名字   返回值:0 成功   -1 出错

(2)信号的捕捉 signal()

 #include  void (*signal(int sig, void (*func)(int)))(int);  // SIGKILL         参数:sig ----》你需要捕捉的那个信号               void (*func)(int) ----》函数指针,回调函数,捕捉到对应的信号的时候就调用该函数;第二个参数除了可以传递一个函数指针意外,还可以使用以下两个宏定义:                SIG_IGN ---->你捕捉到的那个信号会被忽略                SIG_DFL-----》你捕捉的信号会采用系统默认的方式响应返回值:成功:设置之前的信号处理方式出错:-1

(3)等待信号 pause()

#include int pause(void);返回值:-1 把error值设为EINTR0 成功

(4)信号的阻塞
每个进程都有属于它自己的一个信号掩码(也就是该进程在运行的过程中会阻塞掉的那些信号就被称作信号掩码)。
关于信号掩码操作的一系列函数:

#include   int sigemptyset(sigset_t *set):清空信号掩码        int sigfillset(sigset_t *set):将所有的信号添加到信号掩码中        int sigaddset(sigset_t *set, int signum):将特定的信号添加到信号掩码中   int sigdelset(sigset_t *set, int signum):将特定的信号从掩码中删除  int sigismember(const sigset_t *set, int signum):判断某个信号在不在该掩码中参数:sigset_t ----》存储被进程阻塞的信号

(5)配置信号掩码 sigprocmask()—阻塞或解除阻塞信号

    #include  int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset)参数:      how ---》SIG_BLOCK 将set所包含的信号添加到原来的信号掩码中              SIG_SETMASK 用set去替换原来的信号掩码              SIG_UNBLOCK 将set中包含的信号从原来的掩码中删除      set ---》新的信号掩码      oset ---》原本的信号掩码 原本进程中信号掩码包含了:SIGINT ,SIGCONT

(6)捕捉指定信号并获取信号携带信息sigaction()

#include int sigaction(int sig, const struct sigaction *restrict act, struct sigaction *restrict oact);参数:  sig ---》你要捕捉的那个信号    act ---》你需要捕捉的信号对应的响应函数就定义在这个结构体    oact ---》原来的    struct sigaction    {        void(*) (int)     sa_handler ----》 信号的响应函数        sigset_t          sa_mask  ---》信号的掩码        int               sa_flags ----》  SA_SIGINFO        void(*) (int, siginfo_t * ,void )---》信号的响应函数     }sa_flags ---》等于SA_SIGINFO,那么信号的响应函数是void(*) (int, siginfo_t * ,void )          不等于,那么信号的响应函数是void(*) (int)    siginfo_t---》/usr/include/i386-linux-gnu/bits/siginfo.h 保存的是信号的状态信息,信号的标号,发送该信号的进程的id等等这些

2.1.4 共享内存

查看共享内存: ipcs -m
删除共享内存: ipcrm -m 共享内存的id
SYSTEM-V ipc通信方式:共享内存、信号量、消息队列。

1、共享内存特点:跟mmap()思想上有些类似

在进程通信方式中共享内存是效率最高的,进程可以直接读写内存,而不需要任何数据的拷贝

如果代码不人为地删除共享共享内存,那么程序退出的时候它还在;

多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等

linux可不可以创建多个进程

2、共享内存对应的一系列操作函数
(1)创建共享内存:shmget()

#include  int shmget(key_t key, size_t size, int shmflg);返回值:成功—共享内存对象的mid(标识符) 出错—-1参数:key----》创建共享内存需要用到的键值      size----》内存空间的大小(字节)      shmflg----》设置属性  IPC_CREAT   IPC_EXCL    0666组合      key键值的获取有两种方法: **方法一**:使用ftok()生成键值            #include             #include             key_t ftok(const char *pathname, int proj_id);            参数:pathname----》 路径名                  proj_id----》整数                 ftok(“.”  ,  11)  生成一个唯一的key值            进程1:ftok(“.”  ,  11)  ----》shmget( 100);.............       进程2:ftok(“/home/gec”  ,  11)  ----》shmget( 106);    无法通信,要确保键值一致才能通信        **方法二:**不使用ftok(),程序员自己写个数字            shmget((key_t)1234, size_t size, int shmflg);

(2) 映射共享内存到用户空间 shmat()

#include void *shmat(int shmid, const void *shmaddr, int shmflg); 返回值:成功—映射到用户空间的那片地址的首地址   出错—-1 参数:shmid ----》使用shmget的返回值      shmaddr----》一般设置为NULL 系统自动分配       shmflg----》 SHM_RDONLY:共享内存只读        一般设置为0: 共享内存可读写         if it is 0 and the calling process has read and write permission, the segment is attached for reading and writing.

(3)解除映射:shmdt()

#include int shmdt(const void *shmaddr);参数:shmaddr----》 shmat()共享内存映射后的地址返回值:成功—0 出错—-1

(4)删除共享内存:shmctl()

 #include  int shmctl(int shmid, int cmd, struct shmid_ds *buf); 参数: shmid----》共享内存的id        cmd----》IPC_RMID    删除共享内存                 IPC_STAT  (获取对象属性)        IPC_SET (设置对象属性)        *buf----》指定IPC_STAT/IPC_SET时保存共享内存的状态信息返回值:成功失败—-1

linux可不可以创建多个进程

linux可不可以创建多个进程

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 :%dn",*p);// 解除映射shmdt(p);// 删除内存shmctl(shmid,IPC_RMID,NULL);return 0;}

2.1.5消息队列

消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列由消息队列ID来唯一标识
消息队列可以按照类型来发送/接收消息
消息队列的操作包括创建或打开消息队列、添加消息、读取消息和控制消息队列

linux可不可以创建多个进程

1、消息队列的特点
写入消息队列的信息,在编写程序的时候会人为的去设置消息的类型(用整数来表示),目的是为了其它进程在读取信息的时候能够准确地通过消息的类型判断要读取的信息。

2、消息队列操作的系列函数
(1)消息队列的创建 msgget()

 #include  int msgget(key_t key, int msgflg);

linux可不可以创建多个进程

(2)消息队列的收发信息msgsnd()msgrcv()

 #include  int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);参数:void *msgp ----》你要发送信息就存储在这个指针中                  在实际的编程中我们都是定义一个结构体来存储信息                  struct msgbuf {                      long mtype;  ----》消息的类型                      char mtext[100]; ----》消息的内容                    }                    msgsz ----》消息的长度,大小                    msgflg ----》设置为0    除开以上三种宏定义之外的----阻塞读写

linux可不可以创建多个进程

(3)消息队列的删除 msgctl()

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

linux可不可以创建多个进程

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];//获取msgidmsgid = 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:%sn",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;//获取msgidmsgid = 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:%sn",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 = %sn",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;}

2.1.6 信号量

信号量协调不同进程对于共享资源的访问,它是不同进程间或一个给定进程内部不同线程间同步的机制。

linux可不可以创建多个进程

1、信号量概述
(1)二值信号量
值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0
(2)计数信号量
值在0到n之间。用来统计资源,其值代表可用资源数
(3)对信号量的操作
P操作:即申请资源,亦即将信号量值减1,可能引起进程睡眠。
V操作:即释放资源,亦即将信号量值加1,V操作从不会睡眠。
等0操作:不申请也不释放资源,而是令进程阻塞直到信号量的值为0为止

2、信号量相关的接口函数
(1) 创建信号量集合semget()

#include int semget(key_t key, int nsems, int semflg);    参数:key ----》键值          nsems----》你创建的信号量集中信号量的个数          semflg----》 IPC_CREAT|0666组合返回值:成功—信号量ID出错—-1

(2)设置/删除信号量集 semctl()

   #include       int semctl(int semid, int semnum, int cmd, ...);  返回值:成功—0 失败—-1

linux可不可以创建多个进程

(3)信号量的PV操作 semop()
核心:信号量为 <=0 时进行p操作,会阻塞程序,直到另一进程中是该信号进行了v操作后,本程序才会继续运行——》key值相同,信号量共通
p 减一操作
v 加一操作

  #include   int semop(int semid, struct sembuf *sops, size_t nsops);  返回值:成功—0   出错—-1  参数:semid ----》semget的返回值        nsops ---》要操作的信号量的个数(结构体的个数)  sops---》信号量操作结构体struct sembuf {short  sem_num;=>> 要操作的信号量的编号(数组下标)short  sem_op;   =>> 0 :  等待,直到信号量的值变成0                            1 :  释放资源,V操作                           -1 :  分配资源,P操作                    short  sem_flg;   =>> 0/IPC_NOWAIT/SEM_UNDOSEM_UNDO: 程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值;IPC_NOWAIT: 对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息;};

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:%sn",p);printf("please input a message!n");scanf("%s",p);printf("message is %sn",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);  //第一个信号量初值为1printf("sem num is:%dn",semctl(semid,0,GETVAL));semctl(semid,1,SETVAL,0);  //第二个信号量初值为0printf("sem num is:%dn",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  // 执行完这句话以后信号量的值就立马变成1printf("the message I recv is:%sn",p); printf("please input a message!n");scanf("%s",p);printf("message is %sn",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可不可以创建多个进程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/124837.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Linux环境下OpenSSL如何实现SSL/TLS握手
上一篇 2025年11月27日 03:13:06
yandex入口引擎俄罗斯无需登录 俄罗斯搜索引擎无需登录官网
下一篇 2025年11月27日 03:13:56

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • php常量怎么用_PHP常量(define/const)定义与使用方法

    PHP中可通过define函数和const关键字定义常量,用于存储不可变值。define适用于全局作用域,支持动态名称和条件定义,如define(‘SITE_NAME’, ‘MyWebsite’);const在编译时生效,语法简洁但限制多,只能在类或全…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    100
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • Debian Copilot的社区活跃度如何

    debian copilot是codeberg社区维护的ai助手,旨在为debian用户提供服务。尽管搜索结果中没有直接提供关于debian copilot社区支持活跃度的具体数据,但我们可以通过debian社区的整体活跃度和特点来推断其活跃性。 Debian社区的一般情况: Debian拥有详尽的…

    2026年5月10日
    000
  • JavaScript 闭包:理解闭包原理与内存泄漏问题

    闭包是函数访问其外部作用域变量的能力,即使外部函数已执行完毕。如 inner 函数引用 outer 中的 count,形成闭包,使变量持久存在。闭包本身无害,但可能因延长变量生命周期导致内存泄漏,例如事件监听器引用大对象时。若未及时清理 DOM 事件或定时器,闭包会阻止垃圾回收,造成内存占用过高。解…

    2026年5月10日
    100
  • JavaScript 动态菜单点击高亮效果实现教程

    本教程详细介绍了如何使用 JavaScript 实现动态菜单的点击高亮功能。通过事件委托和状态管理,当用户点击菜单项时,被点击项会高亮显示(绿色),同时其他菜单项恢复默认样式(白色)。这种方法避免了不必要的DOM操作,提高了性能和代码可维护性,确保了无论点击方向如何,功能都能稳定运行。 动态菜单高亮…

    2026年5月10日
    200
  • c++如何实现UDP通信_c++基于UDP的网络通信示例

    UDP通信基于套接字实现,适用于实时性要求高的场景。1. 流程包括创建套接字、绑定地址(接收方)、发送(sendto)与接收(recvfrom)数据、关闭套接字;2. 服务端监听指定端口,接收客户端消息并回传;3. 客户端发送消息至服务端并接收响应;4. 跨平台需处理Winsock初始化与库链接,编…

    2026年5月10日
    100
  • 谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧谷歌浏览器如何截图 谷歌浏览器页面截图技巧

    使用谷歌浏览器的开发者工具截图步骤:1. 按ctrl+shift+i(windows/linux)或cmd+option+i(mac)打开开发者工具。2. 点击右上角三个点,选择”更多工具”,再选择”截图”。3. 选择截取整个页面。推荐的谷歌浏览器扩展…

    2026年5月10日 用户投稿
    100

发表回复

登录后才能评论
关注微信