If IPC_CREAT is used alone, msgget () either returns the identifier of a newly created message queue or the identifier of a queue with the same key value. If IPC_EXCL and IPC_CREAT are used together, msgget () either creates a new message queue or returns a failure value of -1 if the queue already exists. IPC_EXCL is useless by itself.
Let's look at an example of opening and creating a message queue:
intopen_queue (key_t keyval)
{
intqid;
if ((qid = msgget (keyval, IPC_CREAT | 0660)) ==-1)
{
return (-1);
}
return (qid);
}
[directory]
-------------- -------------------------------------------------- ----------------
msgsnd ()
system call msgsnd ()
Once we get the queue identifier, we can perform the operation we want on the queue. If you want to send a message to the queue, you can use the system call msgsnd ():
system call: msgsnd ();
prototype: intmsgsnd (int msqid, struct msgbuf * msgp, int msgsz, int msgflg);
return value: if successful , 0.
If it fails, -1: errno = EAGAIN (the queue is full and IPC_NOWAIT is used)
EACCES (no write permission)
EFAULT (invalid msgp address)
EIDRM (the message queue has been deleted)
EINTR (when waiting for a write operation, a Signal)
EINVAL (invalid message queue identifier, non-positive message type, or
invalid message length)
ENOMEM (not enough memory to copy message buffer)
The first parameter of the system call msgsnd () is the message queue identifier, which is returned by the system call msgget. The second parameter is msgp, which is a pointer to the message buffer. The parameter msgsz contains the size of the message in bytes, but does not include the length of the message type (4 bytes).
The parameter msgflg can be set to 0 (this parameter is ignored at this time), or use IPC_NOWAIT.
If the message queue is full, the message will not be written to the message queue and control will return to the calling process. If not specified, the calling process will hang until the message can be written to the queue.
The following is a program to send messages:
intsend_message (int qid, struct mymsgbuf * qbuf)
{
intresult, length;
/ * The length is essentially the size of the structure minus sizeof (mtype) * /
length = sizeof (structmymsgbuf) -sizeof ( long);
if ((result = msgsnd (qid, qbuf, length, 0)) ==-1)
{
return (-1);
}
return (result);
}
This applet tries to store the buffer in qbuf The message is sent to the message queue qid. The following program is a complete program combining the above two programs:
#include <stdio.h>
#include <stdlib.h>
#include <linux / ipc.h>
#include <linux / msg.h>
main ()
{
intqid;
key_t msgkey;
struct mymsgbuf {
longmtype; / * Message type * /
intrequest; / * Work request number * /
doublesalary; / * Employee's salary * /
} msg;
/ * Generateour IPC key value * /
msgkey = ftok (".", 'M');
/ * Open / createthequeue * /
if ((qid = open_queue (msgkey)) ==-1) {
perror ("open_queue");
exit (1);
}
/ * Load up the message with ar bitrary test data * /
msg.mtype = 1; / * Messagetypemustbeapositivenumber! * /
msg.request = 1; / * Dataelement # 1 * /
msg.salary = 1000.00; / * Data element # 2 (my yearly salary!) * /
/ * Bombsaway! * /
If ((send_message (qid, &msg)) ==-1) {
perror ("send_message");
exit (1);
}
}
After creating and opening the message queue, we load the test data into the message buffer. Finally, send_messag is called to send the message to the message queue. Now that there is a message in the message queue, we can use the ipcs command to view the status of the queue. The following discusses how to get messages from the queue. You can use the system call msgrcv ():
[directory]
--------------------------------------- -----------------------------------------
msgrcv ()
Let's look at an example of opening and creating a message queue:
intopen_queue (key_t keyval)
{
intqid;
if ((qid = msgget (keyval, IPC_CREAT | 0660)) ==-1)
{
return (-1);
}
return (qid);
}
[directory]
-------------- -------------------------------------------------- ----------------
msgsnd ()
system call msgsnd ()
Once we get the queue identifier, we can perform the operation we want on the queue. If you want to send a message to the queue, you can use the system call msgsnd ():
system call: msgsnd ();
prototype: intmsgsnd (int msqid, struct msgbuf * msgp, int msgsz, int msgflg);
return value: if successful , 0.
If it fails, -1: errno = EAGAIN (the queue is full and IPC_NOWAIT is used)
EACCES (no write permission)
EFAULT (invalid msgp address)
EIDRM (the message queue has been deleted)
EINTR (when waiting for a write operation, a Signal)
EINVAL (invalid message queue identifier, non-positive message type, or
invalid message length)
ENOMEM (not enough memory to copy message buffer)
The first parameter of the system call msgsnd () is the message queue identifier, which is returned by the system call msgget. The second parameter is msgp, which is a pointer to the message buffer. The parameter msgsz contains the size of the message in bytes, but does not include the length of the message type (4 bytes).
The parameter msgflg can be set to 0 (this parameter is ignored at this time), or use IPC_NOWAIT.
If the message queue is full, the message will not be written to the message queue and control will return to the calling process. If not specified, the calling process will hang until the message can be written to the queue.
The following is a program to send messages:
intsend_message (int qid, struct mymsgbuf * qbuf)
{
intresult, length;
/ * The length is essentially the size of the structure minus sizeof (mtype) * /
length = sizeof (structmymsgbuf) -sizeof ( long);
if ((result = msgsnd (qid, qbuf, length, 0)) ==-1)
{
return (-1);
}
return (result);
}
This applet tries to store the buffer in qbuf The message is sent to the message queue qid. The following program is a complete program combining the above two programs:
#include <stdio.h>
#include <stdlib.h>
#include <linux / ipc.h>
#include <linux / msg.h>
main ()
{
intqid;
key_t msgkey;
struct mymsgbuf {
longmtype; / * Message type * /
intrequest; / * Work request number * /
doublesalary; / * Employee's salary * /
} msg;
/ * Generateour IPC key value * /
msgkey = ftok (".", 'M');
/ * Open / createthequeue * /
if ((qid = open_queue (msgkey)) ==-1) {
perror ("open_queue");
exit (1);
}
/ * Load up the message with ar bitrary test data * /
msg.mtype = 1; / * Messagetypemustbeapositivenumber! * /
msg.request = 1; / * Dataelement # 1 * /
msg.salary = 1000.00; / * Data element # 2 (my yearly salary!) * /
/ * Bombsaway! * /
If ((send_message (qid, &msg)) ==-1) {
perror ("send_message");
exit (1);
}
}
After creating and opening the message queue, we load the test data into the message buffer. Finally, send_messag is called to send the message to the message queue. Now that there is a message in the message queue, we can use the ipcs command to view the status of the queue. The following discusses how to get messages from the queue. You can use the system call msgrcv ():
[directory]
--------------------------------------- -----------------------------------------
msgrcv ()
system call: msgrcv () ;
Prototype: intmsgrcv (intmsqid, structmsgbuf * msgp, intmsgsz, longmtype, intmsgflg);
Return value: if successful, returns the number of bytes copied to the message buffer.
If it fails, return -1: errno = E2BIG (the length of the message is greater than msgsz, no MSG_NOERROR)
EACCES (no read permission)
EFAULT (the address pointed to by msgp is invalid)
EIDRM (the queue has been deleted)
EINTR (interrupted by the signal )
EINVAL (msgqid is invalid, or msgsz is less than 0)
ENOMSG (using IPC_NOWAIT, and the messages in the queue cannot meet the requirements)
Obviously, the first parameter is used to specify the queue that will read the message. The second parameter represents the address of the message buffer where the message is to be stored. The third parameter is the length of the message buffer, excluding the length of mtype, which can be calculated as follows:
msgsz = sizeof (structmymsgbuf) -sizeof (long); the
fourth parameter is to be read from the message queue The type of message. If the value of this parameter is 0, the longest message in the queue will be returned, regardless of its type.
If IPC_NOWAIT is used as a flag in the call, then when no data is available, the call will return ENOMSG to the calling process. Otherwise, the calling process will hang until a message in the queue meets the msgrcv () parameter requirements. If the queue is empty while the client is waiting for a message, EIDRM will be returned. If the process catches a signal while waiting for a message, it returns EINTR.
The following is a program to read messages from the queue:
intread_message (int qid, long type, struct mymsgbuf * qbuf)
{
intresult, length;
/ * The length is essentially the size of the structure minus sizeof (mtype) * /
length = sizeof (structmymsgbuf) -sizeof (long);
if ((result = msgrcv (qid, qbuf, length, type, 0)) ==-1)
{
return (-1);
}
return (result);
}
After successfully reading a message, the entry for this message in the queue will be deleted.
The MSG_NOERROR bit in the parameter msgflg provides an additional purpose. If the actual length of the message is greater than msgsz and MSG_NOERROR is used at the same time, the message will be truncated and only messages equal to the length of msgsz will be returned. Under normal circumstances, the system call msgrcv () will return -1, and this message will continue to be stored in the queue. We can use this feature to compile a program, use this program to view the status of the message queue, and see if the message that meets our conditions has arrived:
intpeek_message (int qid, long type)
{
intresult, length;
if ((result = msgrcv ( qid, NULL, 0, type, IPC_NOWAIT)) ==-1)
{
if (errno == E2BIG)
return (TRUE);
}
return (FALSE);
}
In the above program, we ignored the buffer address and length. In this way, the system call will fail. Nonetheless, we can check the returned E2BIG value, which shows that the qualified message does exist.
[Directory]
----------------------------------------------- ---------------------------------
msgctl ()
system call msgctl ()
Below we continue to discuss how to use a given Internal data structure of the message queue. We can use the system call msgctl () to control the operation of the message queue.
Prototype: intmsgrcv (intmsqid, structmsgbuf * msgp, intmsgsz, longmtype, intmsgflg);
Return value: if successful, returns the number of bytes copied to the message buffer.
If it fails, return -1: errno = E2BIG (the length of the message is greater than msgsz, no MSG_NOERROR)
EACCES (no read permission)
EFAULT (the address pointed to by msgp is invalid)
EIDRM (the queue has been deleted)
EINTR (interrupted by the signal )
EINVAL (msgqid is invalid, or msgsz is less than 0)
ENOMSG (using IPC_NOWAIT, and the messages in the queue cannot meet the requirements)
Obviously, the first parameter is used to specify the queue that will read the message. The second parameter represents the address of the message buffer where the message is to be stored. The third parameter is the length of the message buffer, excluding the length of mtype, which can be calculated as follows:
msgsz = sizeof (structmymsgbuf) -sizeof (long); the
fourth parameter is to be read from the message queue The type of message. If the value of this parameter is 0, the longest message in the queue will be returned, regardless of its type.
If IPC_NOWAIT is used as a flag in the call, then when no data is available, the call will return ENOMSG to the calling process. Otherwise, the calling process will hang until a message in the queue meets the msgrcv () parameter requirements. If the queue is empty while the client is waiting for a message, EIDRM will be returned. If the process catches a signal while waiting for a message, it returns EINTR.
The following is a program to read messages from the queue:
intread_message (int qid, long type, struct mymsgbuf * qbuf)
{
intresult, length;
/ * The length is essentially the size of the structure minus sizeof (mtype) * /
length = sizeof (structmymsgbuf) -sizeof (long);
if ((result = msgrcv (qid, qbuf, length, type, 0)) ==-1)
{
return (-1);
}
return (result);
}
After successfully reading a message, the entry for this message in the queue will be deleted.
The MSG_NOERROR bit in the parameter msgflg provides an additional purpose. If the actual length of the message is greater than msgsz and MSG_NOERROR is used at the same time, the message will be truncated and only messages equal to the length of msgsz will be returned. Under normal circumstances, the system call msgrcv () will return -1, and this message will continue to be stored in the queue. We can use this feature to compile a program, use this program to view the status of the message queue, and see if the message that meets our conditions has arrived:
intpeek_message (int qid, long type)
{
intresult, length;
if ((result = msgrcv ( qid, NULL, 0, type, IPC_NOWAIT)) ==-1)
{
if (errno == E2BIG)
return (TRUE);
}
return (FALSE);
}
In the above program, we ignored the buffer address and length. In this way, the system call will fail. Nonetheless, we can check the returned E2BIG value, which shows that the qualified message does exist.
[Directory]
----------------------------------------------- ---------------------------------
msgctl ()
system call msgctl ()
Below we continue to discuss how to use a given Internal data structure of the message queue. We can use the system call msgctl () to control the operation of the message queue.
System call: msgctl ();
Call prototype: int msgctl (int msgqid, int cmd, struct msqid_ds * buf);
return value: 0 if successful.
-1, if it fails: errno = EACCES (no read permission and cmd is IPC_STAT)
EFAULT (the address pointed to by buf is invalid)
EIDRM (the queue was deleted during the read)
EINVAL (msgqid is invalid, or msgsz is less than 0)
EPERM (IPC_SET Or the IPC_RMID command is used, but the calling program does not have write permission)
Let's take a look at several commands that can be used:
IPC_STAT
reads the data structure of the message queue msqid_ds and stores it in the address specified by buf.
IPC_SET
sets the value of the ipc_perm element in the data structure of the message queue msqid_ds. This value is taken from the buf parameter.
IPC_RMID
removes the message queue from the system kernel.
We discussed the data structure of the message queue (msqid_ds) earlier. An instance of this data structure is kept in the system kernel for each message queue in the system. By using the IPC_STAT command, we can get a copy of this data structure. The following program is the process of implementing this function:
int get_queue_ds (int qid, struct msgqid_ds * qbuf)
{
if (msgctl (qid, IPC_STAT, qbuf) == -1)
{
return (-1);
}
return (0);
}
If the internal buffer cannot be copied, the calling process will return -1. If the call is successful, it returns 0. The data structure in the message queue should be included in the buffer.
The only element that can be changed in the data structure in the message queue is ipc_perm. It includes the access rights of the queue and information about the creator and owner of the queue. You can change the user id, user group id, and access permissions for the message queue.
The following is a program to modify the queue access mode:
int change_queue_mode (int qid, char * mode)
{
struct msqid_ds tmpbuf;
/ * Retrieve a current copy of the internal data structure * /
get_queue_ds (qid, & tmpbuf);
/ * Change the permissions using an old trick * /
sscanf (mode, "% ho", & tmpbuf.msg_perm.mode);
/ * Update the internal data structure * /
if (msgctl (qid, IPC_SET, & tmpbuf) == -1)
{
return ( -1);
}
return (
}
We call get_queue_ds to read the internal data structure of the queue. Then we call sscanf () to modify the value of the mode member in the data structure msg_perm. But it was not until msgctl () was called that the permission change was actually completed. Here msgctl () uses the IPC_SET command.
Finally, we use the IPC_RMID command in the system call msgctl () to delete the message queue:
int remove_queue (int qid)
{
if (msgctl (qid, IPC_RMID, 0) == -1)
{
return (-1);
}
return (0 );
}
};
[Directory]
------------------------------------------ --------------------------------------
Semaphore
Call prototype: int msgctl (int msgqid, int cmd, struct msqid_ds * buf);
return value: 0 if successful.
-1, if it fails: errno = EACCES (no read permission and cmd is IPC_STAT)
EFAULT (the address pointed to by buf is invalid)
EIDRM (the queue was deleted during the read)
EINVAL (msgqid is invalid, or msgsz is less than 0)
EPERM (IPC_SET Or the IPC_RMID command is used, but the calling program does not have write permission)
Let's take a look at several commands that can be used:
IPC_STAT
reads the data structure of the message queue msqid_ds and stores it in the address specified by buf.
IPC_SET
sets the value of the ipc_perm element in the data structure of the message queue msqid_ds. This value is taken from the buf parameter.
IPC_RMID
removes the message queue from the system kernel.
We discussed the data structure of the message queue (msqid_ds) earlier. An instance of this data structure is kept in the system kernel for each message queue in the system. By using the IPC_STAT command, we can get a copy of this data structure. The following program is the process of implementing this function:
int get_queue_ds (int qid, struct msgqid_ds * qbuf)
{
if (msgctl (qid, IPC_STAT, qbuf) == -1)
{
return (-1);
}
return (0);
}
If the internal buffer cannot be copied, the calling process will return -1. If the call is successful, it returns 0. The data structure in the message queue should be included in the buffer.
The only element that can be changed in the data structure in the message queue is ipc_perm. It includes the access rights of the queue and information about the creator and owner of the queue. You can change the user id, user group id, and access permissions for the message queue.
The following is a program to modify the queue access mode:
int change_queue_mode (int qid, char * mode)
{
struct msqid_ds tmpbuf;
/ * Retrieve a current copy of the internal data structure * /
get_queue_ds (qid, & tmpbuf);
/ * Change the permissions using an old trick * /
sscanf (mode, "% ho", & tmpbuf.msg_perm.mode);
/ * Update the internal data structure * /
if (msgctl (qid, IPC_SET, & tmpbuf) == -1)
{
return ( -1);
}
return (
}
We call get_queue_ds to read the internal data structure of the queue. Then we call sscanf () to modify the value of the mode member in the data structure msg_perm. But it was not until msgctl () was called that the permission change was actually completed. Here msgctl () uses the IPC_SET command.
Finally, we use the IPC_RMID command in the system call msgctl () to delete the message queue:
int remove_queue (int qid)
{
if (msgctl (qid, IPC_RMID, 0) == -1)
{
return (-1);
}
return (0 );
}
};
[Directory]
------------------------------------------ --------------------------------------
Semaphore
Semaphore is one that can be used to control multiple Counter for processes accessing shared resources. It is often used as a locking mechanism to prevent another process from accessing the same resource when one process is accessing the shared resource. The following briefly introduces the data structure involved in the semaphore.
1. The data structure semid_ds in the kernel is the
same as the message queue. The system kernel saves an internal data structure for each semaphore set in the kernel address space. The prototype of the data structure is semid_ds. It is defined as follows in linux / sem.h:
/ * One semid data structure for each set of semaphores in the system. * /
structsemid_ds {
structipc_permsem_perm; / * permissions..seeipc.h * /
time_tsem_otime; / * last semop time * /
time_tsem_ctime; / * last change time * /
structsem * sem_base; / * ptr to first semaphore in array * /
structwait_queue
structwait_queue * eventz;
structsem_undo * undo; / * undo requestson this array * /
ushortsem_nsems; / * no. of semaphores in array * /
};
sem_perm is an instance of the data structure ipc_perm defined in linux / ipc.h. It holds the information about the access rights of the semaphore set and the information about the creator of the semaphore set.
sem_otime The last semop () operation time.
The last time sem_ctime changed this data structure.
sem_base Pointer to the first semaphore in the array.
The number of unfinished requests in the sem_undo array.
sem_nsems The number of semaphores in the semaphore set (array).
2. The data structure sem
in the kernel contains a pointer to the semaphore array in the data structure semid_ds. Each element in this array is a
Data structure sem. It is also defined in linux / sem.h:
/ * One semaphore structure for each semaphore in the system. * /
Structsem {
shortsempid; / * pid of las toperation * /
ushortsemval; / * current value * /
ushortsemncnt; / * num procs awaiting increase in semval * /
ushortsemzcnt; / * num procs awaiting semval = 0 * /
};
sem_pid PID of the last operation (process ID).
The current value of sem_semval semaphore.
sem_semncnt The number of processes waiting for resources.
sem_semzcnt The number of processes waiting for resources to be completely idle.
[Directory]
----------------------------------------------- ---------------------------------
semget ()
We can use the system call semget () to create a new semaphore Set, or access an existing semaphore set:
system call: semget ();
prototype: intsemget (key_t key, int nsems, int semflg);
Return value: If successful, the IPC identifier of the semaphore set is returned. If it fails, it returns -1: errno = EACCESS (no permission)
EEXIST (the semaphore set already exists and cannot be created)
EIDRM (the semaphore set has been deleted)
ENOENT (the semaphore set does not exist, and IPC_CREAT is not used)
ENOMEM (no Enough memory to create a new semaphore set) The first parameter of the
ENOSPC (over limit)
system call semget () is the key value (generally returned by the system call ftok ()). The system kernel compares this value with the key value of other semaphore sets present in the system. The opening and access operations are related to the content in the parameter semflg. IPC_CREAT If the semaphore set does not exist in the system kernel, create a semaphore set. When IPC_EXCL is used together with IPC_CREAT, if the semaphore set already exists, the call fails. If IPC_CREAT is used alone, semget () either returns the identifier of the newly created semaphore set or the identifier of the semaphore with the same key value already existing in the system. If IPC_EXCL and IPC_CREAT are used together, either the identifier of the newly created semaphore set is returned, or -1 is returned. IPC_EXCL is useless alone. The parameter nsems indicates the number of semaphores that should be created in a new semaphore set. The maximum number of semaphores in the semaphore set is defined in linux / sem.h:
1. The data structure semid_ds in the kernel is the
same as the message queue. The system kernel saves an internal data structure for each semaphore set in the kernel address space. The prototype of the data structure is semid_ds. It is defined as follows in linux / sem.h:
/ * One semid data structure for each set of semaphores in the system. * /
structsemid_ds {
structipc_permsem_perm; / * permissions..seeipc.h * /
time_tsem_otime; / * last semop time * /
time_tsem_ctime; / * last change time * /
structsem * sem_base; / * ptr to first semaphore in array * /
structwait_queue
structwait_queue * eventz;
structsem_undo * undo; / * undo requestson this array * /
ushortsem_nsems; / * no. of semaphores in array * /
};
sem_perm is an instance of the data structure ipc_perm defined in linux / ipc.h. It holds the information about the access rights of the semaphore set and the information about the creator of the semaphore set.
sem_otime The last semop () operation time.
The last time sem_ctime changed this data structure.
sem_base Pointer to the first semaphore in the array.
The number of unfinished requests in the sem_undo array.
sem_nsems The number of semaphores in the semaphore set (array).
2. The data structure sem
in the kernel contains a pointer to the semaphore array in the data structure semid_ds. Each element in this array is a
Data structure sem. It is also defined in linux / sem.h:
/ * One semaphore structure for each semaphore in the system. * /
Structsem {
shortsempid; / * pid of las toperation * /
ushortsemval; / * current value * /
ushortsemncnt; / * num procs awaiting increase in semval * /
ushortsemzcnt; / * num procs awaiting semval = 0 * /
};
sem_pid PID of the last operation (process ID).
The current value of sem_semval semaphore.
sem_semncnt The number of processes waiting for resources.
sem_semzcnt The number of processes waiting for resources to be completely idle.
[Directory]
----------------------------------------------- ---------------------------------
semget ()
We can use the system call semget () to create a new semaphore Set, or access an existing semaphore set:
system call: semget ();
prototype: intsemget (key_t key, int nsems, int semflg);
Return value: If successful, the IPC identifier of the semaphore set is returned. If it fails, it returns -1: errno = EACCESS (no permission)
EEXIST (the semaphore set already exists and cannot be created)
EIDRM (the semaphore set has been deleted)
ENOENT (the semaphore set does not exist, and IPC_CREAT is not used)
ENOMEM (no Enough memory to create a new semaphore set) The first parameter of the
ENOSPC (over limit)
system call semget () is the key value (generally returned by the system call ftok ()). The system kernel compares this value with the key value of other semaphore sets present in the system. The opening and access operations are related to the content in the parameter semflg. IPC_CREAT If the semaphore set does not exist in the system kernel, create a semaphore set. When IPC_EXCL is used together with IPC_CREAT, if the semaphore set already exists, the call fails. If IPC_CREAT is used alone, semget () either returns the identifier of the newly created semaphore set or the identifier of the semaphore with the same key value already existing in the system. If IPC_EXCL and IPC_CREAT are used together, either the identifier of the newly created semaphore set is returned, or -1 is returned. IPC_EXCL is useless alone. The parameter nsems indicates the number of semaphores that should be created in a new semaphore set. The maximum number of semaphores in the semaphore set is defined in linux / sem.h:
# defineSEMMSL32 / * <= 512maxnumofsemaphoresperid * /
The following is a program to open and create a semaphore set:
intopen_semaphore_set (key_t keyval, int numsems)
{
intsid;
if (! numsems)
return (-1);
if ((sid = semget (mykey, numsems, IPC_CREAT | 0660)) ==-1)
{
return (-1);
}
return (sid);
}
};
[directory]
---- -------------------------------------------------- --------------------------
semop ()
system call: semop ();
call prototype: int semop (int semid, struct sembuf * sops, unsign ednsops);
Return value: 0, if successful. -1, if it fails: errno = E2BIG (nsops is greater than the maximum number of ops)
EACCESS (insufficient authority)
EAGAIN (IPC_NOWAIT is used, but the operation cannot continue)
EFAULT (the address pointed to by sops is invalid)
EIDRM (the semaphore set has been deleted)
EINTR (received other signals while sleeping)
EINVAL (semaphore set does not exist, or semid is invalid)
ENOMEM (SEM_UNDO is used, but there is not enough memory to create the required data structure)
ERANGE (semaphore value is out of range)
The first parameter is the key value. The second parameter is a pointer to the array to be operated on. The third parameter is the number of operations in the array. The parameter sops points to an array consisting of sembuf. This array is defined in linux / sem.h:
/ * semop systemcall takes an array of these * /
structsembuf {
ushortsem_num; / * semaphore index in array * /
shortsem_op; / * semaphore operation * /
shortsem_flg; / * operation flags * / The number of
semaphores to be processed by sem_num.
The operation to be performed by sem_op.
sem_flg operation flag.
If sem_op is negative, then the semaphore will subtract its value. This is related to the resources controlled by the semaphore. If IPC_NOWAIT is not used, the calling process will go to sleep until the resources controlled by the semaphore are available. If sem_op is a positive number, the semaphore is added to its value. This is the resource that the process releases the semaphore control. Finally, if sem_op is 0, then the calling process will call sleep () until the semaphore value is 0. This is used when a process is waiting for completely idle resources.
[Directory]
----------------------------------------------- ---------------------------------
semctl ()
system call: semctl ();
prototype: int semctl (int semid , int semnum, int cmd, union semunarg);
Return value: If successful, it is a positive number.
If it fails, it is -1: errno = EACCESS (insufficient authority)
EFAULT (the address pointed to by arg is invalid)
EIDRM (the semaphore set has been deleted)
EINVAL (the semaphore set does not exist, or the semid is invalid)
EPERM (EUID does not have the right to cmd )
ERANGE (semaphore value out of range)
system call semctl is used to perform control operations on the semaphore set. This is very similar to the system call msgctl in the message queue. But the parameters of these two system calls are slightly different. Because the semaphore is generally used as a semaphore set, rather than a single semaphore. Therefore, in the operation of the semaphore set, not only the IPC key value but also the specific semaphore in the semaphore set must be known. Both of these system calls use the parameter cmd, which is used to indicate the specific command to be operated. The last parameter in the two system calls is also different. In the system call msgctl, the last parameter is a pointer to the data structure used in the kernel. We use this data structure to obtain some information about the message queue, and to set or change the access rights and users of the queue. However, additional optional commands are supported in the semaphore, which requires a more complex data structure.
The first parameter of the system call semctl () is the key value. The second parameter is the number of semaphores.
The following is a program to open and create a semaphore set:
intopen_semaphore_set (key_t keyval, int numsems)
{
intsid;
if (! numsems)
return (-1);
if ((sid = semget (mykey, numsems, IPC_CREAT | 0660)) ==-1)
{
return (-1);
}
return (sid);
}
};
[directory]
---- -------------------------------------------------- --------------------------
semop ()
system call: semop ();
call prototype: int semop (int semid, struct sembuf * sops, unsign ednsops);
Return value: 0, if successful. -1, if it fails: errno = E2BIG (nsops is greater than the maximum number of ops)
EACCESS (insufficient authority)
EAGAIN (IPC_NOWAIT is used, but the operation cannot continue)
EFAULT (the address pointed to by sops is invalid)
EIDRM (the semaphore set has been deleted)
EINTR (received other signals while sleeping)
EINVAL (semaphore set does not exist, or semid is invalid)
ENOMEM (SEM_UNDO is used, but there is not enough memory to create the required data structure)
ERANGE (semaphore value is out of range)
The first parameter is the key value. The second parameter is a pointer to the array to be operated on. The third parameter is the number of operations in the array. The parameter sops points to an array consisting of sembuf. This array is defined in linux / sem.h:
/ * semop systemcall takes an array of these * /
structsembuf {
ushortsem_num; / * semaphore index in array * /
shortsem_op; / * semaphore operation * /
shortsem_flg; / * operation flags * / The number of
semaphores to be processed by sem_num.
The operation to be performed by sem_op.
sem_flg operation flag.
If sem_op is negative, then the semaphore will subtract its value. This is related to the resources controlled by the semaphore. If IPC_NOWAIT is not used, the calling process will go to sleep until the resources controlled by the semaphore are available. If sem_op is a positive number, the semaphore is added to its value. This is the resource that the process releases the semaphore control. Finally, if sem_op is 0, then the calling process will call sleep () until the semaphore value is 0. This is used when a process is waiting for completely idle resources.
[Directory]
----------------------------------------------- ---------------------------------
semctl ()
system call: semctl ();
prototype: int semctl (int semid , int semnum, int cmd, union semunarg);
Return value: If successful, it is a positive number.
If it fails, it is -1: errno = EACCESS (insufficient authority)
EFAULT (the address pointed to by arg is invalid)
EIDRM (the semaphore set has been deleted)
EINVAL (the semaphore set does not exist, or the semid is invalid)
EPERM (EUID does not have the right to cmd )
ERANGE (semaphore value out of range)
system call semctl is used to perform control operations on the semaphore set. This is very similar to the system call msgctl in the message queue. But the parameters of these two system calls are slightly different. Because the semaphore is generally used as a semaphore set, rather than a single semaphore. Therefore, in the operation of the semaphore set, not only the IPC key value but also the specific semaphore in the semaphore set must be known. Both of these system calls use the parameter cmd, which is used to indicate the specific command to be operated. The last parameter in the two system calls is also different. In the system call msgctl, the last parameter is a pointer to the data structure used in the kernel. We use this data structure to obtain some information about the message queue, and to set or change the access rights and users of the queue. However, additional optional commands are supported in the semaphore, which requires a more complex data structure.
The first parameter of the system call semctl () is the key value. The second parameter is the number of semaphores.
The commands that can be used in the parameter cmd are as follows:
IPC_STAT reads the data structure semid_ds of a semaphore set and stores it in the buf parameter in semun.
IPC_SET sets the element ipc_perm in the data structure semid_ds of the semaphore set, and its value is taken from the buf parameter in semun.
Β· IPC_RMID deletes the semaphore set from memory.
Β· GETALL is used to read the value of all semaphores in the semaphore set.
Β· GETNCNT returns the number of processes waiting for resources.
Β· GETPID returns the PID of the last process that performed the semop operation.
GETVAL returns the value of a single semaphore in the semaphore set.
Β· GETZCNT returns the number of processes that are waiting for completely idle resources.
Β· SETALL sets the value of all semaphores in the semaphore set.
Β· SETVAL sets the value of a single semaphore in the semaphore set.
The parameter arg represents an instance of semun. semun is defined in linux / sem.h:
/ * arg for semctl systemcalls. * /
unionsemun {
intval; / * value for SETVAL * /
structsemid_ds * buf; / * buffer for IPC_STAT & IPC_SET * /
ushort * array; / * array for GETALL & SETALL * /
structseminfo * buf; / * buffer for IPC_INFO * /
void * pad;
val Used when executing the SETVAL command. buf is used in the IPC_STAT / IPC_SET command. Represents the data structure of the semaphore used in the kernel. The pointer used by array when using the GETALL / SETALL command.
The following program returns the value of the semaphore. When using the GETVAL command, the last parameter in the call is ignored:
intget_sem_val (intsid, intsemnum)
{
return (semctl (sid, semnum, GETVAL, 0));
}
The following is a practical example:
# defineMAX_PRINTERS5
printer_usage ()
{
int x;
for (x = 0; x <MAX_PRINTERS; x ++)
printf ("Printer% d :% d ", x, get_sem_val (sid, x));
}
The following procedure can be used to initialize a new semaphore value:
void init_semaphore (int sid, int semnum, int initval)
{
union semunsemopts;
semopts.val = initval;
semctl (sid, semnum, SETVAL, semopts);
}
Note that the last parameter in the system call semctl is a copy of the union type, not a pointer to the union type.
[Directory]
----------------------------------------------- ---------------------------------
Shared memory
Shared memory is a memory area shared by several processes. This can be said to be the fastest form of IPC because it does not require any intermediate operations (eg, pipes, message queues, etc.). It simply maps the memory segment directly into the address space of the calling process. Such a memory segment can be created by a process, and then other processes can read and write this memory segment.
The shared memory segment of each system also maintains an internal data structure shmid_ds in the system kernel. This data structure is defined in linux / shm.h as follows:
/ * One shmid data structure for each shared memory segment in the system. * /
Struct shmid_ds {
struct ipc_perm shm_perm; / * operation perms * /
int shm_segsz ; / * size of segment (bytes) * /
time_t shm_atime; / * last attach time * /
time_t shm_dtime; / * last detach time * /
time_t shm_ctime; / * last change time * /
unsigned short shm_cpid; / * pid of creator * /
unsigned short shm_lpid; / * pid of last operator * /
short shm_nattch; / * no. of current attaches * /
/ * the following are private * /
unsigned short shm_npages; / * size of segment (pages) * /
unsigned long * shm_pages; / * array of ptrs to frames-> SHMMAX * /
struct vm_area_struct * attaches; / * descriptors for attaches * /
};
IPC_STAT reads the data structure semid_ds of a semaphore set and stores it in the buf parameter in semun.
IPC_SET sets the element ipc_perm in the data structure semid_ds of the semaphore set, and its value is taken from the buf parameter in semun.
Β· IPC_RMID deletes the semaphore set from memory.
Β· GETALL is used to read the value of all semaphores in the semaphore set.
Β· GETNCNT returns the number of processes waiting for resources.
Β· GETPID returns the PID of the last process that performed the semop operation.
GETVAL returns the value of a single semaphore in the semaphore set.
Β· GETZCNT returns the number of processes that are waiting for completely idle resources.
Β· SETALL sets the value of all semaphores in the semaphore set.
Β· SETVAL sets the value of a single semaphore in the semaphore set.
The parameter arg represents an instance of semun. semun is defined in linux / sem.h:
/ * arg for semctl systemcalls. * /
unionsemun {
intval; / * value for SETVAL * /
structsemid_ds * buf; / * buffer for IPC_STAT & IPC_SET * /
ushort * array; / * array for GETALL & SETALL * /
structseminfo * buf; / * buffer for IPC_INFO * /
void * pad;
val Used when executing the SETVAL command. buf is used in the IPC_STAT / IPC_SET command. Represents the data structure of the semaphore used in the kernel. The pointer used by array when using the GETALL / SETALL command.
The following program returns the value of the semaphore. When using the GETVAL command, the last parameter in the call is ignored:
intget_sem_val (intsid, intsemnum)
{
return (semctl (sid, semnum, GETVAL, 0));
}
The following is a practical example:
# defineMAX_PRINTERS5
printer_usage ()
{
int x;
for (x = 0; x <MAX_PRINTERS; x ++)
printf ("Printer% d :% d ", x, get_sem_val (sid, x));
}
The following procedure can be used to initialize a new semaphore value:
void init_semaphore (int sid, int semnum, int initval)
{
union semunsemopts;
semopts.val = initval;
semctl (sid, semnum, SETVAL, semopts);
}
Note that the last parameter in the system call semctl is a copy of the union type, not a pointer to the union type.
[Directory]
----------------------------------------------- ---------------------------------
Shared memory
Shared memory is a memory area shared by several processes. This can be said to be the fastest form of IPC because it does not require any intermediate operations (eg, pipes, message queues, etc.). It simply maps the memory segment directly into the address space of the calling process. Such a memory segment can be created by a process, and then other processes can read and write this memory segment.
The shared memory segment of each system also maintains an internal data structure shmid_ds in the system kernel. This data structure is defined in linux / shm.h as follows:
/ * One shmid data structure for each shared memory segment in the system. * /
Struct shmid_ds {
struct ipc_perm shm_perm; / * operation perms * /
int shm_segsz ; / * size of segment (bytes) * /
time_t shm_atime; / * last attach time * /
time_t shm_dtime; / * last detach time * /
time_t shm_ctime; / * last change time * /
unsigned short shm_cpid; / * pid of creator * /
unsigned short shm_lpid; / * pid of last operator * /
short shm_nattch; / * no. of current attaches * /
/ * the following are private * /
unsigned short shm_npages; / * size of segment (pages) * /
unsigned long * shm_pages; / * array of ptrs to frames-> SHMMAX * /
struct vm_area_struct * attaches; / * descriptors for attaches * /
};
shm_perm is the data structure ipc_perm An instance of. What is stored here is the access rights of the memory segment and other information about the creator of the memory segment.
shm_segsz The size of the memory segment in bytes.
shm_atime The time the last process accessed the memory segment.
shm_dtime The time when the last process left the memory segment.
shm_ctime The last time the memory segment was changed.
shm_cpid PID of the memory segment creation process.
shm_lpid The PID of the last process using the memory segment.
shm_nattch The total number of processes currently using memory segments.
[Directory]
----------------------------------------------- ---------------------------------
shmget ()
system call: shmget ();
prototype: int shmget (key_t key , int size, int shmflg);
Return value: If successful, returns the shared memory segment identifier. If it fails, it returns -1: errno = EINVAL (invalid memory segment size)
EEXIST (memory segment already exists and cannot be created)
EIDRM (memory segment has been deleted)
ENOENT (memory segment does not exist)
EACCES (insufficient authority)
ENOMEM ( There is not enough memory to create a memory segment)
The first parameter in the system call shmget () is the key value (it is returned with the system call ftok ()). Other operations must be carried out according to the commands in shmflg.
Β· IPC_CREAT If there is no shared memory segment in the system kernel, create a shared memory segment.
Β· When IPC_EXCL is used together with IPC_CREAT, if the shared memory segment already exists, the call fails.
When IPC_CREAT is used alone, the system call shmget () either returns the identifier of a newly created shared memory segment or returns the key value of an existing shared memory segment. If IPC_EXCL and IPC_CREAT are used together, either the system call creates a new shared memory segment or returns an error value of -1. IPC_EXCL is useless alone.
The following is a program to locate and create a shared memory segment:
int open_segment (key_t keyval, int segsize)
{
int shmid;
if ((shmid = shmget (keyval, segsize, IPC_CREAT | 0660)) ==-1)
{
return (- 1);
}
return (shmid);
}
Once a process has a valid IPC identifier for a given memory segment, its next step is to map the shared memory segment into its own address space.
[Directory]
----------------------------------------------- ---------------------------------
shmat ()
system call: shmat ();
prototype: int shmat (int shmid , char * shmaddr, int shmflg);
Return value: If successful, it returns the address of the shared memory segment connected to the process. If it fails, return -1: errno = EINVAL (invalid IPC ID value or invalid address)
ENOMEM (not enough memory)
EACCES (insufficient access rights)
If the value of the parameter addr is 0, then the system kernel tries to find An unmapped memory area is displayed. We recommend this method. You can specify an address, but this is usually to speed up access to hardware devices or resolve conflicts with other programs.
The call parameter in the following program is the IPC identifier of a memory segment, which returns the address of the memory segment connection:
char * attach_segment (int shmid)
{
return (shmat (shmid, 0, 0));
}
Once the memory segment is connected correctly After reaching the process, there is a pointer to the memory segment in the process. In this way, you can use the pointer to read this memory segment in the future. But be careful not to lose the initial value of the pointer.
[table of Contents]
-------------------------------------------------- ------------------------------
shmctl ()
shm_segsz The size of the memory segment in bytes.
shm_atime The time the last process accessed the memory segment.
shm_dtime The time when the last process left the memory segment.
shm_ctime The last time the memory segment was changed.
shm_cpid PID of the memory segment creation process.
shm_lpid The PID of the last process using the memory segment.
shm_nattch The total number of processes currently using memory segments.
[Directory]
----------------------------------------------- ---------------------------------
shmget ()
system call: shmget ();
prototype: int shmget (key_t key , int size, int shmflg);
Return value: If successful, returns the shared memory segment identifier. If it fails, it returns -1: errno = EINVAL (invalid memory segment size)
EEXIST (memory segment already exists and cannot be created)
EIDRM (memory segment has been deleted)
ENOENT (memory segment does not exist)
EACCES (insufficient authority)
ENOMEM ( There is not enough memory to create a memory segment)
The first parameter in the system call shmget () is the key value (it is returned with the system call ftok ()). Other operations must be carried out according to the commands in shmflg.
Β· IPC_CREAT If there is no shared memory segment in the system kernel, create a shared memory segment.
Β· When IPC_EXCL is used together with IPC_CREAT, if the shared memory segment already exists, the call fails.
When IPC_CREAT is used alone, the system call shmget () either returns the identifier of a newly created shared memory segment or returns the key value of an existing shared memory segment. If IPC_EXCL and IPC_CREAT are used together, either the system call creates a new shared memory segment or returns an error value of -1. IPC_EXCL is useless alone.
The following is a program to locate and create a shared memory segment:
int open_segment (key_t keyval, int segsize)
{
int shmid;
if ((shmid = shmget (keyval, segsize, IPC_CREAT | 0660)) ==-1)
{
return (- 1);
}
return (shmid);
}
Once a process has a valid IPC identifier for a given memory segment, its next step is to map the shared memory segment into its own address space.
[Directory]
----------------------------------------------- ---------------------------------
shmat ()
system call: shmat ();
prototype: int shmat (int shmid , char * shmaddr, int shmflg);
Return value: If successful, it returns the address of the shared memory segment connected to the process. If it fails, return -1: errno = EINVAL (invalid IPC ID value or invalid address)
ENOMEM (not enough memory)
EACCES (insufficient access rights)
If the value of the parameter addr is 0, then the system kernel tries to find An unmapped memory area is displayed. We recommend this method. You can specify an address, but this is usually to speed up access to hardware devices or resolve conflicts with other programs.
The call parameter in the following program is the IPC identifier of a memory segment, which returns the address of the memory segment connection:
char * attach_segment (int shmid)
{
return (shmat (shmid, 0, 0));
}
Once the memory segment is connected correctly After reaching the process, there is a pointer to the memory segment in the process. In this way, you can use the pointer to read this memory segment in the future. But be careful not to lose the initial value of the pointer.
[table of Contents]
-------------------------------------------------- ------------------------------
shmctl ()
system call: shmctl ();
prototype: int shmctl (int shmqid, int cmd , struct shmid_ds * buf);
Return value: 0 if successful.
-1, if it fails: errno = EACCES (no read permission, and the command is IPC_STAT)
EFAULT (the address pointed to by buf is invalid, and the commands are IPC_SET and IPC_STAT)
EIDRM (the memory segment is removed)
EINVAL (shmqid is invalid)
EPERM ( Use IPC_SET or IPC_RMID command, but the calling process does not have write permission)
IPC_STAT reads the data structure shmid_ds of a memory segment and stores it in the address pointed to by the buf parameter.
IPC_SET sets the value of element ipc_perm in the data structure shmid_ds of the memory segment. Get the value to be set from the parameter buf.
IPC_RMID marks the memory segment as removed.
The command IPC_RMID does not really remove the shared memory segment from the system kernel, but marks the memory segment as removable. The process calls the system call shmdt () out of a shared memory segment.
[Directory]
----------------------------------------------- ---------------------------------
shmdt ()
System call: shmdt ();
Call prototype: int shmdt (char * shmaddr);
Return value: If it fails, return -1: errno = EINVAL (invalid connection address)
When a process is not in a shared memory segment, it The memory segment will be detached from its address space. But this does not mean removing the shared memory segment from the system kernel. When the process is successfully detached, the element shm_nattch in the data structure shmid_ds will decrease by 1. When this value is reduced to 0, the system kernel will physically remove the memory segment from the system kernel.
[Directory]
----------------------------------------------- ---------------------------------
Threads
Threads are usually called lightweight processes. Although this name is somewhat simplified, it helps to understand the concept of threads. Because threads and processes are relatively small, relatively speaking, threads spend less CPU resources. Processes often need their own resources, but resources can be shared between threads, so threads save memory. Mach threads allow programmers to write programs that run concurrently, and these programs can run on single-processor machines or multi-processor machines. In addition, in a single-processor environment, threads can increase efficiency when applications perform operations that are likely to cause blocking and delays.
Use the subfunction pthread_create to create a new thread. It has four parameters: a thread variable used to save the thread, a thread attribute, a function to be called when the thread executes, and a parameter of this function. For example:
pthread_ta_thread;
pthread_attr_ta_thread_attribute;
void thread_function (void * argument);
char * some_argument;
pthread_create (& a_thread, a_thread_attribute, (void *) & thread_function,
(void *) & some_argument); The
thread attribute only specifies the minimum stack size that needs to be used. In future programs, the attributes of the thread can specify other values, but now most programs can use the default values. Unlike processes created using the fork system call in UNIX systems, they use the same execution point as their parent process, and threads use the parameters in pthread_create to indicate the function to start execution.
prototype: int shmctl (int shmqid, int cmd , struct shmid_ds * buf);
Return value: 0 if successful.
-1, if it fails: errno = EACCES (no read permission, and the command is IPC_STAT)
EFAULT (the address pointed to by buf is invalid, and the commands are IPC_SET and IPC_STAT)
EIDRM (the memory segment is removed)
EINVAL (shmqid is invalid)
EPERM ( Use IPC_SET or IPC_RMID command, but the calling process does not have write permission)
IPC_STAT reads the data structure shmid_ds of a memory segment and stores it in the address pointed to by the buf parameter.
IPC_SET sets the value of element ipc_perm in the data structure shmid_ds of the memory segment. Get the value to be set from the parameter buf.
IPC_RMID marks the memory segment as removed.
The command IPC_RMID does not really remove the shared memory segment from the system kernel, but marks the memory segment as removable. The process calls the system call shmdt () out of a shared memory segment.
[Directory]
----------------------------------------------- ---------------------------------
shmdt ()
System call: shmdt ();
Call prototype: int shmdt (char * shmaddr);
Return value: If it fails, return -1: errno = EINVAL (invalid connection address)
When a process is not in a shared memory segment, it The memory segment will be detached from its address space. But this does not mean removing the shared memory segment from the system kernel. When the process is successfully detached, the element shm_nattch in the data structure shmid_ds will decrease by 1. When this value is reduced to 0, the system kernel will physically remove the memory segment from the system kernel.
[Directory]
----------------------------------------------- ---------------------------------
Threads
Threads are usually called lightweight processes. Although this name is somewhat simplified, it helps to understand the concept of threads. Because threads and processes are relatively small, relatively speaking, threads spend less CPU resources. Processes often need their own resources, but resources can be shared between threads, so threads save memory. Mach threads allow programmers to write programs that run concurrently, and these programs can run on single-processor machines or multi-processor machines. In addition, in a single-processor environment, threads can increase efficiency when applications perform operations that are likely to cause blocking and delays.
Use the subfunction pthread_create to create a new thread. It has four parameters: a thread variable used to save the thread, a thread attribute, a function to be called when the thread executes, and a parameter of this function. For example:
pthread_ta_thread;
pthread_attr_ta_thread_attribute;
void thread_function (void * argument);
char * some_argument;
pthread_create (& a_thread, a_thread_attribute, (void *) & thread_function,
(void *) & some_argument); The
thread attribute only specifies the minimum stack size that needs to be used. In future programs, the attributes of the thread can specify other values, but now most programs can use the default values. Unlike processes created using the fork system call in UNIX systems, they use the same execution point as their parent process, and threads use the parameters in pthread_create to indicate the function to start execution.
Now we can compile the first program. We compile a multi-threaded application and print "Hello Wo rld" on the standard output. First, we need two thread variables, a function that can be called when a new thread starts execution. We also need to specify the information that each thread should print. One approach is to separate the strings to be printed and give each thread a string as the starting parameter. Please see the following code:
void print_message_function (void * ptr);
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = "Wo rld";
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
pthread_create (& thread2, pthread_attr_default,
(void *) & print_message_function, (void *) message2);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s ", message);
} The
program creates the first thread by calling pthread_create and takes" Hello "as its startup parameter. The parameter of the second thread is "World". When the first thread starts executing, it uses the parameter "Hello" to execute the function print_message_function. It prints "Hello" on standard output, and then ends the call to the function. The thread terminates when it leaves its initialization function, so the first thread terminates after printing "Hello". When the second thread executes, it prints "World" and then terminates. But this program has two major flaws.
First and foremost is that threads are executed simultaneously. In this way, there is no guarantee that the first thread executes the print statement first. So you probably see "World Hello" on the screen, not "Hello World". Please note that the call to exit is used by the parent thread in the main program. In this way, if the parent thread calls exit before the two child threads call the print statement, there will be no print output. This is because the exit function will exit the process and release the task at the same time, so all threads are ended. Any thread (whether parent thread or child thread) calling exit will terminate all other threads. If you want the threads to terminate separately, you can use the pthread_exit function.
We can use a method to make up for this defect. We can insert a delay program in the parent thread to give the child thread enough time to complete the print call. Similarly, a delay program is inserted before the second is called to ensure that the first thread completes the task before the second thread executes.
void print_message_function (void * ptr);
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = "Wo rld";
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
sleep (10);
pthread_create (& thread2, pthread_attr_default,
(void *) & print_message_function, (void *) message2);
sleep (10);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s", message);
pthread_exit (0);
}
Did this meet our requirements? Not so, because the time-dependent delay to perform synchronization is not reliable. The situation encountered here is the same as a distributed program and shared resources. The shared resource is a standard output device, and the distributed computing program is three threads.
In fact, there is another mistake here. The function sleep and the function exit are related to the process. When the thread calls sleep, the entire process is in a sleep state, that is, all three threads go to sleep. In this way, we did not actually solve any problems. The function that wants to make a thread sleep is pthread_delay_np. For example, to make a thread sleep for 2 seconds, use the following procedure:
struct
timespec delay; delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np (& delay);
}
[directory]
------------ -------------------------------------------------- ------------------
Thread synchronization
void print_message_function (void * ptr);
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = "Wo rld";
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
pthread_create (& thread2, pthread_attr_default,
(void *) & print_message_function, (void *) message2);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s ", message);
} The
program creates the first thread by calling pthread_create and takes" Hello "as its startup parameter. The parameter of the second thread is "World". When the first thread starts executing, it uses the parameter "Hello" to execute the function print_message_function. It prints "Hello" on standard output, and then ends the call to the function. The thread terminates when it leaves its initialization function, so the first thread terminates after printing "Hello". When the second thread executes, it prints "World" and then terminates. But this program has two major flaws.
First and foremost is that threads are executed simultaneously. In this way, there is no guarantee that the first thread executes the print statement first. So you probably see "World Hello" on the screen, not "Hello World". Please note that the call to exit is used by the parent thread in the main program. In this way, if the parent thread calls exit before the two child threads call the print statement, there will be no print output. This is because the exit function will exit the process and release the task at the same time, so all threads are ended. Any thread (whether parent thread or child thread) calling exit will terminate all other threads. If you want the threads to terminate separately, you can use the pthread_exit function.
We can use a method to make up for this defect. We can insert a delay program in the parent thread to give the child thread enough time to complete the print call. Similarly, a delay program is inserted before the second is called to ensure that the first thread completes the task before the second thread executes.
void print_message_function (void * ptr);
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = "Wo rld";
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
sleep (10);
pthread_create (& thread2, pthread_attr_default,
(void *) & print_message_function, (void *) message2);
sleep (10);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s", message);
pthread_exit (0);
}
Did this meet our requirements? Not so, because the time-dependent delay to perform synchronization is not reliable. The situation encountered here is the same as a distributed program and shared resources. The shared resource is a standard output device, and the distributed computing program is three threads.
In fact, there is another mistake here. The function sleep and the function exit are related to the process. When the thread calls sleep, the entire process is in a sleep state, that is, all three threads go to sleep. In this way, we did not actually solve any problems. The function that wants to make a thread sleep is pthread_delay_np. For example, to make a thread sleep for 2 seconds, use the following procedure:
struct
timespec delay; delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_delay_np (& delay);
}
[directory]
------------ -------------------------------------------------- ------------------
Thread synchronization
POSIX provides two methods for thread synchronization, mutex and condition variables. mutex is a simple locking method to control access to shared resources. We can create a read / write program that shares a shared buffer and use mutex to control access to the buffer.
void reader_function (void);
void writer_function (void);
char buf fer;
int buffer_has_item = 0;
pthread_mutex_t mutex;
struct timespec delay;
main ()
{
pthread_t reader;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_mutex_init (& mutex, pthread_mutexattr_default);
pthread_create (& reader, pthread_attr_default, (void
NULL);
writer_function ()
void writer_function (void)
{
while (1)
{
pthread_mutex_lock (& ββmutex);
if (buffer_has_item == 0)
{
buffer = make_new_item ();
buffer_has_item = 1;
}
pthread_mutex_unlock (& ββmutex lay; pthread_de & lay ;
pthread_deex ;
}
}
void reader_function (void)
{
while (1)
{
pthread_mutex_lock (& ββmutex);
if (buffer_has_item == 1)
{ consume_item
(buffer);
buffer_has_item = 0;
}
pthread_mutex_unlock (& ββmutex);
pthread_delay_np (& delay);
}
}
on the program In, we assume that the buffer can only hold one piece of information, so that the buffer has only two states, with one piece of information or no information. The delay is used to avoid that a thread will always occupy the mutex.
But the disadvantage of mutex is that it has only two states, locked and unlocked. POSIX condition variables make up for the lack of mutex by allowing threads to block and wait for another thread's signal method. When a signal is received, the blocked thread will be evoked and try to obtain the related mutex lock.
[Directory]
----------------------------------------------- ---------------------------------
Use semaphore coordination procedures
We can use semaphores to revisit the above read / write procedure. The operations involving semaphores are semaphore_up, semaphore_down, semaphore_init, semaphore_destroy, and semaphore_decrement. All these operations have only one parameter, a pointer to the semaphore target.
void reader_function (void);
void writer_function (void);
char buffer;
Semaphore writers_turn;
Semaphore readers_turn;
main ()
{
pthread_t reader;
semaphore_init (& readers_turn);
semaphore_init (& writers_turn);
/ * writer must go first * /
semaphore_down (& readers ;
pthread_create (& reader, pthread_attr_default,
(void *) & reader_function, NULL);
writer _ function ();
}
void writer_function (void)
{
while (1)
{
semaphore_down (& writers_turn);
Bu FFER = make_new_item ();
semaphore_up (& readers_turn);
}
}
void reader_function (void)
{
the while (. 1)
{
semaphore_down (& readers_turn);
consume_item (Buffer);
semaphore_up (& writers_turn);
}
}
This example also Not all functions of general semaphores are fully utilized. We can rewrite the "Hello world" program using semaphores:
void print_message_function (void * ptr);
Semaphore child_counter;
Semaphore worlds_turn;
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = " Wo rld ";
semaphore_init (& child_counter);
semaphore_init (& worlds_turn);
semaphore_down (& worlds_turn); / * world goes second * /
semaphore_decrement (& child_counter); / * value now 0 * /
semaphore_decrement (& child_counter); / * value now -1 * /
/ *
* child_counter now must be up -ed 2 times for a thread blocked on it
* to be released
*
* /
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
semaphore_down (& worlds_turn);
pthread_create (& thread2, pthread_attr_default,
(void * ) & print_message_function, (void *) message2);
semaphore_down (& child_counter);
/ * not really necessary to destroy since we are exiting anyway * /
semaphore_destroy (& child_counter);
semaphore_destroy (& worlds_turn);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s", message);
fflush (stdout) ;
semaphore_up (& worlds_turn);
semaphore_up (& child_counter);
pthread _ exit (0);
} The
void reader_function (void);
void writer_function (void);
char buf fer;
int buffer_has_item = 0;
pthread_mutex_t mutex;
struct timespec delay;
main ()
{
pthread_t reader;
delay.tv_sec = 2;
delay.tv_nsec = 0;
pthread_mutex_init (& mutex, pthread_mutexattr_default);
pthread_create (& reader, pthread_attr_default, (void
NULL);
writer_function ()
void writer_function (void)
{
while (1)
{
pthread_mutex_lock (& ββmutex);
if (buffer_has_item == 0)
{
buffer = make_new_item ();
buffer_has_item = 1;
}
pthread_mutex_unlock (& ββmutex lay; pthread_de & lay ;
pthread_deex ;
}
}
void reader_function (void)
{
while (1)
{
pthread_mutex_lock (& ββmutex);
if (buffer_has_item == 1)
{ consume_item
(buffer);
buffer_has_item = 0;
}
pthread_mutex_unlock (& ββmutex);
pthread_delay_np (& delay);
}
}
on the program In, we assume that the buffer can only hold one piece of information, so that the buffer has only two states, with one piece of information or no information. The delay is used to avoid that a thread will always occupy the mutex.
But the disadvantage of mutex is that it has only two states, locked and unlocked. POSIX condition variables make up for the lack of mutex by allowing threads to block and wait for another thread's signal method. When a signal is received, the blocked thread will be evoked and try to obtain the related mutex lock.
[Directory]
----------------------------------------------- ---------------------------------
Use semaphore coordination procedures
We can use semaphores to revisit the above read / write procedure. The operations involving semaphores are semaphore_up, semaphore_down, semaphore_init, semaphore_destroy, and semaphore_decrement. All these operations have only one parameter, a pointer to the semaphore target.
void reader_function (void);
void writer_function (void);
char buffer;
Semaphore writers_turn;
Semaphore readers_turn;
main ()
{
pthread_t reader;
semaphore_init (& readers_turn);
semaphore_init (& writers_turn);
/ * writer must go first * /
semaphore_down (& readers ;
pthread_create (& reader, pthread_attr_default,
(void *) & reader_function, NULL);
writer _ function ();
}
void writer_function (void)
{
while (1)
{
semaphore_down (& writers_turn);
Bu FFER = make_new_item ();
semaphore_up (& readers_turn);
}
}
void reader_function (void)
{
the while (. 1)
{
semaphore_down (& readers_turn);
consume_item (Buffer);
semaphore_up (& writers_turn);
}
}
This example also Not all functions of general semaphores are fully utilized. We can rewrite the "Hello world" program using semaphores:
void print_message_function (void * ptr);
Semaphore child_counter;
Semaphore worlds_turn;
main ()
{
pthread_t
thread1, thread2 ; char * message1 = "Hello";
char * message2 = " Wo rld ";
semaphore_init (& child_counter);
semaphore_init (& worlds_turn);
semaphore_down (& worlds_turn); / * world goes second * /
semaphore_decrement (& child_counter); / * value now 0 * /
semaphore_decrement (& child_counter); / * value now -1 * /
/ *
* child_counter now must be up -ed 2 times for a thread blocked on it
* to be released
*
* /
pthread_create (& thread1, pthread_attr_default,
(void *) & print_message_function, (void *) message1);
semaphore_down (& worlds_turn);
pthread_create (& thread2, pthread_attr_default,
(void * ) & print_message_function, (void *) message2);
semaphore_down (& child_counter);
/ * not really necessary to destroy since we are exiting anyway * /
semaphore_destroy (& child_counter);
semaphore_destroy (& worlds_turn);
exit (0);
}
void print_message_function (void * ptr)
{
char * message;
message = (char *) ptr;
printf ("% s", message);
fflush (stdout) ;
semaphore_up (& worlds_turn);
semaphore_up (& child_counter);
pthread _ exit (0);
} The
semaphore child _ counter is used to force the parent thread to block until two child threads execute the printf statement and the subsequent semaphore_up (& child_counter) statement. carried out.
Semaphore.h
#ifndef SEMAPHORES
#define SEMAPHORES
#include
#include
typedef struct Semaphore
{
int v;
pthread_mutex_t mutex;
pthread_cond_t cond;
}
S emaphore;
int semaphore_down (Semaphore * S);
int semaphore_decrement (Semaphore * S);
int semaphore_up (Semaphore * S);
void semaphore_destroy (Semaphore * S);
void semaphore_init (Semaphore * S);
int semaphore_value ( semaphore * S);
int tw_pthread_cond_signal (pthread_cond_t * C);
int tw_pthread_cond_wait (pthread_cond_t * C, pthread_mutex_t * m);
int tw_pthread_mutex_unlock (pthread_mutex_t * m);
int tw_pthread_mutex_lock (pthread_mutex_t * m);
void do_error (char * MSG);
# endif
Semaphore.c
#include "semaphore.h"
/ *
* function must be called prior to semaphore use.
*
* /
void
semaphore_init (Semaphore * s)
{
s-> v = 1;
if (pthread_mutex_init (& (s-> mutex), pthread_mutexattr_default) == -1)
do_error ("Error setting up semaphore mutex");
if ( pthread_cond_init (& (s-> cond), pthread_condattr_default) == -1)
do_error ("Error setting up semaphore condition signal");
* function should be called when there is no longer a need for
* the semaphore.
*
* /
void
semaphore_destroy (Semaphore * s)
{
if (pthread_mutex_destroy (& (s-> mutex)) == -1)
do_error ("Error destroying semaphore mutex");
if (pthread_cond_destroy (& (s->cond)) == -1)
do_error ("Error destroying semaphore condition signal");
}
/ *
* function increments the semaphore and signals any threads that
* are blocked waiting a change in the semaphore.
*
* /
int
semaphore_up (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock ( & (s-> mutex));
(s-> v) + +;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
tw_pthread_cond_signal (& (s-> cond));
return (value_after_op );
}
/ *
* function decrements the semaphore and blocks if the semaphore is
* <= 0 until another thread signals a change.
*
* /
int
semaphore_down (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex));
while (s-> v <= 0)
{
tw_pthread_cond_wait (& (s-> cond), & (s-> mutex) );
}
(s-> v)--;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ *
* function does NOT block but simply decrements the semaphore.
* should not be used instead of down-only for programs where
* multiple threads must up on a semaphore before another thread
* can go down, ie, allows programmer to set the semaphore to
* a negative value prior to using it for synchronization.
*
* /
int
semaphore_decrement (Semaphore * s)
(
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex)); s-> v--
;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ *
* function returns the value of the semaphore at the time the
* critical section is accessed. obviously the value is not guarenteed
* after the function unlocks the critical section. provided only
* for casual debugging, a better approach is for the programmar to
* protect one semaphore with another and then check its value.
* an alternative is to simply record the value returned by semaphore_up
* or semaphore_down.
*
* /
int
semaphore_value (Semaphore * s)
{
/ * not for sync * /
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex));
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ * -------------------------------------- ------------------------------ * /
/ * The following functions replace standard library functions in that * /
/ * they exit on any error returned from the system calls. Saves us * /
/ * from having to check each and every call above. * /
/ * ---------------------- ---------------------------------------------- * /
int
tw_pthread_mutex_unlock (pthread_mutex_t * m)
{
int the return_value;
IF ((pthread_mutex_unlock the return_value = (m)) == -1)
do_error ( "pthread_mutex_unlock");
return (the return_value);
}
int
Semaphore.h
#ifndef SEMAPHORES
#define SEMAPHORES
#include
#include
typedef struct Semaphore
{
int v;
pthread_mutex_t mutex;
pthread_cond_t cond;
}
S emaphore;
int semaphore_down (Semaphore * S);
int semaphore_decrement (Semaphore * S);
int semaphore_up (Semaphore * S);
void semaphore_destroy (Semaphore * S);
void semaphore_init (Semaphore * S);
int semaphore_value ( semaphore * S);
int tw_pthread_cond_signal (pthread_cond_t * C);
int tw_pthread_cond_wait (pthread_cond_t * C, pthread_mutex_t * m);
int tw_pthread_mutex_unlock (pthread_mutex_t * m);
int tw_pthread_mutex_lock (pthread_mutex_t * m);
void do_error (char * MSG);
# endif
Semaphore.c
#include "semaphore.h"
/ *
* function must be called prior to semaphore use.
*
* /
void
semaphore_init (Semaphore * s)
{
s-> v = 1;
if (pthread_mutex_init (& (s-> mutex), pthread_mutexattr_default) == -1)
do_error ("Error setting up semaphore mutex");
if ( pthread_cond_init (& (s-> cond), pthread_condattr_default) == -1)
do_error ("Error setting up semaphore condition signal");
* function should be called when there is no longer a need for
* the semaphore.
*
* /
void
semaphore_destroy (Semaphore * s)
{
if (pthread_mutex_destroy (& (s-> mutex)) == -1)
do_error ("Error destroying semaphore mutex");
if (pthread_cond_destroy (& (s->cond)) == -1)
do_error ("Error destroying semaphore condition signal");
}
/ *
* function increments the semaphore and signals any threads that
* are blocked waiting a change in the semaphore.
*
* /
int
semaphore_up (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock ( & (s-> mutex));
(s-> v) + +;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
tw_pthread_cond_signal (& (s-> cond));
return (value_after_op );
}
/ *
* function decrements the semaphore and blocks if the semaphore is
* <= 0 until another thread signals a change.
*
* /
int
semaphore_down (Semaphore * s)
{
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex));
while (s-> v <= 0)
{
tw_pthread_cond_wait (& (s-> cond), & (s-> mutex) );
}
(s-> v)--;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ *
* function does NOT block but simply decrements the semaphore.
* should not be used instead of down-only for programs where
* multiple threads must up on a semaphore before another thread
* can go down, ie, allows programmer to set the semaphore to
* a negative value prior to using it for synchronization.
*
* /
int
semaphore_decrement (Semaphore * s)
(
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex)); s-> v--
;
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ *
* function returns the value of the semaphore at the time the
* critical section is accessed. obviously the value is not guarenteed
* after the function unlocks the critical section. provided only
* for casual debugging, a better approach is for the programmar to
* protect one semaphore with another and then check its value.
* an alternative is to simply record the value returned by semaphore_up
* or semaphore_down.
*
* /
int
semaphore_value (Semaphore * s)
{
/ * not for sync * /
int value_after_op;
tw_pthread_mutex_lock (& ββ(s-> mutex));
value_after_op = s-> v;
tw_pthread_mutex_unlock (& ββ(s-> mutex));
return (value_after_op);
}
/ * -------------------------------------- ------------------------------ * /
/ * The following functions replace standard library functions in that * /
/ * they exit on any error returned from the system calls. Saves us * /
/ * from having to check each and every call above. * /
/ * ---------------------- ---------------------------------------------- * /
int
tw_pthread_mutex_unlock (pthread_mutex_t * m)
{
int the return_value;
IF ((pthread_mutex_unlock the return_value = (m)) == -1)
do_error ( "pthread_mutex_unlock");
return (the return_value);
}
int
tw_pthread_mutex_lock (pthread_mutex_t * m)
{
int the return_value;
IF ((return_value = pthread_mutex_lock (m)) == -1)
do_error ("pthread_mutex_lock");
return (return_value);
}
int
tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m)
(
int return_value;
if ((return_value = pthread , m)) == -1)
do_error ("pthread_cond_wait");
return (return_value);
}
int
tw_pthread_cond_signal (pthread_cond_t * c)
{
int return_value;
if ((return_value = pthread_cond_signal (c)) == -1)
do_error ("pthread_cond_signal");
return (return_value);
}
/ *
* function just prints an error message and exits
*
* /
void
do_error (char * msg)
{
perror (msg);
exit (1);
}
[directory]
------------------------- -------------------------------------------------- ----
written by undercode
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
{
int the return_value;
IF ((return_value = pthread_mutex_lock (m)) == -1)
do_error ("pthread_mutex_lock");
return (return_value);
}
int
tw_pthread_cond_wait (pthread_cond_t * c, pthread_mutex_t * m)
(
int return_value;
if ((return_value = pthread , m)) == -1)
do_error ("pthread_cond_wait");
return (return_value);
}
int
tw_pthread_cond_signal (pthread_cond_t * c)
{
int return_value;
if ((return_value = pthread_cond_signal (c)) == -1)
do_error ("pthread_cond_signal");
return (return_value);
}
/ *
* function just prints an error message and exits
*
* /
void
do_error (char * msg)
{
perror (msg);
exit (1);
}
[directory]
------------------------- -------------------------------------------------- ----
written by undercode
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
π¦Programming Technology-Process and Thread Programming by undercode FULL
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
π¦FRESH Premium Proxies :
t.me/UndercodeTesting
47.75.90.57 80 1 hour ago
1320 ms 70% (46) hk Hong Kong - Central Elite -
82.119.170.106 8080 1 hour ago
1049 ms 96% (44) de Germany - Berlin Elite -
85.10.219.103 1080 1 hour ago
3400 ms 44% (53) de Germany Elite -
85.10.219.97 1080 1 hour ago
3500 ms 53% (44) de Germany Elite -
85.10.219.98 1080 1 hour ago
3363 ms 47% (54) de Germany Elite -
85.10.219.100 1080 1 hour ago
2823 ms 36% (59) de Germany Elite -
88.255.63.53 8080 1 hour ago
3939 ms 14% (43) tr Turkey Elite -
182.138.160.189 8118 1 hour ago
1885 ms 25% (4) cn China Elite -
188.40.183.188 1080 1 hour ago
3501 ms 43% (45) de Germany Elite -
202.138.241.166 8080 1 hour ago
4710 ms 18% (58) id Indonesia - Bandung Elite -
203.218.82.122 8080 1 hour ago
783 ms 13% (70) hk Hong Kong - Central Elite -
218.14.108.53 8060 1 hour ago
1222 ms 36% (54) cn China - Huizhou Elite -
218.75.102.198 8000 1 hour ago
990 ms 48% (61) cn China - Hangzhou Elite -
217.219.179.60 5220 1 hour ago
3735 ms 16% (59) ir Iran Elite -
113.252.95.19 8380 1 hour ago
773 ms 19% (59) hk Hong Kong - Kowloon Elite -
124.239.216.14 8060 1 hour ago
1694 ms 34% (50) cn China - Tianjin Elite -
128.199.71.230 8080 1 hour ago
1272 ms 94% (33) sg Singapore Elite -
148.251.153.6 1080 1 hour ago
3515 ms 40% (55) de Germany Elite -
159.138.22.112 443 1 hour ago
3268 ms 60% (52) hk Hong Kong Elite -
163.204.243.151 9999 1 hour ago
0 ms 0% (56) cn China Elite -
169.57.157.148 8123 1 hour ago
535 ms 98% (38) br Brazil - SΓ£o Paulo Elite -
173.192.128.238 25 1 hour ago
343 ms 98% (39) us United States - Seattle Elite -
61.54.225.130 8060 1 hour ago
2546 ms 22% (60) cn China - Anyang Elite -
80.187.140.26 80 1 hour ago
803 ms 84% (37) de Germany Elite -
78.108.66.26 3128 1 hour ago
3931 ms 20% (50) ru Russia - Kurgan Elite -
79.104.25.218 8080 1 hour ago
4197 ms 8% (66) ru Russia - Moscow Elite -
79.137.44.85 3129 1 hour ago
2405 ms 59% (51) es Spain - Madrid Elite -
81.93.4.11 81 1 hour ago
0 ms 0% (70) fr France - Saint-Gratien Elite -
101.4.136.34 8080 1 hour ago
1004 ms 49% (41) cn China Elite -
83.168.86.1 8090 1 hour ago
3218 ms 17% (56) pl Poland - Lobez Elite -
103.216.82.22 6666 1 hour ago
4001 ms 11% (64) in India - Ahmedabad Elite -
188.120.232.181 8118 1 hour ago
917 ms 25% (49) ru Russia Elite -
188.40.183.185 1080 1 hour ago
3436 ms 56% (49) de Germany Elite -
200.108.183.2 8080 1 hour ago
2031 ms 22% (57) uy Uruguay Elite -
218.27.204.240 8000 1 hour ago
3405 ms 42% (51) cn China Elite -
@UndercodeTesting
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
π¦FRESH Premium Proxies :
t.me/UndercodeTesting
47.75.90.57 80 1 hour ago
1320 ms 70% (46) hk Hong Kong - Central Elite -
82.119.170.106 8080 1 hour ago
1049 ms 96% (44) de Germany - Berlin Elite -
85.10.219.103 1080 1 hour ago
3400 ms 44% (53) de Germany Elite -
85.10.219.97 1080 1 hour ago
3500 ms 53% (44) de Germany Elite -
85.10.219.98 1080 1 hour ago
3363 ms 47% (54) de Germany Elite -
85.10.219.100 1080 1 hour ago
2823 ms 36% (59) de Germany Elite -
88.255.63.53 8080 1 hour ago
3939 ms 14% (43) tr Turkey Elite -
182.138.160.189 8118 1 hour ago
1885 ms 25% (4) cn China Elite -
188.40.183.188 1080 1 hour ago
3501 ms 43% (45) de Germany Elite -
202.138.241.166 8080 1 hour ago
4710 ms 18% (58) id Indonesia - Bandung Elite -
203.218.82.122 8080 1 hour ago
783 ms 13% (70) hk Hong Kong - Central Elite -
218.14.108.53 8060 1 hour ago
1222 ms 36% (54) cn China - Huizhou Elite -
218.75.102.198 8000 1 hour ago
990 ms 48% (61) cn China - Hangzhou Elite -
217.219.179.60 5220 1 hour ago
3735 ms 16% (59) ir Iran Elite -
113.252.95.19 8380 1 hour ago
773 ms 19% (59) hk Hong Kong - Kowloon Elite -
124.239.216.14 8060 1 hour ago
1694 ms 34% (50) cn China - Tianjin Elite -
128.199.71.230 8080 1 hour ago
1272 ms 94% (33) sg Singapore Elite -
148.251.153.6 1080 1 hour ago
3515 ms 40% (55) de Germany Elite -
159.138.22.112 443 1 hour ago
3268 ms 60% (52) hk Hong Kong Elite -
163.204.243.151 9999 1 hour ago
0 ms 0% (56) cn China Elite -
169.57.157.148 8123 1 hour ago
535 ms 98% (38) br Brazil - SΓ£o Paulo Elite -
173.192.128.238 25 1 hour ago
343 ms 98% (39) us United States - Seattle Elite -
61.54.225.130 8060 1 hour ago
2546 ms 22% (60) cn China - Anyang Elite -
80.187.140.26 80 1 hour ago
803 ms 84% (37) de Germany Elite -
78.108.66.26 3128 1 hour ago
3931 ms 20% (50) ru Russia - Kurgan Elite -
79.104.25.218 8080 1 hour ago
4197 ms 8% (66) ru Russia - Moscow Elite -
79.137.44.85 3129 1 hour ago
2405 ms 59% (51) es Spain - Madrid Elite -
81.93.4.11 81 1 hour ago
0 ms 0% (70) fr France - Saint-Gratien Elite -
101.4.136.34 8080 1 hour ago
1004 ms 49% (41) cn China Elite -
83.168.86.1 8090 1 hour ago
3218 ms 17% (56) pl Poland - Lobez Elite -
103.216.82.22 6666 1 hour ago
4001 ms 11% (64) in India - Ahmedabad Elite -
188.120.232.181 8118 1 hour ago
917 ms 25% (49) ru Russia Elite -
188.40.183.185 1080 1 hour ago
3436 ms 56% (49) de Germany Elite -
200.108.183.2 8080 1 hour ago
2031 ms 22% (57) uy Uruguay Elite -
218.27.204.240 8000 1 hour ago
3405 ms 42% (51) cn China Elite -
@UndercodeTesting
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
π¦Carding 2020 How to create free .edu email
π¦ Part 1:
> Step 1: Go to this https://www.apply.vccs.edu/Home/Sign_In/Logon.aspx and solve captcha. Then Click on new user and then sign up with email.
> Step 2: To fill the detail you can use your real name and email but if you are not an US citizen then you can use this (https://www.fakeaddressgenerator.com/usa_address_generator) to generate fake user detail. [For temporary email you can go https://emailfake.com/.] For Eg:
π¦Full Name Mary N Morey
Gender female
Title Mrs.
Race Black
Birthday 11/16/1957
Social Security Number 306-90-8491
π¦[Note: Save name, username and password in notepad or somewhere it may need later]
Step 3: After filling all the detail click on submit.
Now that you have created account lets move on to part two of this tutorial.
π¦Part 2:
After creating account and loging into click on Apply Now. Select any college name from the list and click on Apply. A pop-up will open then click on Apply Now (OR CONTINUE APPLICATION) BUTTON. Then you are asked for different questions. You can answer these questions randomly or you can use the detail below to answer these questions.
First name and last name: Put the name that you have entered in part 1. Leave other field empty.
Birthdate, Social Security Number: Enter details which was generated from this https://www.fakeaddressgenerator.com/usa_address_generator .
Gender: Male/Female (as you like)
Racial or ethnic identification: White
Hispanico or Latino : No
Have you ever applied, attended β¦β¦ : No
After that click on Save and Continue.
For Mailing address use the detail generated from this link.
Is this your permanent address: Yes
You can leave telephone number blank.
Then check on I have reviewed the guidelines box and click on save and continue.
Which β¦.. high school education: I donβt have a GED/High β¦β¦.
Last date attended: 01/2017
Highest grade completed: 11th grade.
Have you ever attend β¦. : No
I have planned to earn a degreeβ¦..: No
I plan to start class: Choose any
After that click on Save and Continue.
Have you ever served β¦β¦.. military: No
Are you β¦β¦.. military: No
After that click on Save and Continue.
Parent 1 and 2: Choose any.
What is your current status?: Native USβ¦β¦
Primary spoken language: English
Do you wantβ¦β¦: No
After that click on Submit your completed application.
π¦ Part 3.
> After successfully completing the application put on your signature i.e your full name. Then you will be redirect to with all your detail. Under the student information you will get your username and temporary password. Note down that password, username and other details.
> After that you can go> if link not open try later then works because this anonymously https://bit.ly/38wwKJc and login where you will get your .edu email.
> NOTE: It will take more than 6hours for the login credentials to activate. So you will get error that your username and password is invalid
WRITTEN BY UNDER CODE
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
π¦Carding 2020 How to create free .edu email
π¦ Part 1:
> Step 1: Go to this https://www.apply.vccs.edu/Home/Sign_In/Logon.aspx and solve captcha. Then Click on new user and then sign up with email.
> Step 2: To fill the detail you can use your real name and email but if you are not an US citizen then you can use this (https://www.fakeaddressgenerator.com/usa_address_generator) to generate fake user detail. [For temporary email you can go https://emailfake.com/.] For Eg:
π¦Full Name Mary N Morey
Gender female
Title Mrs.
Race Black
Birthday 11/16/1957
Social Security Number 306-90-8491
π¦[Note: Save name, username and password in notepad or somewhere it may need later]
Step 3: After filling all the detail click on submit.
Now that you have created account lets move on to part two of this tutorial.
π¦Part 2:
After creating account and loging into click on Apply Now. Select any college name from the list and click on Apply. A pop-up will open then click on Apply Now (OR CONTINUE APPLICATION) BUTTON. Then you are asked for different questions. You can answer these questions randomly or you can use the detail below to answer these questions.
First name and last name: Put the name that you have entered in part 1. Leave other field empty.
Birthdate, Social Security Number: Enter details which was generated from this https://www.fakeaddressgenerator.com/usa_address_generator .
Gender: Male/Female (as you like)
Racial or ethnic identification: White
Hispanico or Latino : No
Have you ever applied, attended β¦β¦ : No
After that click on Save and Continue.
For Mailing address use the detail generated from this link.
Is this your permanent address: Yes
You can leave telephone number blank.
Then check on I have reviewed the guidelines box and click on save and continue.
Which β¦.. high school education: I donβt have a GED/High β¦β¦.
Last date attended: 01/2017
Highest grade completed: 11th grade.
Have you ever attend β¦. : No
I have planned to earn a degreeβ¦..: No
I plan to start class: Choose any
After that click on Save and Continue.
Have you ever served β¦β¦.. military: No
Are you β¦β¦.. military: No
After that click on Save and Continue.
Parent 1 and 2: Choose any.
What is your current status?: Native USβ¦β¦
Primary spoken language: English
Do you wantβ¦β¦: No
After that click on Submit your completed application.
π¦ Part 3.
> After successfully completing the application put on your signature i.e your full name. Then you will be redirect to with all your detail. Under the student information you will get your username and temporary password. Note down that password, username and other details.
> After that you can go> if link not open try later then works because this anonymously https://bit.ly/38wwKJc and login where you will get your .edu email.
> NOTE: It will take more than 6hours for the login credentials to activate. So you will get error that your username and password is invalid
WRITTEN BY UNDER CODE
β β β ο½ππ»βΊπ«Δπ¬πβ β β β
www.apply.vccs.edu
Apply to Virginia's Community Colleges - Sign In
College advice, college admission applications, pay for college, financial aid, scholarships, apply online for admission, online applications at Xap.
π¦ About X100 VERIFIED NORDVPN PREMIUM , send screanshoats to @UNDERCODE_TESTING