Linux can create multiple processes. Linux can handle multiple tasks at the same time and support multiple processes to maximize the utilization of system resources. Communication methods between Linux processes: 1. Use unnamed pipes; 2. Use named pipes (FIFO); 3. Use signals single; 4. Use shared memory; 5. Use message queues; 6. Use semaphores.
Linux can create multiple processes.
linux supports multiple processes. One of the benefits of a multi-process system is that it can handle multiple tasks at the same time to achieve maximum utilization of system resources.
Chapter 1 Introduction to Linux Multi-Process
1.1 Overview
1.1.1 Process Concept
In Linux, a running program is called a process.
Program: Static concept, it is a compiled binary file
Process: Dynamic concept, when the program is running, the system will automatically run a corresponding Process
The process contains process control block (PCB), code segment, and data segment three parts
Process control block: In Linux, it is represented by a structure, which records the process Status information
Zombie process: The parent process exits before the child process
If you create a child process, but the resources of the child process are not recycled in the parent process, then the child process will It becomes a zombie process, and the zombie process will eventually be recycled by a process called INIT in the system.
The init process (process No. 1) is the first process that runs when the system starts and is the ancestor process of all processes.
1.1.2 Process view shell command
top View dynamic process information
ps -ef View the detailed information of the process
pstree Display the process information in the form of a tree
bg Put the suspended process to run in the background
1.2 Process running status
1.2.1 Running state
Execution state (RUNNING): The process is occupying the CPU.
Ready state (RUNNING): The process is in the waiting queue waiting for scheduling.
Light sleep (INTRUPTABLE): At this time, the process is waiting for an event to occur or some system resource to respond to a signal.
Deep sleep (UNINTERRUPTABLE): At this time, the process is waiting for the occurrence of an event or some system resource and cannot respond to the signal.
Stop state (STOPPED): The process is suspended at this time.
Zombie state (ZOMBIE): The process cannot be scheduled at this time, but the PCB has not been released.
DEAD: This is a terminated process and the PCB will be released
1.2.2 User mode/Kernel mode
Kernel mode: Also called Kernel space, it is the area where the kernel process/thread is located. Mainly responsible for operating system and hardware interaction.
User mode: Also called User space, it is the area where user processes/threads are located. Mainly used to execute user programs.
1. Difference
Kernel state: The running code is not subject to any restrictions, and the CPU can execute any instructions.
User mode: Cannot schedule the CPU and cannot directly access the hardware. The running code needs to be subject to many checks by the CPU and cannot directly access kernel data and programs, that is, it cannot access any valid address like a kernel-mode thread.
When the operating system executes user programs, it mainly works in the user mode. It only switches to the kernel mode when it performs tasks that it does not have permission to complete.
2. Reasons for distinguishing user mode and kernel mode
Protection mechanism to prevent user processes from misoperation or malicious damage to the system
Ensure centralized management of resources and reduce resource usage conflicts.
3. Switch from user mode to kernel mode
(1) System call (active)
System call ( System call) is an interface provided by the operating system to the user process to request the operating system to perform some privileged operations, that is, a window that provides services to the user process. Under Linux, you can use the man syscalls command to view all system call API interfaces provided by Linux.
Since the user state cannot complete certain tasks, the user state will request to switch to the kernel state, and the kernel state completes the switch through interrupts specially opened for users.
(2) Peripheral device interrupt (passive)
The peripheral device sends an interrupt signal. When the interrupt occurs, the currently running process is suspended, and the operating system kernel responds to the interrupt process Processing, if the CPU was executing a user-mode program before the interrupt, it is equivalent to switching from user mode to kernel mode.
Interrupts are used to ensure that CPU control is handed over to the operating system so that the operating system can perform certain operations.
(3) Exception (passive)
When some unknown exception occurs when executing the user program, it will switch from the user program to the program in the kernel that handles the exception, that is, switch to Kernel state.
1.3 Process interface function
1.3.1 Process creation fork/vfork
1, fork(), vfork()
(1 ) The newly created process is called a child process. It copies all the resources of the parent process (only copied once at the time of creation. The global variable values will be different after ). It is uncertain which of the parent and child processes comes first. .
#include <unistd.h> pid_t fork(void); 返回值: > 0表示处于父进程中 这个时候的返回值就是**子进程进程id号** ==0 表示处于子进程中 < 0 创建进程出错 #include <sys/types.h> #include <unistd.h> pid_t vfork(void);
(2) **vfork()** The child process shares all the resources of the parent process. It must be that the child process runs first , and then the parent process runs (even if you Adding sleep() to artificially interfere is useless)
(3) Note
The result of using exit() in the child process is completely different from that of not using it
Whether to use sleep() in the parent-child process to Giving up the cpu time slice is also different
Whether wait() is used in the parent process, the waitpid() result is also different
(4) Process switching execution
1.3.2 Process exit exit/_exit
1, exit(), _exit()
#include <stdlib.h> void exit(int status); void _exit(int status); 参数: status --->进程退出时的状态 status在实际编写程序中是可以自己约定的: 比如: exit(2)----》出现打开文件错误 exit(3)----》出现段错误(逻辑错误) exit(0)----》正常退出 返回: void 区别: exit()在退出的时候会刷新IO缓冲区,然后才退出(负责任的退出) _exit() 直接退出(不负责任的退出)
1.3 .3 Wait for the child process to exit (the parent process recycles resources) wait/waitpid
1, wait()
#include <sys/wait.h> pid_t wait(int *stat_loc); 返回值:你回收的那个子进程的id号 参数:stat_loc --》保存子进程退出时的状态信息(不仅仅只是返回值) stat_loc里面不仅仅只是保存了exit退出时的数值,它还保存了子进程退出时是哪个信号让它退出的, 出错了是什么原因导致的。
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.3.4 Get the id of the process-getpid
(1)获取自己的id getpid() #include <unistd.h> pid_t getpid(void); 返回值:就是该进程的id号 (2) 获取父进程id getppid() #include <unistd.h> pid_t getppid(void); 返回值:就是父进程的id号
Chapter 2 Linux multi-process communication method
Whether it is inter-process communication or thread communication between. This sentence can be rewritten as: Solving the allocation problem of shared resources is its fundamental purpose, which is to coordinate the access of multiple processes/threads to shared resources
2.1 Inter-process communication methods
1. Traditional inter-process communication method
Unnamed pipe
Named pipe
-
Signal
2, System V IPC object
Shared memory
Message queue
Semaphore
3、BSD
Network socket (socket)
2.1.1 Unnamed pipe pipe
1. Features: The most primitive communication method between processes
It can only be used between processes with affinity. Communication (father-child process, sibling process);
It has no name (it exists);
can be created in the share between linux and windows (the pipe file will not be generated at all), but it has a name Pipes are not available (generate pipeline files);
Half-duplex communication.
2. Use of unnamed pipes
(1) Create pipe()
#include <unistd.h> int pipe(int fildes[2]); 参数:fildes[2]里面放的是两个文件描述符fildes[0],fildes[1] fildes[0] 读端 fildes[1] 写端 返回值:成功返回0 失败返回-1
(2) Sending and receiving pipe information
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]); }
2.1.2 Named pipe (FIFO)
1. Features: Can be used between any two processes
cannot be created in a share between linux and windows;
Ensure the atomicity of writing (atomicity: either don’t do it, or do it in one go without interference from the outside world);
Famous pipesNo Can overwrite the creation (generally use the access() function in code to determine whether it exists. If a pipe with the same name already exists, it cannot be created again);
Remember to close it after use ;
When the pipe is opened in read-only mode, it will block until another process opens the pipe in write-only mode. , then it will not be blocked; if it is opened in a read-write mode, it will not be blocked.
Full-duplex communication, half-duplex.
2. Use of famous pipes
(1) Create mkfifo()
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数:pathname 有名管道的路径名 mode:权限 0666 返回值:0 成功 -1 失败
(2) FIFO process information sending and receiving
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; }
2.1 .3 Signal single
When a program (process) is running, the outside world will send signals to the program from time to time. At this time, the program faces two choices:
Ignore it (block/ignore)
Blocking: refers to suspending the signal and waiting until the program is finished running before responding
Ignore: discard this signal
Respond to it
1. What signals are there in Linux: kill -l to view
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) Signals 1 to 31 are called non-real-time signals: queues are not supported (if multiple signals come at the same time, the response will be irregular)
(2) User-defined signals 10 ) SIGUSR1 12) SIGUSR2
(3) Signals No. 34 to 64 are called real-time signals: they support queues and are signals added later in the Linux system.
The signals are similar to interrupts: Hardware and software
Above signals There are two special ones: SIGKILL and SIGSTOP cannot be ignored or blocked
2. Signal-related operation functions
(1) Send signal kill()
#include <signal.h> int kill(pid_t pid, int sig); 参数: pid ----》进程的id 正数:要接收信号的进程的进程号 0:信号被发送到所有和pid进程在同一个进程组的进程 -1:信号发给所有的进程表中的进程(除了进程号最大的进程外) sig ----》信号名字 返回值:0 成功 -1 出错
(2) Signal capture signal()
#include <signal.h> void (*signal(int sig, void (*func)(int)))(int); // SIGKILL 参数:sig ----》你需要捕捉的那个信号 void (*func)(int) ----》函数指针,回调函数,捕捉到对应的信号的时候就调用该函数;第二个参数除了可以传递一个函数指针意外,还可以使用以下两个宏定义: SIG_IGN ---->你捕捉到的那个信号会被忽略 SIG_DFL-----》你捕捉的信号会采用系统默认的方式响应 返回值:成功:设置之前的信号处理方式 出错:-1
(3) Waiting for signal pause()
#include <unistd.h> int pause(void); 返回值:-1 把error值设为EINTR 0 成功
(4)信号的阻塞
每个进程都有属于它自己的一个信号掩码(也就是该进程在运行的过程中会阻塞掉的那些信号就被称作信号掩码)。
关于信号掩码操作的一系列函数:
#include <signal.h> 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 <signal.h> 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 <signal.h> 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()思想上有些类似
在进程通信方式中共享内存是效率最高的,进程可以直接读写内存,而不需要任何数据的拷贝;
如果代码不人为地删除共享共享内存,那么程序退出的时候它还在;
多个进程共享一段内存,因此也需要依靠某种同步机制,如互斥锁和信号量等
2、共享内存对应的一系列操作函数
(1)创建共享内存:shmget()
#include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg); 返回值:成功—共享内存对象的mid(标识符) 出错—-1 参数:key----》创建共享内存需要用到的键值 size----》内存空间的大小(字节) shmflg----》设置属性 IPC_CREAT IPC_EXCL 0666组合 key键值的获取有两种方法: **方法一**:使用ftok()生成键值 #include <sys/types.h> #include <sys/ipc.h> 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 <sys/shm.h> 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 <sys/shm.h> int shmdt(const void *shmaddr); 参数:shmaddr----》 shmat()共享内存映射后的地址 返回值:成功—0 出错—-1
(4)删除共享内存:shmctl()
#include <sys/shm.h> 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
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; }
2.1.5消息队列
消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等。
消息队列由消息队列ID来唯一标识
消息队列可以按照类型来发送/接收消息
消息队列的操作包括创建或打开消息队列、添加消息、读取消息和控制消息队列
1、消息队列的特点
写入消息队列的信息,在编写程序的时候会人为的去设置消息的类型(用整数来表示),目的是为了其它进程在读取信息的时候能够准确地通过消息的类型判断要读取的信息。
2、消息队列操作的系列函数
(1)消息队列的创建 msgget()
#include <sys/msg.h> int msgget(key_t key, int msgflg);
(2)消息队列的收发信息msgsnd()msgrcv()
#include <sys/msg.h> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 参数:void *msgp ----》你要发送信息就存储在这个指针中 在实际的编程中我们都是定义一个结构体来存储信息 struct msgbuf { long mtype; ----》消息的类型 char mtext[100]; ----》消息的内容 } msgsz ----》消息的长度,大小 msgflg ----》设置为0 除开以上三种宏定义之外的----阻塞读写
(3)消息队列的删除 msgctl()
#include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf);
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; }
2.1.6 信号量
信号量协调不同进程对于共享资源的访问,它是不同进程间或一个给定进程内部不同线程间同步的机制。
1、信号量概述
(1)二值信号量
值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0
(2)计数信号量
值在0到n之间。用来统计资源,其值代表可用资源数
(3)对信号量的操作
P操作:即申请资源,亦即将信号量值减1,可能引起进程睡眠。
V操作:即释放资源,亦即将信号量值加1,V操作从不会睡眠。
等0操作:不申请也不释放资源,而是令进程阻塞直到信号量的值为0为止
2、信号量相关的接口函数
(1) 创建信号量集合semget()
#include <sys/sem.h> int semget(key_t key, int nsems, int semflg); 参数:key ----》键值 nsems----》你创建的信号量集中信号量的个数 semflg----》 IPC_CREAT|0666组合 返回值:成功—信号量ID 出错—-1
(2)设置/删除信号量集 semctl()
#include <sys/sem.h> int semctl(int semid, int semnum, int cmd, ...); 返回值:成功—0 失败—-1
(3)信号量的PV操作 semop()
核心:信号量为 p 减一操作
v 加一操作
#include <sys/sem.h> 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_UNDO SEM_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:%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 Delete shared memory
##ipcs -s View semaphore
ipcrm -s SEM_ID Delete semaphore
2.2 Comparison of inter-process communication methods
- pipe: Inter-process with affinity, simplex, data in memory
- fifo: Can be used for any inter-process, duplex, with file name, Data in memory
- signal: The only asynchronous communication method
- msg: often used in cs mode, accessed by message type, priority can be given Level
- #shm: the most efficient (direct access to memory), requires synchronization and mutual exclusion mechanism
- #sem: used in conjunction with shared memory, for Implement synchronization and mutual exclusion
The above is the detailed content of Can linux create multiple processes?. For more information, please follow other related articles on the PHP Chinese website!

