1. Create process fork()
1.1 the header file
#include<unistd.h>
#include<sys/types.h>
Copy the code
1.2 Function Prototype
pid_t fork( void);
Copy the code
Pid_t is a macro definition, essentially int is defined in #include
1.3 Returned Value:
The child process returns 0, and the parent process returns the child process ID. Otherwise, an error returns -1
1.4 Note the following points
- There are two ways to create processes in Linux: by the operating system, or by a parent process (usually a child process). The system call to fork() is the only way to create a new process. Of course, vfork() can also create a process, but it actually calls fork() anyway. Fork () is a special function in Linux that returns two values in one call.
- It is important to realize that after a call to fork(), the order in which the parent and child processes execute is uncertain (i.e., the scheduler uses the CPU), because in some poorly designed programs, resource contention can lead to unpredictable problems.
- The result of fork is that it returns 0 twice, executing the following sequence. This is the child process. Return the PID of the child once, and also execute the following code sequentially, which is the parent process.
- After the process is created successfully, both parent and child processes start executing after fork(), with different knowledge pid. The fork statement can be thought of as splitting the program into A and B parts. After fork(), the child gets all of the parent’s variables, environment variables, and the current space and value of the program counter.
- In general, after fork(), the order of parent and child execution is uncertain. Depending on the scheduling algorithm used by the kernel, some form of interprocess communication is required if parent and child processes are required to synchronize with each other.
1.5 vfork () function
Also used to create a process that returns the same value as fork().
Similarities and differences between fork() and vfork()
-
Execution order: fork(): the dispatcher for the parent and child processes is determined by the dispatcher; Vfork (): the child process is called first and the parent process is called after exit(1) of the child process is called.
-
Impact on data segment: fork(): The parent process does not share the address space. If the child process is modified, the content of the parent process is not affected.
Vfork (): It runs in the parent’s space until exit is called by the child, that is, it changes the parent’s data segment, stack, and heap. That is, the code area and data area are shared, and the address and content are the same.
2. Similarities and differences between parent and child processes
1. The similarities
After the child process is created, the child process copies all variables of the parent process, and the child process does not affect the value of variables of the parent process.
2. The difference between
Fork () returns a different value. Different Process ids
3. Code examples
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <vector> #include <iostream> #include<sys/wait.h> using namespace std; Void print_exit() {printf("the exit pid:[%d] \n",getpid()); } int main() { string sMatch; pid_t pid, child_pid; vector<string> provList; provList.push_back("taskFace"); provList.push_back("taskObj"); provList.push_back("taskAction"); provList.push_back("taskHat"); provList.push_back("taskOther"); cout << "Main process,id=" << getpid() << endl; // loop "100,200,300,400,500" for (vector<string>::iterator it = provlist.begin (); it ! = provList.end(); ++it) { sMatch = *it; atexit( print_exit ); pid = fork(); HSQ / / (* *) the child process to exit the loop and create the child no longer, all child created by the main process, here is the key to the if (pid = = 0 | | pid = = 1) {break; } } if(pid == -1) { cout<<"Fail to fork!" <<endl; exit(1); Else if(pid == 0) {cout <<"This is children process,id=" << getpid() <<",start to process "<< sMatch << endl; sleep(10); exit(0); Cout <<" This is main process,id=" << getpid() <<",end to process "<< sMatch << endl; Child_pid = waitPID (pid, NULL, WNOHANG); if(child_pid ! = pid) { printf("---- watpid error! \n"); } printf("I am main progress.The pid progress has not exited! \n"); sleep(2); }while(child_pid == 0); exit(0); } return 0; }Copy the code
The execution result
4. Where does the parent process and child process start execution
#include <unistd.h> #include <sys/types.h> main () { pid_t pid; printf("hello! \n"); pid=fork(); if (pid < 0) printf("error in fork!" ); else if (pid == 0) printf("i am the child process, my process id is %d\n ",getpid()); else printf("i am the parent process, my process id is %d\n",getpid()); printf("bye! \n"); }Copy the code
Here you can see that the parent Process executes printf(” Hello! \ n “); The Child process does not execute printf(” Hello! \ n “); Here’s a very confusing example:
#include <unistd.h> #include <sys/types.h> main () { pid_t pid; printf("fork!" ); //printf("fork! \n") pid=fork(); if (pid < 0) printf("error in fork! \n"); else if (pid == 0) printf("i am the child process, my process id is %d\n",getpid()); else printf("i am the parent process, my process id is %d\n",getpid()); }Copy the code
Two forks are printed! This leads to the assumption that the child process starts at #include, so printf(” fork! ); Statements. The reason for this problem is that it has to do with the caching mechanism of Printf. When Printf does something, the operating system simply puts the content in the stdout buffer queue, without actually writing it to the screen. However, as soon as we see \n, stdout is flushed immediately, so it is ready to print. The mian function (parent process) runs printf(” fork! ), the “fork!” The AAAAAA child process inherits the cache, so the stdout buffer also has a “fork!” . So, what you end up seeing is “fork!” By printf 2 times!!!! The mian function (parent process) runs printf(” fork! After \ n “), “fork!” Is immediately printed to the screen, and the stdout buffer in the child process forked to later has no “fork!” Content so the result you see is “fork!” Is printf 1 times!!!!
5. Interprocess communication
1. Sample code (using message queues as an example)
#include <stdlib.h> #include<stdio.h> #include<unistd.h> #include <sys/types.h> #include<vector> #include <iostream> #include<sys/wait.h> #include <string.h> #include <sys/ipc.h> #include <sys/msg.h> using namespace std; typedef struct TASK_LIST_ { int taskId; const char *taskInfo; }TASK_LIST; void print_exit() { printf("the exit pid:[%d] \n",getpid() ); } TASK_LIST taskList[5]; #define SIZE 1024 const long id = 1; int main(int argc, char const *argv[]) { key_t unique_key; int msgid; int status; char str[SIZE]; struct msgbuf { long msgtype; char msgtext[SIZE]; }sndmsg, rcvmsg; TASK_LIST sMatch; pid_t pid, child_pid; //int status; vector<TASK_LIST> provList; taskList[0].taskInfo = "1001"; taskList[1].taskInfo = "2002"; taskList[2].taskInfo = "3003"; taskList[3].taskInfo = "4004"; taskList[4].taskInfo = "5005"; for (int i = 0; i < 5; i++) { cout << i << " " << taskList[i].taskInfo << endl; taskList[i].taskId = i; provList.push_back(taskList[i]); } cout<<"main process,id="<<getpid()<<endl; / / / / to create a message queue IPC_CREAT: create a message queue IPC_EXCL: if the existing error msgid = msgget (unique_key, IPC_CREAT | IPC_EXCL); for (int i = 0; i < provList.size(); i++) { atexit( print_exit ); pid = fork(); HSQ / / (* *) the child process to exit the loop and create the child no longer, all child created by the main process, here is the key to the if (pid = = 0 | | pid = = 1) {break; } } if(pid == -1) { cout<<"fail to fork!" <<endl; msgctl(msgid, IPC_RMID, 0); exit(1); } else if(pid == 0) { sleep(5); if((status = msgrcv(msgid, (struct msgbuf *)&rcvmsg, sizeof(str) + 1, id, IPC_NOWAIT)) == -1) { fprintf(stderr, "msgrcv error! \n"); exit(4); } int taskId = atoi(rcvmsg.msgtext); if(taskId == 1) { cout << "start task [" << taskList[1].taskId <<"] :" << taskList[1].taskInfo << endl; } printf("The received message is:%s\n", rcvmsg.msgtext); // msgctl(msgid, IPC_RMID, 0); Cout << "this is children process,id=" << getpid() << endl; sleep(2); exit(0); } else { cout << "this is main process,pid=" << getpid() << endl; sleep(3); for (int i = 0; i < 5; ++i) { sndmsg.msgtype = id; char id[4]; sprintf(id, "%d", i); strcpy(str, id); sprintf(sndmsg.msgtext, str); if(msgsnd(msgid, (struct msgbuf *)&sndmsg, sizeof(str) + 1, 0) == -1) { fprintf(stderr, "msgsnd error! \n"); exit(2); Child_pid = waitpid(pid, NULL, WNOHANG); child_pid = wait (pid, NULL, WNOHANG); // if(child_pid ! = pid) // { // printf("---- watpid error! \n"); // } // printf("I am main progress.The pid progress has not exited! \n"); // sleep(2); // }while(child_pid == 0); // exit(0); } return 0; }Copy the code
2. Interface analysis
1.1 msgget function
This function is used to create and access a message queue
int msgget(key_t, key, int msgflg);
As with other IPC mechanisms, a program must provide a key to name a particular message queue. MSGFLG is a permission flag that indicates access to the message queue, which is the same as access to files. MSGFLG can do or with IPC_CREAT to create a message queue if the message queue named by key does not exist. If the message queue named by key exists, the IPC_CREAT flag is ignored and only an identifier is returned, which returns the identifier (non-zero integer) of the message queue named by key. Returns -1 on failure.
1.2 MSGSND function
This function is used to add messages to the message queue.
int msgsend(int msgid, const void *msg_ptr, size_t msg_sz, int msgflg);
Copy the code
Msgid is the message queue identifier returned by the msgget function. Msg_ptr is a pointer to a message that is about to be sent, but the data structure of the message has certain requirements. The message structure that the pointer to msG_ptr points to must be a structure that begins with a long integer member variable, which the receiving function will use to determine the type of the message. So the message structure is defined like this:
struct my_message
{
long int message_type;
/* The data you wish to transfer*/
};
Copy the code
Msg_sz is the length of the message that MSG_ptr points to, not the length of the entire structure, i.e. msg_sz is the length of the member variables of the message type excluding long integers.
MSGFLG is used to control what happens when the current message queue is full or when queue messages reach a system-wide limit.
If the call is successful, a copy of the message data is put into the message queue and returns 0, or -1 on failure.
1.3 MSGRCV function
Int MSGRCV (int MSGID, void * MSG_ptr, size_t MSG_st, long int MSgType, int MSGFLG);
Msgid, msg_ptr, msg_st also function as MSGSND.
Msgtype implements a simple receive priority. If msgType is 0, the first message in the queue is retrieved. If its value is greater than zero, the first message of the same message type is retrieved. If it is less than zero, the first message whose type is equal to or less than the absolute value of MSgType is retrieved.
MSGFLG is used to control what happens when there are no messages of the corresponding type in the queue to receive.
On success, the function returns the number of bytes put into the receive cache, the message is copied to the user-allocated cache pointed to by MSG_ptr, and the corresponding message is deleted from the message queue. Returns -1 on failure.
1.4 MSGCTL function
Int MSGCTL (int msgid, int command, struct msgid_ds *buf); int msgid (int command, struct msgid_ds *buf);
IPC_STAT: Sets the msGID_ds structure to the current associated value of the message queue. That is, the msGID_ds value is overwritten by the current associated value of the message queue. IPC_SET: if the process has sufficient permissions, the current associated value of the message queue is set to the value given in the MSGID_DS structure IPC_RMID: deletes the message queue
Buf is a pointer to the MSGID_DS structure, which points to the message queue schema and access permission structure. The MSgid_ds structure contains at least the following members:
struct msgid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
Copy the code
Returns 0 on success and -1 on failure.
The above content is the author in the study, referring to other people’s experience, as well as “Unix advanced environment programming” and other books summed up, hoping to learn this part of the beginners have help. Welcome to exchange and learn together!