linux设备节点是应用程序和设备驱动程序沟通的一个桥梁;设备节点被创建在“/dev”,是连接内核与用户层的枢纽,相当于硬盘的inode一样的东西,记录了硬件设备的位置和信息。设备节点使用户可以与内核进行硬件的沟通,读写设备以及其他的操作。

区别:1、open是UNIX系统调用函数,而fopen是ANSIC标准中的C语言库函数;2、open的移植性没fopen好;3、fopen只能操纵普通正规文件,而open可以操作普通文件、网络套接字等;4、open无缓冲,fopen有缓冲。

端口映射又称端口转发,是指将外部主机的IP地址的端口映射到Intranet中的一台计算机,当用户访问外网IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上;可以通过使用动态或固定的公共网络IP路由ADSL宽带路由器来实现。

在linux中,交叉编译是指在一个平台上生成另一个平台上的可执行代码,即编译源代码的平台和执行源代码编译后程序的平台是两个不同的平台。使用交叉编译的原因:1、目标系统没有能力在其上进行本地编译;2、有能力进行源代码编译的平台与目标平台不同。

在linux中,eof是自定义终止符,是“END Of File”的缩写;因为是自定义的终止符,所以eof就不是固定的,可以随意的设置别名,linux中按“ctrl+d”就代表eof,eof一般会配合cat命令用于多行文本输出,指文件末尾。

在linux中,可以利用“rpm -qa pcre”命令判断pcre是否安装;rpm命令专门用于管理各项套件,使用该命令后,若结果中出现pcre的版本信息,则表示pcre已经安装,若没有出现版本信息,则表示没有安装pcre。

linux查询mac地址的方法:1、打开系统,在桌面中点击鼠标右键,选择“打开终端”;2、在终端中,执行“ifconfig”命令,查看输出结果,在输出信息第四行中紧跟“ether”单词后的字符串就是mac地址。

在linux中,rpc是远程过程调用的意思,是Reomote Procedure Call的缩写,特指一种隐藏了过程调用时实际通信细节的IPC方法;linux中通过RPC可以充分利用非共享内存的多处理器环境,提高系统资源的利用率。


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

AI Hentai Generator
Generate AI Hentai for free.

Hot Article

Hot Tools

DVWA
Damn Vulnerable Web App (DVWA) is a PHP/MySQL web application that is very vulnerable. Its main goals are to be an aid for security professionals to test their skills and tools in a legal environment, to help web developers better understand the process of securing web applications, and to help teachers/students teach/learn in a classroom environment Web application security. The goal of DVWA is to practice some of the most common web vulnerabilities through a simple and straightforward interface, with varying degrees of difficulty. Please note that this software

SublimeText3 Mac version
God-level code editing software (SublimeText3)

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

Safe Exam Browser
Safe Exam Browser is a secure browser environment for taking online exams securely. This software turns any computer into a secure workstation. It controls access to any utility and prevents students from using unauthorized resources.

Zend Studio 13.0.1
Powerful PHP integrated development environment
