Concise Linux System Programming Tutorial

Table of contents

01 Process thread concept

02 Process creation function

03 Process concurrency

04 Process monitoring

05 Create thread function

06 Multithreading and sharing between threads

07 Process communication - unnamed pipe

08 Measure the size of the nameless pipe

09 Example of anonymous pipe process communication

10 pipeline two-way transmission

11 famous pipes

12 shared memory


01 Process thread concept

program: (static concept)
        source code
        instructions

Process: (Dynamic concept)         The creation and destruction
        of a running program consumes a lot of resources. The process is independent.         Multiple processes can be a program         defined by the kernel as an abstract entity. Open up a series of resource                 internal data structures. The                 state                 information of the process         has an id number.

        





Task:
        specific things to be done
        by processes or threads

Why introduce threads?
        When a task is to be completed, the resource is destroyed by the kernel recycling process when the process is created by the kernel. This process wastes cpu resources very much and will bring a very large system consumption to the liunx system kernel.

Thread:
        Created by a process is faster than creating a process.
        Threads belong to the process. A process can have multiple threads that
        share the resources of the process.
        If a thread crashes, others will be affected.
        Creating and destroying threads consumes less resources.

A program can create multiple processes,
        and each process can do one thing.
Multiple processes can also call the same program,
        which is called resources.

Practice getting process pid

     pid = process id

View the user manual of getpid

man getpid 

GETPID(2)                   Linux Programmer's Manual                   GETPID(2)

NAME
       getpid, getppid - get process identification

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       pid_t getpid(void);
       pid_t getppid(void);

DESCRIPTION
       getpid()  returns  the  process ID (PID) of the calling process.  (This is
       often used by routines that generate unique temporary filenames.)

       getppid() returns the process ID of the parent  of  the  calling  process.
       This  will be either the ID of the process that created this process using
       fork(), or, if that process has already terminated, the ID of the  process
       to which this process has been reparented (either init(1) or a "subreaper"
       process defined via the prctl(2) PR_SET_CHILD_SUBREAPER operation).

ERRORS
       These functions are always successful.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, 4.3BSD, SVr4.

NOTES
       If the caller's parent is in a  different  PID  namespace  (see  pid_name‐
       spaces(7)), getppid() returns 0.

       From  a kernel perspective, the PID (which is shared by all of the threads
       in a multithreaded process) is sometimes also known as the thread group ID
       (TGID).   This  contrasts with the kernel thread ID (TID), which is unique
       for each thread.  For further details, see gettid(2) and the discussion of
       the CLONE_THREAD flag in clone(2).

   C library/kernel differences
       From glibc version 2.3.4 up to and including version 2.24, the glibc wrap‐
       per function for getpid() cached PIDs, with the  goal  of  avoiding  addi‐
       tional  system  calls  when a process calls getpid() repeatedly.  Normally
       this caching was invisible, but its correct operation relied on support in
       the  wrapper functions for fork(2), vfork(2), and clone(2): if an applica‐
       tion  bypassed  the  glibc  wrappers  for  these  system  calls  by  using
       syscall(2),  then  a  call to getpid() in the child would return the wrong
       value (to be precise: it would return the PID of the parent process).   In
       addition,  there  were  cases  where getpid() could return the wrong value
       even when invoking clone(2) via the glibc wrapper function.  (For  a  dis‐
       cussion  of  one  such case, see BUGS in clone(2).)  Furthermore, the com‐
       plexity of the caching code had been the source of a few bugs within glibc
       over the years.

       Because  of the aforementioned problems, since glibc version 2.25, the PID
       cache is removed: calls to getpid() always invoke the actual system  call,
       rather than returning a cached value.

       On Alpha, instead of a pair of getpid() and getppid() system calls, a sin‐
       gle getxpid() system call is provided, which returns a  pair  of  PID  and
       parent  PID.  The glibc getpid() and getppid() wrapper functions transpar‐
       ently deal with this.  See syscall(2) for details regarding register  map‐
       ping.

SEE ALSO
       clone(2),  fork(2),  gettid(2),  kill(2), exec(3), mkstemp(3), tempnam(3),
       tmpfile(3), tmpnam(3), credentials(7), pid_namespaces(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.   A  de‐
       scription of the project, information about reporting bugs, and the latest
       version      of      this      page,      can      be       found       at
       https://www.kernel.org/doc/man-pages/.

Linux                               2020-11-01                          GETPID(2)

getpid() current process
getppid() parent process

getpid code example 01.1

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>

int main(void)
{
	pid_t pid;
	while(1)
	{
		printf("pid=%d\n",getpid());
		printf("ppid=%d\n",getppid());
		printf("hello world\n");
		sleep(1);
	}
	return 0;
}

--gcc pid.c

--./a.out //Analysis result

 Open the terminal and run the command to view the number of processes

# pstree -p

Overall look at the process tree of a.out

Systemd(1)——systemd(2578)——gnome-terminal-(9474)——bash(9492)——a.out(19740)

systemd (1): the parent process of all processes

=init(1)(history)(one thing)

02 Process creation function

Use fork to create a process

See the manual man fork()

FORK(2)                                 Linux Programmer's Manual                                FORK(2)

NAME
       fork - create a child process

SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>

       pid_t fork(void);

DESCRIPTION
       fork()  creates a new process by duplicating the calling process.  The new process is referred to
       as the child process.  The calling process is referred to as the parent process.

       The child process and the parent process run in separate memory spaces.  At the  time  of  fork()
       both memory spaces have the same content.  Memory writes, file mappings (mmap(2)), and unmappings
       (munmap(2)) performed by one of the processes do not affect the other.

       The child process is an exact duplicate of the parent process except for the following points:

       *  The child has its own unique process ID, and this PID does not match the ID  of  any  existing
          process group (setpgid(2)) or session.

       *  The child's parent process ID is the same as the parent's process ID.

       *  The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).

       *  Process  resource  utilizations  (getrusage(2))  and CPU time counters (times(2)) are reset to
          zero in the child.

       *  The child's set of pending signals is initially empty (sigpending(2)).

       *  The child does not inherit semaphore adjustments from its parent (semop(2)).

       *  The child does not inherit process-associated record locks from its  parent  (fcntl(2)).   (On
          the  other  hand, it does inherit fcntl(2) open file description locks and flock(2) locks from
          its parent.)

       *  The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).

       *  The  child  does  not  inherit  outstanding  asynchronous  I/O  operations  from  its   parent
          (aio_read(3), aio_write(3)), nor does it inherit any asynchronous I/O contexts from its parent
          (see io_setup(2)).

       The process attributes in the preceding list are all specified in POSIX.1.  The parent and  child
       also differ with respect to the following Linux-specific process attributes:

       *  The  child  does not inherit directory change notifications (dnotify) from its parent (see the
          description of F_NOTIFY in fcntl(2)).

       *  The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does  not  receive  a  signal
          when its parent terminates.

       *  The  default  timer slack value is set to the parent's current timer slack value.  See the de‐
          scription of PR_SET_TIMERSLACK in prctl(2).

       *  Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited
          across a fork().

       *  Memory in address ranges that have been marked with the madvise(2) MADV_WIPEONFORK flag is ze‐
          roed in the child after a fork().  (The MADV_WIPEONFORK setting remains in place for those ad‐
          dress ranges in the child.)

       *  The termination signal of the child is always SIGCHLD (see clone(2)).

       *  The  port  access  permission  bits set by ioperm(2) are not inherited by the child; the child
          must turn on any bits that it requires using ioperm(2).

       Note the following further points:

       *  The child process is created with a single thread—the one that called fork().  The entire vir‐
          tual  address space of the parent is replicated in the child, including the states of mutexes,
          condition variables, and other pthreads objects; the use of pthread_atfork(3) may  be  helpful
          for dealing with problems that this can cause.

       *  After  a  fork()  in a multithreaded program, the child can safely call only async-signal-safe
          functions (see signal-safety(7)) until such time as it calls execve(2).

       *  The child inherits copies of the parent's set of open file descriptors.  Each file  descriptor
          in  the child refers to the same open file description (see open(2)) as the corresponding file
          descriptor in the parent.  This means that the two file descriptors  share  open  file  status
          flags,  file  offset,  and  signal-driven  I/O attributes (see the description of F_SETOWN and
          F_SETSIG in fcntl(2)).

       *  The child inherits copies of the parent's set of open message queue descriptors (see  mq_over‐
          view(7)).  Each file descriptor in the child refers to the same open message queue description
          as the corresponding file descriptor in the parent.  This means that the two file  descriptors
          share the same flags (mq_flags).

       *  The  child  inherits  copies  of  the parent's set of open directory streams (see opendir(3)).
          POSIX.1 says that the corresponding directory streams in the parent and child  may  share  the
          directory stream positioning; on Linux/glibc they do not.

RETURN VALUE
       On  success,  the  PID  of  the child process is returned in the parent, and 0 is returned in the
       child.  On failure, -1 is returned in the parent, no child process is created, and errno  is  set
       appropriately.

ERRORS
       EAGAIN A  system-imposed  limit  on the number of threads was encountered.  There are a number of
              limits that may trigger this error:

              *  the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of
                 processes and threads for a real user ID, was reached;

              *  the  kernel's  system-wide limit on the number of processes and threads, /proc/sys/ker‐
                 nel/threads-max, was reached (see proc(5));

              *  the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or

              *  the PID limit (pids.max) imposed by the cgroup "process number" (PIDs)  controller  was
                 reached.

       EAGAIN The  caller  is operating under the SCHED_DEADLINE scheduling policy and does not have the
              reset-on-fork flag set.  See sched(7).

       ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.

       ENOMEM An attempt was made to create a child process in a PID namespace whose "init" process  has
              terminated.  See pid_namespaces(7).

       ENOSYS fork()  is  not supported on this platform (for example, hardware without a Memory-Manage‐
              ment Unit).

       ERESTARTNOINTR (since Linux 2.6.17)
              System call was interrupted by a signal and will be restarted.  (This  can  be  seen  only
              during a trace.)

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4, 4.3BSD.

NOTES
       Under  Linux, fork() is implemented using copy-on-write pages, so the only penalty that it incurs
       is the time and memory required to duplicate the parent's page tables, and  to  create  a  unique
       task structure for the child.

   C library/kernel differences
       Since version 2.3.3, rather than invoking the kernel's fork() system call, the glibc fork() wrap‐
       per that is provided as part of the NPTL threading implementation  invokes  clone(2)  with  flags
       that  provide the same effect as the traditional system call.  (A call to fork() is equivalent to
       a call to clone(2) specifying flags as just SIGCHLD.)  The glibc wrapper invokes  any  fork  han‐
       dlers that have been established using pthread_atfork(3).

EXAMPLES
       See pipe(2) and wait(2).

SEE ALSO
       clone(2), execve(2), exit(2), setrlimit(2), unshare(2), vfork(2), wait(2), daemon(3), pthread_at‐
       fork(3), capabilities(7), credentials(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.  A description of the  project,
       information  about  reporting  bugs,  and  the  latest  version  of  this  page,  can be found at
       https://www.kernel.org/doc/man-pages/.

Linux                                          2020-06-09                                        FORK(2)

fork code example 02.1

#include<sys/types.h>
#include<unistd.h>
#include<stdio.h>
int main(void)
{
    pid_t pid;
    pid=fork();
    printf("pid=%d\n",pid);
    printf("hello world\n");
    return 0;
}

 fork code example 02.2

#include<sys/types.h>
#include<unistd.h>
#include <stdio.h>
int main(void)
{
		pid_t pid1,pid2;
		pid1=fork();
		pid2=fork();
		printf("pid1=%d,pid2=%d\n",pid1,pid2);
        while(1);
	return 0;
}

output

:~/Desktop# ./a.out

pid1=24387B , pid2= 24388C

pid1=24387B,pid2=0

pid1=0,pid2=24389D

pid1=0,pid2=0

Analyze the flow of program execution 

 Join the loop to call up the process tree pstree -p

 

   It can be analyzed that the process id is probably executed in this way. However, the processes are independent and do not necessarily execute first.

03 Process concurrency

Why use multiple processes?
        I hope that the parent and child processes (independent split) execute different content

Concurrent Execution Code Example 03.1

#include<sys/types.h>
#include<unistd.h>
#include <stdio.h>
int main(void)
{
       pid_t pid;
        pid=fork();
        if(pid>0){ //父进程
                while(1){
                        printf("hello world\n");
                        sleep(1);
                }
        }
        else if(pid==0){
                while(1){
                        printf("good moring\n");
                        sleep(1);
                }
        }
        else{
                printf("err\n");
        }
        return 0;
}

Hello world and good moring are executed in parallel

 It is not possible to judge who executes first based on the output

Micro: By default (kernel) the parent process first calls
        the parent process in the process of creating a child process is being executed

The end of the parent process will not affect the execution of the child process?

        status impact

 State Impact Code Example 03.2

#include<sys/types.h>
#include<unistd.h>
#include <stdio.h>
int main(void)
{
       pid_t pid;
int count = 0;
        pid=fork();
        if(pid>0){ //父进程
        for(int i=0;i<10;i++){
        
                        printf("hello world count=%d\n",count++);
                        sleep(1);}
                   }
        else if(pid==0){
                while(1){
                        printf("good moring count=%d\n",count++);
                        sleep(1);
                }
        }
        else{
                printf("err\n");
        }
        return 0;
}

output

 It can be seen that the end of the parent process does not affect the execution status of the child process.

...........................................................................................................................................................

Is there any means! Process synchronization monitors the status of child processes...

How to influence! Communication and synchronization between tasks...

04 Process monitoring

The parent process monitors the state of the child process for changes (end; stop;)

man wait

WAIT(1POSIX)                          POSIX Programmer's Manual                          WAIT(1POSIX)

PROLOG
       This  manual  page is part of the POSIX Programmer's Manual.  The Linux implementation of this
       interface may differ (consult the corresponding Linux manual page for details of Linux  behav‐
       ior), or the interface may not be implemented on Linux.

NAME
       wait — await process completion

SYNOPSIS
       wait [pid...]

DESCRIPTION
       When an asynchronous list (see Section 2.9.3.1, Examples) is started by the shell, the process
       ID of the last command in each element of the asynchronous list shall become known in the cur‐
       rent shell execution environment; see Section 2.12, Shell Execution Environment.

       If  the wait utility is invoked with no operands, it shall wait until all process IDs known to
       the invoking shell have terminated and exit with a zero exit status.

       If one or more pid operands are specified that represent known process IDs, the  wait  utility
       shall  wait  until all of them have terminated. If one or more pid operands are specified that
       represent unknown process IDs, wait shall treat them as if they were known  process  IDs  that
       exited  with  exit  status 127. The exit status returned by the wait utility shall be the exit
       status of the process requested by the last pid operand.

       The known process IDs are applicable only for invocations of wait in the current shell  execu‐
       tion environment.

OPTIONS
       None.

OPERANDS
       The following operand shall be supported:

       pid       One of the following:

                  1. The  unsigned  decimal integer process ID of a command, for which the utility is
                     to wait for the termination.

                  2. A job control job ID (see the Base Definitions volume of  POSIX.1‐2017,  Section
                     3.204,  Job  Control  Job  ID)  that identifies a background process group to be
                     waited for. The job control job ID notation is applicable only  for  invocations
                     of wait in the current shell execution environment; see Section 2.12, Shell Exe‐
                     cution Environment.  The exit status of wait shall be  determined  by  the  last
                     command in the pipeline.

                     Note:     The  job  control job ID type of pid is only available on systems sup‐
                               porting the User Portability Utilities option.

STDIN
       Not used.

INPUT FILES
       None.

ENVIRONMENT VARIABLES
       The following environment variables shall affect the execution of wait:

       LANG      Provide a default value for the internationalization variables  that  are  unset  or
                 null.  (See the Base Definitions volume of POSIX.1‐2017, Section 8.2, International‐
                 ization Variables for the precedence of internationalization variables used  to  de‐
                 termine the values of locale categories.)

       LC_ALL    If  set  to  a non-empty string value, override the values of all the other interna‐
                 tionalization variables.

       LC_CTYPE  Determine the locale for the interpretation of sequences of bytes of  text  data  as
                 characters  (for  example,  single-byte as opposed to multi-byte characters in argu‐
                 ments).

       LC_MESSAGES
                 Determine the locale that should be used to affect the format and contents of  diag‐
                 nostic messages written to standard error.

       NLSPATH   Determine the location of message catalogs for the processing of LC_MESSAGES.

ASYNCHRONOUS EVENTS
       Default.

STDOUT
       Not used.

STDERR
       The standard error shall be used only for diagnostic messages.

OUTPUT FILES
       None.

EXTENDED DESCRIPTION
       None.

EXIT STATUS
       If  one  or more operands were specified, all of them have terminated or were not known by the
       invoking shell, and the status of the last operand specified is known, then the exit status of
       wait  shall be the exit status information of the command indicated by the last operand speci‐
       fied. If the process terminated abnormally due to the receipt of a  signal,  the  exit  status
       shall  be  greater than 128 and shall be distinct from the exit status generated by other sig‐
       nals, but the exact value is unspecified. (See the kill -l option.) Otherwise, the wait  util‐
       ity shall exit with one of the following values:

           0   The  wait utility was invoked with no operands and all process IDs known by the invok‐
               ing shell have terminated.

       1‐126   The wait utility detected an error.

         127   The command identified by the last pid operand specified is unknown.

CONSEQUENCES OF ERRORS
       Default.

       The following sections are informative.

APPLICATION USAGE
       On most implementations, wait is a shell built-in. If it is called in a subshell  or  separate
       utility execution environment, such as one of the following:

           (wait)
           nohup wait ...
           find . -exec wait ... \;

       it  returns  immediately  because there are no known process IDs to wait for in those environ‐
       ments.

       Historical implementations of interactive shells have discarded the exit status of  terminated
       background  processes  before each shell prompt. Therefore, the status of background processes
       was usually lost unless it terminated while wait was waiting for it. This could be  a  serious
       problem when a job that was expected to run for a long time actually terminated quickly with a
       syntax or initialization error because the exit status returned was usually zero  if  the  re‐
       quested  process  ID was not found. This volume of POSIX.1‐2017 requires the implementation to
       keep the status of terminated jobs available until the status is requested,  so  that  scripts
       like:

           j1&
           p1=$!
           j2&
           wait $p1
           echo Job 1 exited with status $?
           wait $!
           echo Job 2 exited with status $?

       work  without  losing status on any of the jobs. The shell is allowed to discard the status of
       any process if it determines that the application cannot get the process ID for  that  process
       from  the  shell. It is also required to remember only {CHILD_MAX} number of processes in this
       way. Since the only way to get the process ID from the shell is by using the '!'  shell param‐
       eter,  the shell is allowed to discard the status of an asynchronous list if "$!" was not ref‐
       erenced before another asynchronous list was started. (This means that the shell only  has  to
       keep  the  status  of  the last asynchronous list started if the application did not reference
       "$!".  If the implementation of the shell is smart enough to determine  that  a  reference  to
       "$!" was not saved anywhere that the application can retrieve it later, it can use this infor‐
       mation to trim the list of saved information. Note also that a successful call to wait with no
       operands discards the exit status of all asynchronous lists.)

       If the exit status of wait is greater than 128, there is no way for the application to know if
       the waited-for process exited with that value or was killed by a signal.  Since most utilities
       exit  with  small values, there is seldom any ambiguity. Even in the ambiguous cases, most ap‐
       plications just need to know that the asynchronous job failed; it does not matter  whether  it
       detected an error and failed or was killed and did not complete its job normally.

EXAMPLES
       Although  the  exact value used when a process is terminated by a signal is unspecified, if it
       is known that a signal terminated a process, a script can still reliably determine which  sig‐
       nal by using kill as shown by the following script:

           sleep 1000&
           pid=$!
           kill -kill $pid
           wait $pid
           echo $pid was terminated by a SIG$(kill -l $?) signal.

       If the following sequence of commands is run in less than 31 seconds:

           sleep 257 | sleep 31 &
           jobs -l %%

       either of the following commands returns the exit status of the second sleep in the pipeline:

           wait <pid of sleep 31>
           wait %%

RATIONALE
       The  description  of  wait does not refer to the waitpid() function from the System Interfaces
       volume of POSIX.1‐2017 because that would needlessly overspecify this interface. However,  the
       wording  means that wait is required to wait for an explicit process when it is given an argu‐
       ment so that the status information of other processes is not consumed. Historical implementa‐
       tions  use  the  wait() function defined in the System Interfaces volume of POSIX.1‐2017 until
       wait() returns the requested process ID or finds that the requested process  does  not  exist.
       Because  this  means  that  a shell script could not reliably get the status of all background
       children if a second background job was ever started before the first job finished, it is rec‐
       ommended  that  the  wait utility use a method such as the functionality provided by the wait‐
       pid() function.

       The ability to wait for multiple pid operands was adopted from the KornShell.

       This new functionality was added because it is needed to determine  the  exit  status  of  any
       asynchronous list accurately. The only compatibility problem that this change creates is for a
       script like

           while sleep 60 do
               job& echo Job started $(date) as $!  done

       which causes the shell to monitor all of the jobs started until the script terminates or  runs
       out of memory. This would not be a problem if the loop did not reference "$!" or if the script
       would occasionally wait for jobs it started.

FUTURE DIRECTIONS
       None.

SEE ALSO
       Chapter 2, Shell Command Language, kill, sh

       The Base Definitions volume of POSIX.1‐2017, Section 3.204, Job Control Job ID, Chapter 8, En‐
       vironment Variables

       The System Interfaces volume of POSIX.1‐2017, wait()

COPYRIGHT
       Portions  of  this  text  are  reprinted  and  reproduced  in  electronic  form  from IEEE Std
       1003.1-2017, Standard for  Information  Technology  --  Portable  Operating  System  Interface
       (POSIX),  The  Open Group Base Specifications Issue 7, 2018 Edition, Copyright (C) 2018 by the
       Institute of Electrical and Electronics Engineers, Inc and The Open Group.  In  the  event  of
       any  discrepancy  between  this version and the original IEEE and The Open Group Standard, the
       original IEEE and The Open Group Standard is the referee document. The original  Standard  can
       be obtained online at http://www.opengroup.org/unix/online.html .

       Any  typographical  or formatting errors that appear in this page are most likely to have been
       introduced during the conversion of the source files to man page format. To  report  such  er‐
       rors, see https://www.kernel.org/doc/man-pages/reporting_bugs.html .

IEEE/The Open Group                              2017                                    WAIT(1POSIX)

 wait code example 04.1 Pass in parameters to let the child process delay all child processes after the delay is completed, and the parent process ends

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

//run model: ./a.out 10 5 15  (three child process, after 10, 5, 15seconds, they are over)
int main(int argc, char *argv[])
{
	pid_t child_pid;
	int numDead;
	int i;

	for(i = 1; i < argc; i++)
	{
		switch(fork())
		{
			case -1:
				perror("fork()");
				exit(0);
			case 0:
				printf("Child %d started with PID = %d, sleeping %s seconds\n", i, getpid(), argv[i]);
				sleep(atoi(argv[i]));
				exit(0);
			default:
				break;
		}
	}

	numDead = 0;

	while(1)
	{
		child_pid = wait(NULL);

		if(child_pid == -1)
		{
			printf("No more children, Byebye!\n");
			exit(0);
		}

		numDead++;
		printf("wait() returned child PID : %d(numDead = %d)\n", child_pid, numDead);
	}
}

output 

05 Create thread function

Review the concept of thread process

Process: (Dynamic concept) (Running program)
        creation and destruction consumes a lot of resources. Processes
        are independent.
        Multiple processes can be an
        abstract entity defined by the kernel to open up a series of resources
        with id numbers.

Thread: The process
        created by the process
        and the end of the thread will also end.
        The thread is subordinate to the process, and a process can have multiple threads.
        The resources of the process are shared between the threads.
        If one crashes, the others will be affected.
        Creating and destroying consumes less resources.

The thread creation function pthread_create receives four parameters

int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start rourtine)(void),void *arg); pthread_t *thread
        output type parameter  
        const pthread_attr_t *attr structure pointer to place thread attribute
        void *(*start rourtine)(void) pointer function return pointer to receive parameter pointer is actually connected to the content of thread execution
        void *arg pass the parameters of the current thread

If the function executes successfully, it returns 0; if it fails, it returns error number

Check the manual man pthread_cread

PTHREAD_CREATE(3)                        Linux Programmer's Manual                       PTHREAD_CREATE(3)

NAME
       pthread_create - create a new thread

SYNOPSIS
       #include <pthread.h>

       int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_create()  function  starts a new thread in the calling process.  The new thread starts
       execution by invoking start_routine(); arg is passed as the sole argument of start_routine().

       The new thread terminates in one of the following ways:

       * It calls pthread_exit(3), specifying an exit status value that is available to another thread  in
         the same process that calls pthread_join(3).

       * It  returns  from  start_routine().  This is equivalent to calling pthread_exit(3) with the value
         supplied in the return statement.

       * It is canceled (see pthread_cancel(3)).

       * Any of the threads in the process calls exit(3), or  the  main  thread  performs  a  return  from
         main().  This causes the termination of all threads in the process.

       The  attr  argument points to a pthread_attr_t structure whose contents are used at thread creation
       time  to  determine  attributes  for  the  new  thread;  this  structure   is   initialized   using
       pthread_attr_init(3)  and  related functions.  If attr is NULL, then the thread is created with de‐
       fault attributes.

       Before returning, a successful call to pthread_create() stores the ID of the new thread in the buf‐
       fer  pointed  to  by  thread; this identifier is used to refer to the thread in subsequent calls to
       other pthreads functions.

       The new thread inherits a copy of the creating thread's signal mask (pthread_sigmask(3)).  The  set
       of  pending  signals  for the new thread is empty (sigpending(2)).  The new thread does not inherit
       the creating thread's alternate signal stack (sigaltstack(2)).

       The new thread inherits the calling thread's floating-point environment (fenv(3)).

       The initial value of the new thread's CPU-time clock is 0 (see pthread_getcpuclockid(3)).

   Linux-specific details
       The new thread inherits copies of the calling thread's capability sets  (see  capabilities(7))  and
       CPU affinity mask (see sched_setaffinity(2)).

RETURN VALUE
       On  success,  pthread_create() returns 0; on error, it returns an error number, and the contents of
       *thread are undefined.

ERRORS
       EAGAIN Insufficient resources to create another thread.

       EAGAIN A system-imposed limit on the number of threads was encountered.  There are a number of lim‐
              its  that  may  trigger  this  error:  the  RLIMIT_NPROC  soft resource limit (set via setr‐
              limit(2)), which limits the number of processes and threads for a real user ID, was reached;
              the  kernel's  system-wide  limit  on  the  number  of processes and threads, /proc/sys/ker‐
              nel/threads-max, was reached (see proc(5)); or the maximum number  of  PIDs,  /proc/sys/ker‐
              nel/pid_max, was reached (see proc(5)).

       EINVAL Invalid settings in attr.

       EPERM  No permission to set the scheduling policy and parameters specified in attr.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────┬───────────────┬─────────┐
       │Interface        │ Attribute     │ Value   │
       ├─────────────────┼───────────────┼─────────┤
       │pthread_create() │ Thread safety │ MT-Safe │
       └─────────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       See  pthread_self(3)  for  further information on the thread ID returned in *thread by pthread_cre‐
       ate().  Unless real-time scheduling policies are being employed, after a call to  pthread_create(),
       it is indeterminate which thread—the caller or the new thread—will next execute.

       A thread may either be joinable or detached.  If a thread is joinable, then another thread can call
       pthread_join(3) to wait for the thread to terminate and fetch its exit status.  Only when a  termi‐
       nated  joinable  thread  has been joined are the last of its resources released back to the system.
       When a detached thread terminates, its resources are automatically released back to the system:  it
       is  not  possible  to join with the thread in order to obtain its exit status.  Making a thread de‐
       tached is useful for some types of daemon threads whose exit status the application does  not  need
       to  care  about.   By  default, a new thread is created in a joinable state, unless attr was set to
       create the thread in a detached state (using pthread_attr_setdetachstate(3)).

       Under the NPTL threading implementation, if the RLIMIT_STACK soft resource limit at  the  time  the
       program  started has any value other than "unlimited", then it determines the default stack size of
       new threads.  Using pthread_attr_setstacksize(3), the stack size attribute can be explicitly set in
       the  attr argument used to create a thread, in order to obtain a stack size other than the default.
       If the RLIMIT_STACK resource limit is set to "unlimited", a per-architecture value is used for  the
       stack size.  Here is the value for a few architectures:

              ┌─────────────┬────────────────────┐
              │Architecture │ Default stack size │
              ├─────────────┼────────────────────┤
              │i386         │               2 MB │
              ├─────────────┼────────────────────┤
              │IA-64        │              32 MB │
              ├─────────────┼────────────────────┤
              │PowerPC      │               4 MB │
              ├─────────────┼────────────────────┤
              │S/390        │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-32     │               2 MB │
              ├─────────────┼────────────────────┤
              │Sparc-64     │               4 MB │
              ├─────────────┼────────────────────┤
              │x86_64       │               2 MB │
              └─────────────┴────────────────────┘
BUGS
       In  the  obsolete  LinuxThreads  implementation,  each  of the threads in a process has a different
       process ID.  This is in violation of the POSIX threads specification, and is  the  source  of  many
       other nonconformances to the standard; see pthreads(7).

EXAMPLES
       The  program below demonstrates the use of pthread_create(), as well as a number of other functions
       in the pthreads API.

       In the following run, on a system providing the NPTL threading implementation, the stack  size  de‐
       faults to the value given by the "stack size" resource limit:

           $ ulimit -s
           8192            # The stack size limit is 8 MB (0x800000 bytes)
           $ ./a.out hola salut servus
           Thread 1: top of stack near 0xb7dd03b8; argv_string=hola
           Thread 2: top of stack near 0xb75cf3b8; argv_string=salut
           Thread 3: top of stack near 0xb6dce3b8; argv_string=servus
           Joined with thread 1; returned value was HOLA
           Joined with thread 2; returned value was SALUT
           Joined with thread 3; returned value was SERVUS

       In  the  next  run,  the program explicitly sets a stack size of 1 MB (using pthread_attr_setstack‐
       size(3)) for the created threads:

           $ ./a.out -s 0x100000 hola salut servus
           Thread 1: top of stack near 0xb7d723b8; argv_string=hola
           Thread 2: top of stack near 0xb7c713b8; argv_string=salut
           Thread 3: top of stack near 0xb7b703b8; argv_string=servus
           Joined with thread 1; returned value was HOLA
           Joined with thread 2; returned value was SALUT
           Joined with thread 3; returned value was SERVUS

   Program source

       #include <pthread.h>
       #include <string.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <errno.h>
       #include <ctype.h>

       #define handle_error_en(en, msg) \
               do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

       #define handle_error(msg) \
               do { perror(msg); exit(EXIT_FAILURE); } while (0)

       struct thread_info {    /* Used as argument to thread_start() */
           pthread_t thread_id;        /* ID returned by pthread_create() */
           int       thread_num;       /* Application-defined thread # */
           char     *argv_string;      /* From command-line argument */
       };

       /* Thread start function: display address near top of our stack,
          and return upper-cased copy of argv_string */

       static void *
       thread_start(void *arg)
       {
           struct thread_info *tinfo = arg;
           char *uargv;

           printf("Thread %d: top of stack near %p; argv_string=%s\n",
                   tinfo->thread_num, &p, tinfo->argv_string);

           uargv = strdup(tinfo->argv_string);
           if (uargv == NULL)
               handle_error("strdup");

           for (char *p = uargv; *p != '\0'; p++)
               *p = toupper(*p);

           return uargv;
       }

       int
       main(int argc, char *argv[])
       {
           int s, opt, num_threads;
           pthread_attr_t attr;
           size_t stack_size;
           void *res;

           /* The "-s" option specifies a stack size for our threads */

           stack_size = -1;
           while ((opt = getopt(argc, argv, "s:")) != -1) {
               switch (opt) {
               case 's':
                   stack_size = strtoul(optarg, NULL, 0);
                   break;

               default:
                   fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                           argv[0]);
                   exit(EXIT_FAILURE);
               }
           }

           num_threads = argc - optind;

           /* Initialize thread creation attributes */

           s = pthread_attr_init(&attr);
           if (s != 0)
               handle_error_en(s, "pthread_attr_init");

           if (stack_size > 0) {
               s = pthread_attr_setstacksize(&attr, stack_size);
               if (s != 0)
                   handle_error_en(s, "pthread_attr_setstacksize");
           }

           /* Allocate memory for pthread_create() arguments */

           struct thread_info *tinfo = calloc(num_threads, sizeof(*tinfo));
           if (tinfo == NULL)
               handle_error("calloc");

           /* Create one thread for each command-line argument */

           for (int tnum = 0; tnum < num_threads; tnum++) {
               tinfo[tnum].thread_num = tnum + 1;
               tinfo[tnum].argv_string = argv[optind + tnum];

               /* The pthread_create() call stores the thread ID into
                  corresponding element of tinfo[] */

               s = pthread_create(&tinfo[tnum].thread_id, &attr,
                                  &thread_start, &tinfo[tnum]);
               if (s != 0)
                   handle_error_en(s, "pthread_create");
           }

           /* Destroy the thread attributes object, since it is no
              longer needed */

           s = pthread_attr_destroy(&attr);
           if (s != 0)
               handle_error_en(s, "pthread_attr_destroy");

           /* Now join with each thread, and display its returned value */

           for (int tnum = 0; tnum < num_threads; tnum++) {
               s = pthread_join(tinfo[tnum].thread_id, &res);
               if (s != 0)
                   handle_error_en(s, "pthread_join");

               printf("Joined with thread %d; returned value was %s\n",
                       tinfo[tnum].thread_num, (char *) res);
               free(res);      /* Free memory allocated by thread */
           }

           free(tinfo);
           exit(EXIT_SUCCESS);
       }

SEE ALSO
       getrlimit(2), pthread_attr_init(3), pthread_cancel(3), pthread_detach(3), pthread_equal(3),
       pthread_exit(3), pthread_getattr_np(3), pthread_join(3), pthread_self(3),
       pthread_setattr_default_np(3), pthreads(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.  A description of the project,
       information about reporting bugs, and the latest version of this page, can be found at
       https://www.kernel.org/doc/man-pages/.

Linux                                           2020-11-01                               PTHREAD_CREATE(3)

The process waits for the thread function pthread_join

man pthread_join

PTHREAD_JOIN(3)                                Linux Programmer's Manual                               PTHREAD_JOIN(3)

NAME
       pthread_join - join with a terminated thread

SYNOPSIS
       #include <pthread.h>

       int pthread_join(pthread_t thread, void **retval);

       Compile and link with -pthread.

DESCRIPTION
       The  pthread_join() function waits for the thread specified by thread to terminate.  If that thread has already
       terminated, then pthread_join() returns immediately.  The thread specified by thread must be joinable.

       If retval is not NULL, then pthread_join() copies the exit status of the target thread (i.e.,  the  value  that
       the  target  thread  supplied to pthread_exit(3)) into the location pointed to by retval.  If the target thread
       was canceled, then PTHREAD_CANCELED is placed in the location pointed to by retval.

       If multiple threads simultaneously try to join with the same thread, the results are undefined.  If the  thread
       calling  pthread_join()  is  canceled,  then  the  target thread will remain joinable (i.e., it will not be de‐
       tached).

RETURN VALUE
       On success, pthread_join() returns 0; on error, it returns an error number.

ERRORS
       EDEADLK
              A deadlock was detected (e.g., two threads tried to join with each other); or thread specifies the call‐
              ing thread.

       EINVAL thread is not a joinable thread.

       EINVAL Another thread is already waiting to join with this thread.

       ESRCH  No thread with the ID thread could be found.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌───────────────┬───────────────┬─────────┐
       │Interface      │ Attribute     │ Value   │
       ├───────────────┼───────────────┼─────────┤
       │pthread_join() │ Thread safety │ MT-Safe │
       └───────────────┴───────────────┴─────────┘

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008.

NOTES
       After a successful call to pthread_join(), the caller is guaranteed that the target thread has terminated.  The
       caller may then choose to do any clean-up that is required after termination of the thread (e.g., freeing  mem‐
       ory or other resources that were allocated to the target thread).

       Joining with a thread that has previously been joined results in undefined behavior.

       Failure  to  join  with a thread that is joinable (i.e., one that is not detached), produces a "zombie thread".
       Avoid doing this, since each zombie thread consumes some system resources, and when enough zombie threads  have
       accumulated, it will no longer be possible to create new threads (or processes).

       There is no pthreads analog of waitpid(-1, &status, 0), that is, "join with any terminated thread".  If you be‐
       lieve you need this functionality, you probably need to rethink your application design.

       All of the threads in a process are peers: any thread can join with any other thread in the process.

EXAMPLES
       See pthread_create(3).

SEE ALSO
       pthread_cancel(3), pthread_create(3), pthread_detach(3), pthread_exit(3), pthread_tryjoin_np(3), pthreads(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.  A description of  the  project,  information
       about    reporting    bugs,    and    the    latest    version    of    this    page,    can    be   found   at
       https://www.kernel.org/doc/man-pages/.

Linux                                                 2020-06-09                                       PTHREAD_JOIN(3)

Create Thread Code Example 05.1

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void *thread_function(void *arg);

int main(void)
{
	pthread_t pthread;
	int ret;
	int count = 8;

	ret = pthread_create(&pthread, NULL, thread_function, &count);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	pthread_join(pthread, NULL);
	printf("The thread is over, process is over too.\n");

	return 0;
}

void *thread_function(void *arg)
{
	int i;
	printf("Thread begins running\n");

	for(i = 0; i < *(int *)arg; i++)
	{
		printf("Hello world\n");
		sleep(1);
	}
	return NULL;
}

 

06 Multithreading and sharing between threads

The method of creating multiple threads is the same as the method of creating a thread

Code Example 06.1

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void *thread1_function(void *arg);
void *thread2_function(void *arg);

int count = 0;//线程可见

int main(void)
{
	pthread_t pthread1, pthread2;
	int ret;

	ret = pthread_create(&pthread1, NULL, thread1_function, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	ret = pthread_create(&pthread2, NULL, thread2_function, NULL);
	if(ret != 0)
	{
		perror("pthread_create");
		exit(1);
	}

	pthread_join(pthread1, NULL);
	pthread_join(pthread2, NULL);
	printf("The thread is over, process is over too.\n");

	return 0;
}

void *thread1_function(void *arg)
{
	printf("Thread1 begins running\n");

	while(1)
	{
		printf("Thread1 count = %d\n", count++);
		sleep(1);
	}
	return NULL;
}

void *thread2_function(void *arg)
{
	printf("Thread2 begins running\n");
	while(1)
	{
		printf("Thread2 count = %d\n", count++);
		sleep(1);
	}
	return NULL;
}

 

07 Process communication - unnamed pipe

Inter-process communication means

pipe
        named pipe
        unnamed pipe one-way

man pipe

PIPE(2)                                        Linux Programmer's Manual                                       PIPE(2)

NAME
       pipe, pipe2 - create pipe

SYNOPSIS
       #include <unistd.h>

       /* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */
       struct fd_pair {
           long fd[2];
       };
       struct fd_pair pipe();

       /* On all other architectures */
       int pipe(int pipefd[2]);

       #define _GNU_SOURCE             /* See feature_test_macros(7) */
       #include <fcntl.h>              /* Obtain O_* constant definitions */
       #include <unistd.h>

       int pipe2(int pipefd[2], int flags);

DESCRIPTION
       pipe()  creates a pipe, a unidirectional data channel that can be used for interprocess communication.  The ar‐
       ray pipefd is used to return two file descriptors referring to the ends of the pipe.  pipefd[0] refers  to  the
       read  end  of  the  pipe.  pipefd[1] refers to the write end of the pipe.  Data written to the write end of the
       pipe is buffered by the kernel until it is read from the read end  of  the  pipe.   For  further  details,  see
       pipe(7).

       If flags is 0, then pipe2() is the same as pipe().  The following values can be bitwise ORed in flags to obtain
       different behavior:

       O_CLOEXEC
              Set the close-on-exec (FD_CLOEXEC) flag on the two new file descriptors.  See  the  description  of  the
              same flag in open(2) for reasons why this may be useful.

       O_DIRECT (since Linux 3.4)
              Create a pipe that performs I/O in "packet" mode.  Each write(2) to the pipe is dealt with as a separate
              packet, and read(2)s from the pipe will read one packet at a time.  Note the following points:

              *  Writes of greater than PIPE_BUF bytes (see pipe(7)) will be split into multiple  packets.   The  con‐
                 stant PIPE_BUF is defined in <limits.h>.

              *  If  a read(2) specifies a buffer size that is smaller than the next packet, then the requested number
                 of bytes are read, and the excess bytes in the packet are discarded.  Specifying  a  buffer  size  of
                 PIPE_BUF will be sufficient to read the largest possible packets (see the previous point).

              *  Zero-length  packets  are not supported.  (A read(2) that specifies a buffer size of zero is a no-op,
                 and returns 0.)

              Older kernels that do not support this flag will indicate this via an EINVAL error.

              Since Linux 4.5, it is possible to change the O_DIRECT setting of a pipe file descriptor using fcntl(2).

       O_NONBLOCK
              Set the O_NONBLOCK file status flag on the open file descriptions referred to by the new  file  descrip‐
              tors.  Using this flag saves extra calls to fcntl(2) to achieve the same result.

RETURN VALUE
       On  success,  zero  is  returned.  On error, -1 is returned, errno is set appropriately, and pipefd is left un‐
       changed.

       On Linux (and other systems), pipe() does not modify pipefd on failure.  A requirement standardizing  this  be‐
       havior  was  added in POSIX.1-2008 TC2.  The Linux-specific pipe2() system call likewise does not modify pipefd
       on failure.

ERRORS
       EFAULT pipefd is not valid.

       EINVAL (pipe2()) Invalid value in flags.

       EMFILE The per-process limit on the number of open file descriptors has been reached.

       ENFILE The system-wide limit on the total number of open files has been reached.

       ENFILE The user hard limit on memory that can be allocated for pipes has been reached and  the  caller  is  not
              privileged; see pipe(7).

VERSIONS
       pipe2() was added to Linux in version 2.6.27; glibc support is available starting with version 2.9.

CONFORMING TO
       pipe(): POSIX.1-2001, POSIX.1-2008.

       pipe2() is Linux-specific.

NOTES
       The  System V ABI on some architectures allows the use of more than one register for returning multiple values;
       several architectures (namely, Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64) (ab)use this feature in order  to
       implement the pipe() system call in a functional manner: the call doesn't take any arguments and returns a pair
       of file descriptors as the return value on success.  The glibc pipe() wrapper function transparently deals with
       this.  See syscall(2) for information regarding registers used for storing second file descriptor.

EXAMPLES
       The  following program creates a pipe, and then fork(2)s to create a child process; the child inherits a dupli‐
       cate set of file descriptors that refer to the same pipe.  After the fork(2), each process closes the file  de‐
       scriptors  that it doesn't need for the pipe (see pipe(7)).  The parent then writes the string contained in the
       program's command-line argument to the pipe, and the child reads this string a byte at a time from the pipe and
       echoes it on standard output.

   Program source
       #include <sys/types.h>
       #include <sys/wait.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
       #include <string.h>

       int
       main(int argc, char *argv[])
       {
           int pipefd[2];
           pid_t cpid;
           char buf;

           if (argc != 2) {
               fprintf(stderr, "Usage: %s <string>\n", argv[0]);
               exit(EXIT_FAILURE);
           }

           if (pipe(pipefd) == -1) {
               perror("pipe");
               exit(EXIT_FAILURE);
           }

           cpid = fork();
           if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
           }

           if (cpid == 0) {    /* Child reads from pipe */
               close(pipefd[1]);          /* Close unused write end */

               while (read(pipefd[0], &buf, 1) > 0)
                   write(STDOUT_FILENO, &buf, 1);

               write(STDOUT_FILENO, "\n", 1);
               close(pipefd[0]);
               _exit(EXIT_SUCCESS);

           } else {            /* Parent writes argv[1] to pipe */
               close(pipefd[0]);          /* Close unused read end */
               write(pipefd[1], argv[1], strlen(argv[1]));
               close(pipefd[1]);          /* Reader will see EOF */
               wait(NULL);                /* Wait for child */
               exit(EXIT_SUCCESS);
           }
       }

SEE ALSO
       fork(2), read(2), socketpair(2), splice(2), tee(2), vmsplice(2), write(2), popen(3), pipe(7)

COLOPHON
       This  page  is  part of release 5.10 of the Linux man-pages project.  A description of the project, information
       about   reporting   bugs,    and    the    latest    version    of    this    page,    can    be    found    at
       https://www.kernel.org/doc/man-pages/.

Linux                                                 2020-06-09                                               PIPE(2)

It is now realized to use the pipe parent process to write data to the child process.

Code Example 07.1

/*
	parent process: write pipe
	child  process: read  pipe
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>

int main(void)
{
	int fd[2];
	int pid;

	if(pipe(fd) == -1)
		perror("pipe");

	pid = fork();
	if(pid > 0)  //parent process
	{
		close(fd[0]);
		sleep(5);
		write(fd[1], "ab", 2);

		while(1);
	}
	else if(pid == 0)
	{
		char ch[2];

		printf("Child process is waiting for data: \n");
		close(fd[1]);
		read(fd[0], ch, 2);//没有写入  阻塞等待
		printf("Read from pipe: %s\n", ch);
	}

	return 0;
}

08  Measure the size of the nameless pipe

Inject data into the pipeline loop to test the maximum that the pipeline can load

Code Example 08.1.c

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>

int main(void)
{
	pid_t pid;
	int fd[2];

	if(pipe(fd) == -1)
		perror("pipe");

	pid = fork();

	if(pid == 0)  //child process: write to the pipe
	{
		char ch = '*';
		int n = 0;

		close(fd[0]);

		while(1)
		{
			write(fd[1], &ch, 1);
			printf("count = %d\n", ++n);
		}
	}
	else if(pid > 0)  //parent process: wait until child process is over
	{
		waitpid(pid, NULL, 0);
	}
}

09  Anonymous pipe process communication instance

The input of the process that the parent process waits for.

Code Example 09.1.c

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>

int main(void)
{
	pid_t pid;
	int fd[2];

	if(pipe(fd) == -1)
		perror("pipe");

	pid = fork();

	if(pid == 0)
	{
		char tmp[100];

		close(fd[0]);

		while(1)
		{
			scanf("%s", tmp);
			write(fd[1], tmp, sizeof(tmp));
		}
	}
	else if(pid > 0)
	{
		char tmp[100];
		close(fd[1]);

		while(1)
		{
			printf("Parent process is waiting for the data from pipe:\n");
			read(fd[0], tmp, sizeof(tmp));
			printf("read from pipe: %s\n", tmp);
		}
	}

	return 0;
}

10  pipeline two-way transmission

The parent process converts lowercase to uppercase and sends the output back to the child process.

Code Example 10.1.c

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>
#include <ctype.h>

int main(void)
{
	pid_t pid;
	int fd[2];
	int fd2[2];

	if(pipe(fd) == -1)
		perror("pipe");
	if(pipe(fd2) == -1)
		perror("pipe");

	pid = fork();

	if(pid == 0)
	{
		char tmp[100];
		int i;

		close(fd[1]);
		close(fd2[0]);

		while(1)
		{
			memset(tmp, '\0', sizeof(tmp));
			read(fd[0], tmp, sizeof(tmp));

			for(i = 0; i < sizeof(tmp); i++)
				tmp[i] = toupper(tmp[i]);

			write(fd2[1], tmp, sizeof(tmp));
		}
	}
	else if(pid > 0)
	{
		char tmp[100];
		close(fd[0]);
		close(fd2[1]);

		while(1)
		{
			memset(tmp, '\0', sizeof(tmp));
			gets(tmp);
			write(fd[1], tmp, sizeof(tmp));

			memset(tmp, '\0', sizeof(tmp));
			read(fd2[0], tmp, sizeof(tmp));
			printf("After change: %s\n", tmp);
		}
	}

	return 0;
}

11 famous pipes

The concept leads to the fact that
        unnamed pipes are only applicable between related processes.
        Can two processes that are not related communicate?

View the liunx programming manual
        and know through man man that the serial number 3 is the library call 1 is the shell name 2 is the system call
        man 3 mkfifo

       1   Executable programs or shell commands
       2   System calls (functions provided by the kernel)
       3   Library calls (functions within program libraries)
       4   Special files (usually found in /dev)
       5   File formats and conventions, e.g. /etc/passwd
       6   Games
       7   Miscellaneous  (including  macro  packages and conventions), e.g.
           man(7), groff(7), man-pages(7)
       8   System administration commands (usually only for root)
       9   Kernel routines [Non standard]
MKFIFO(3)                   Linux Programmer's Manual                   MKFIFO(3)

NAME
       mkfifo, mkfifoat - make a FIFO special file (a named pipe)

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <sys/stat.h>

       int mkfifoat(int dirfd, const char *pathname, mode_t mode);

   Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

       mkfifoat():
           Since glibc 2.10:
               _POSIX_C_SOURCE >= 200809L
           Before glibc 2.10:
               _ATFILE_SOURCE

DESCRIPTION
       mkfifo() makes a FIFO special file with name pathname.  mode specifies the
       FIFO's permissions.  It is modified by the process's umask  in  the  usual
       way: the permissions of the created file are (mode & ~umask).

       A  FIFO  special file is similar to a pipe, except that it is created in a
       different way.  Instead of being an anonymous  communications  channel,  a
       FIFO special file is entered into the filesystem by calling mkfifo().

       Once  you  have  created  a FIFO special file in this way, any process can
       open it for reading or writing, in the same way as an ordinary file.  How‐
       ever, it has to be open at both ends simultaneously before you can proceed
       to do any input or output operations on it.  Opening a  FIFO  for  reading
       normally  blocks until some other process opens the same FIFO for writing,
       and vice versa.  See fifo(7) for  nonblocking  handling  of  FIFO  special
       files.

   mkfifoat()
       The  mkfifoat() function operates in exactly the same way as mkfifo(), ex‐
       cept for the differences described here.

       If the pathname given in pathname is relative, then it is interpreted rel‐
       ative  to  the  directory referred to by the file descriptor dirfd (rather
       than relative to the current working directory of the calling process,  as
       is done by mkfifo() for a relative pathname).

       If  pathname  is  relative  and  dirfd is the special value AT_FDCWD, then
       pathname is interpreted relative to the current working directory  of  the
       calling process (like mkfifo()).

       If pathname is absolute, then dirfd is ignored.

RETURN VALUE
       On  success mkfifo() and mkfifoat() return 0.  In the case of an error, -1
       is returned (in which case, errno is set appropriately).

ERRORS
       EACCES One of the directories in pathname did not allow  search  (execute)
              permission.

       EDQUOT The  user's  quota  of  disk blocks or inodes on the filesystem has
              been exhausted.

       EEXIST pathname already exists.  This includes the case where pathname  is
              a symbolic link, dangling or not.

       ENAMETOOLONG
              Either the total length of pathname is greater than PATH_MAX, or an
              individual filename component has a length greater  than  NAME_MAX.
              In  the  GNU  system, there is no imposed limit on overall filename
              length, but some filesystems may place limits on the  length  of  a
              component.

       ENOENT A  directory  component in pathname does not exist or is a dangling
              symbolic link.

       ENOSPC The directory or filesystem has no room for the new file.

       ENOTDIR
              A component used as a directory in pathname is not, in fact, a  di‐
              rectory.

       EROFS  pathname refers to a read-only filesystem.

       The following additional errors can occur for mkfifoat():

       EBADF  dirfd is not a valid file descriptor.

       ENOTDIR
              pathname  is  a relative path and dirfd is a file descriptor refer‐
              ring to a file other than a directory.

VERSIONS
       mkfifoat() was added to glibc in version 2.4.   It  is  implemented  using
       mknodat(2), available on Linux since kernel 2.6.16.

ATTRIBUTES
       For an explanation of the terms used in this section, see attributes(7).

       ┌─────────────────────┬───────────────┬─────────┐
       │Interface            │ Attribute     │ Value   │
       ├─────────────────────┼───────────────┼─────────┤
       │mkfifo(), mkfifoat() │ Thread safety │ MT-Safe │
       └─────────────────────┴───────────────┴─────────┘
CONFORMING TO
       mkfifo(): POSIX.1-2001, POSIX.1-2008.

       mkfifoat(): POSIX.1-2008.

SEE ALSO
       mkfifo(1),   close(2),  open(2),  read(2),  stat(2),  umask(2),  write(2),
       fifo(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.   A  de‐
       scription of the project, information about reporting bugs, and the latest
       version      of      this      page,      can      be       found       at
       https://www.kernel.org/doc/man-pages/.

GNU                                 2020-08-13                          MKFIFO(3)

Code example 11.1.c Create a named pipe and read to the pipe

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
 
int main(void)
{
    int ret;
    int fd;
    char buf[100];
 
    ret = mkfifo("my_fifo", 0666);
    if(ret != 0)
        perror("mkfifo");
 
    printf("Prepare reading from named pipe:\n");
 
    fd = open("my_fifo", O_RDWR);
    if(fd == -1)
        perror("open");
 
    while(1)
    {
        memset(buf, '\0', sizeof(buf));
        read(fd, buf, sizeof(buf));
        printf("Read from named pipe: %s\n", buf);
        sleep(1);
    }
 
    return 0;
}

Code Example 11.2.c User interaction writes data to the pipe

#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
    int fd;
    char buf[100];
 
    fd = open("my_fifo", O_WRONLY);
    if(fd == -1)
        perror("open");
 
    if(argc == 1)
    {
        printf("Please send something to the named pipe:\n");
        exit(EXIT_FAILURE);
    }
 
    strcpy(buf, argv[1]);
    write(fd, buf, sizeof(buf));
    printf("Write to the pipe: %s\n", buf);
 
    return 0;
}

 

12 shared memory

man shmget

SHMGET(2)                                                   Linux Programmer's Manual                                                  SHMGET(2)

NAME
       shmget - allocates a System V shared memory segment

SYNOPSIS
       #include <sys/ipc.h>
       #include <sys/shm.h>

       int shmget(key_t key, size_t size, int shmflg);

DESCRIPTION
       shmget()  returns the identifier of the System V shared memory segment associated with the value of the argument key.  It may be used ei‐
       ther to obtain the identifier of a previously created shared memory segment (when shmflg is zero and key does not have the value IPC_PRI‐
       VATE), or to create a new set.

       A  new shared memory segment, with size equal to the value of size rounded up to a multiple of PAGE_SIZE, is created if key has the value
       IPC_PRIVATE or key isn't IPC_PRIVATE, no shared memory segment corresponding to key exists, and IPC_CREAT is specified in shmflg.

       If shmflg specifies both IPC_CREAT and IPC_EXCL and a shared memory segment already exists for key, then shmget() fails with errno set to
       EEXIST.  (This is analogous to the effect of the combination O_CREAT | O_EXCL for open(2).)

       The value shmflg is composed of:

       IPC_CREAT
              Create  a  new segment.  If this flag is not used, then shmget() will find the segment associated with key and check to see if the
              user has permission to access the segment.

       IPC_EXCL
              This flag is used with IPC_CREAT to ensure that this call creates the segment.  If the segment already exists, the call fails.

       SHM_HUGETLB (since Linux 2.6)
              Allocate the segment using "huge pages."  See the Linux kernel source file Documentation/admin-guide/mm/hugetlbpage.rst  for  fur‐
              ther information.

       SHM_HUGE_2MB, SHM_HUGE_1GB (since Linux 3.8)
              Used  in  conjunction with SHM_HUGETLB to select alternative hugetlb page sizes (respectively, 2 MB and 1 GB) on systems that sup‐
              port multiple hugetlb page sizes.

              More generally, the desired huge page size can be configured by encoding the base-2 logarithm of the desired page size in the  six
              bits at the offset SHM_HUGE_SHIFT.  Thus, the above two constants are defined as:

                  #define SHM_HUGE_2MB    (21 << SHM_HUGE_SHIFT)
                  #define SHM_HUGE_1GB    (30 << SHM_HUGE_SHIFT)

              For some additional details, see the discussion of the similarly named constants in mmap(2).

       SHM_NORESERVE (since Linux 2.6.15)
              This flag serves the same purpose as the mmap(2) MAP_NORESERVE flag.  Do not reserve swap space for this segment.  When swap space
              is reserved, one has the guarantee that it is possible to modify the segment.  When swap space  is  not  reserved  one  might  get
              SIGSEGV  upon  a  write if no physical memory is available.  See also the discussion of the file /proc/sys/vm/overcommit_memory in
              proc(5).

       In addition to the above flags, the least significant 9 bits of shmflg specify the permissions granted to the owner, group,  and  others.
       These  bits  have the same format, and the same meaning, as the mode argument of open(2).  Presently, execute permissions are not used by
       the system.

       When a new shared memory segment is created, its contents are initialized to zero values, and its  associated  data  structure,  shmid_ds
       (see shmctl(2)), is initialized as follows:

       • shm_perm.cuid and shm_perm.uid are set to the effective user ID of the calling process.

       • shm_perm.cgid and shm_perm.gid are set to the effective group ID of the calling process.

       • The least significant 9 bits of shm_perm.mode are set to the least significant 9 bit of shmflg.

       • shm_segsz is set to the value of size.

       • shm_lpid, shm_nattch, shm_atime, and shm_dtime are set to 0.

       • shm_ctime is set to the current time.

       If the shared memory segment already exists, the permissions are verified, and a check is made to see if it is marked for destruction.

RETURN VALUE
       On success, a valid shared memory identifier is returned.  On error, -1 is returned, and errno is set to indicate the error.

ERRORS
       On failure, errno is set to one of the following:

       EACCES The  user does not have permission to access the shared memory segment, and does not have the CAP_IPC_OWNER capability in the user
              namespace that governs its IPC namespace.

       EEXIST IPC_CREAT and IPC_EXCL were specified in shmflg, but a shared memory segment already exists for key.

       EINVAL A new segment was to be created and size is less than SHMMIN or greater than SHMMAX.

       EINVAL A segment for the given key exists, but size is greater than the size of that segment.

       ENFILE The system-wide limit on the total number of open files has been reached.

       ENOENT No segment exists for the given key, and IPC_CREAT was not specified.

       ENOMEM No memory could be allocated for segment overhead.

       ENOSPC All possible shared memory IDs have been taken (SHMMNI), or allocating a segment of the requested size would cause the  system  to
              exceed the system-wide limit on shared memory (SHMALL).

       EPERM  The SHM_HUGETLB flag was specified, but the caller was not privileged (did not have the CAP_IPC_LOCK capability).

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4.

       SHM_HUGETLB and SHM_NORESERVE are Linux extensions.

NOTES
       The inclusion of <sys/types.h> and <sys/ipc.h> isn't required on Linux or by any version of POSIX.  However, some old implementations re‐
       quired the inclusion of these header files, and the SVID also documented their inclusion.  Applications intended to be portable  to  such
       old systems may need to include these header files.

       IPC_PRIVATE  isn't  a flag field but a key_t type.  If this special value is used for key, the system call ignores all but the least sig‐
       nificant 9 bits of shmflg and creates a new shared memory segment.

   Shared memory limits
       The following limits on shared memory segment resources affect the shmget() call:

       SHMALL System-wide limit on the total amount of shared memory, measured in units of the system page size.

              On Linux, this limit can be read and modified via /proc/sys/kernel/shmall.  Since Linux 3.16, the default value for this limit is:

                  ULONG_MAX - 2^24

              The effect of this value (which is suitable for both 32-bit and 64-bit systems) is to impose no limitation on  allocations.   This
              value,  rather than ULONG_MAX, was chosen as the default to prevent some cases where historical applications simply raised the ex‐
              isting limit without first checking its current value.  Such applications would cause the value to overflow if the limit  was  set
              at ULONG_MAX.

              From Linux 2.4 up to Linux 3.15, the default value for this limit was:

                  SHMMAX / PAGE_SIZE * (SHMMNI / 16)

              If  SHMMAX  and  SHMMNI  were not modified, then multiplying the result of this formula by the page size (to get a value in bytes)
              yielded a value of 8 GB as the limit on the total memory used by all shared memory segments.

       SHMMAX Maximum size in bytes for a shared memory segment.

              On Linux, this limit can be read and modified via /proc/sys/kernel/shmmax.  Since Linux 3.16, the default value for this limit is:

                  ULONG_MAX - 2^24

              The effect of this value (which is suitable for both 32-bit and 64-bit systems) is to impose no limitation  on  allocations.   See
              the description of SHMALL for a discussion of why this default value (rather than ULONG_MAX) is used.

              From Linux 2.2 up to Linux 3.15, the default value of this limit was 0x2000000 (32 MB).

              Because  it  is not possible to map just part of a shared memory segment, the amount of virtual memory places another limit on the
              maximum size of a usable segment: for example, on i386 the largest segments that can be mapped have a size of around  2.8 GB,  and
              on x86-64 the limit is around 127 TB.

       SHMMIN Minimum  size  in bytes for a shared memory segment: implementation dependent (currently 1 byte, though PAGE_SIZE is the effective
              minimum size).

       SHMMNI System-wide limit on the number of shared memory segments.  In Linux 2.2, the default value for this limit was  128;  since  Linux
              2.4, the default value is 4096.

              On Linux, this limit can be read and modified via /proc/sys/kernel/shmmni.

       The implementation has no specific limits for the per-process maximum number of shared memory segments (SHMSEG).

   Linux notes
       Until version 2.3.30, Linux would return EIDRM for a shmget() on a shared memory segment scheduled for deletion.

BUGS
       The name choice IPC_PRIVATE was perhaps unfortunate, IPC_NEW would more clearly show its function.

EXAMPLES
       See shmop(2).

SEE ALSO
       memfd_create(2), shmat(2), shmctl(2), shmdt(2), ftok(3), capabilities(7), shm_overview(7), sysvipc(7)

COLOPHON
       This  page  is  part of release 5.10 of the Linux man-pages project.  A description of the project, information about reporting bugs, and
       the latest version of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                              2020-04-11                                                          SHMGET(2)

man shmdt

SHMOP(2)                                                    Linux Programmer's Manual                                                   SHMOP(2)

NAME
       shmat, shmdt - System V shared memory operations

SYNOPSIS
       #include <sys/types.h>
       #include <sys/shm.h>

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);

DESCRIPTION
   shmat()
       shmat()  attaches  the System V shared memory segment identified by shmid to the address space of the calling process.  The attaching ad‐
       dress is specified by shmaddr with one of the following criteria:

       • If shmaddr is NULL, the system chooses a suitable (unused) page-aligned address to attach the segment.

       • If shmaddr isn't NULL and SHM_RND is specified in shmflg, the attach occurs at the address equal to shmaddr rounded down to the nearest
         multiple of SHMLBA.

       • Otherwise, shmaddr must be a page-aligned address at which the attach occurs.

       In addition to SHM_RND, the following flags may be specified in the shmflg bit-mask argument:

       SHM_EXEC (Linux-specific; since Linux 2.6.9)
              Allow the contents of the segment to be executed.  The caller must have execute permission on the segment.

       SHM_RDONLY
              Attach  the  segment for read-only access.  The process must have read permission for the segment.  If this flag is not specified,
              the segment is attached for read and write access, and the process must have read and write permission for the segment.  There  is
              no notion of a write-only shared memory segment.

       SHM_REMAP (Linux-specific)
              This  flag specifies that the mapping of the segment should replace any existing mapping in the range starting at shmaddr and con‐
              tinuing for the size of the segment.  (Normally, an EINVAL error would result if a mapping already exists in this address  range.)
              In this case, shmaddr must not be NULL.

       The  brk(2)  value of the calling process is not altered by the attach.  The segment will automatically be detached at process exit.  The
       same segment may be attached as a read and as a read-write one, and more than once, in the process's address space.

       A successful shmat() call updates the members of the shmid_ds structure (see shmctl(2)) associated with the shared memory segment as fol‐
       lows:

       • shm_atime is set to the current time.

       • shm_lpid is set to the process-ID of the calling process.

       • shm_nattch is incremented by one.

   shmdt()
       shmdt()  detaches  the  shared  memory segment located at the address specified by shmaddr from the address space of the calling process.
       The to-be-detached segment must be currently attached with shmaddr equal to the value returned by the attaching shmat() call.

       On a successful shmdt() call, the system updates the members of the shmid_ds structure associated with the shared memory segment as  fol‐
       lows:

       • shm_dtime is set to the current time.

       • shm_lpid is set to the process-ID of the calling process.

       • shm_nattch is decremented by one.  If it becomes 0 and the segment is marked for deletion, the segment is deleted.

RETURN VALUE
       On success, shmat() returns the address of the attached shared memory segment; on error, (void *) -1 is returned, and errno is set to in‐
       dicate the cause of the error.

       On success, shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.

ERRORS
       When shmat() fails, errno is set to one of the following:

       EACCES The calling process does not have the required permissions for the requested attach type, and does not have the CAP_IPC_OWNER  ca‐
              pability in the user namespace that governs its IPC namespace.

       EIDRM  shmid points to a removed identifier.

       EINVAL Invalid  shmid  value,  unaligned (i.e., not page-aligned and SHM_RND was not specified) or invalid shmaddr value, or can't attach
              segment at shmaddr, or SHM_REMAP was specified and shmaddr was NULL.

       ENOMEM Could not allocate memory for the descriptor or for the page tables.

       When shmdt() fails, errno is set as follows:

       EINVAL There is no shared memory segment attached at shmaddr; or, shmaddr is not aligned on a page boundary.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4.

       In SVID 3 (or perhaps earlier), the type of the shmaddr argument was changed from char * into const void *,  and  the  returned  type  of
       shmat() from char * into void *.

NOTES
       After a fork(2), the child inherits the attached shared memory segments.

       After an execve(2), all attached shared memory segments are detached from the process.

       Upon _exit(2), all attached shared memory segments are detached from the process.

       Using  shmat()  with shmaddr equal to NULL is the preferred, portable way of attaching a shared memory segment.  Be aware that the shared
       memory segment attached in this way may be attached at different addresses in different processes.  Therefore,  any  pointers  maintained
       within the shared memory must be made relative (typically to the starting address of the segment), rather than absolute.

       On Linux, it is possible to attach a shared memory segment even if it is already marked to be deleted.  However, POSIX.1 does not specify
       this behavior and many other implementations do not support it.

       The following system parameter affects shmat():

       SHMLBA Segment low boundary address multiple.  When explicitly specifying an attach address in a call to shmat(), the caller  should  en‐
              sure  that  the  address is a multiple of this value.  This is necessary on some architectures, in order either to ensure good CPU
              cache performance or to ensure that different attaches of the same segment have consistent views within the CPU cache.  SHMLBA  is
              normally some multiple of the system page size.  (On many Linux architectures, SHMLBA is the same as the system page size.)

       The implementation places no intrinsic per-process limit on the number of shared memory segments (SHMSEG).

EXAMPLES
       The  two  programs  shown  below  exchange  a  string using a shared memory segment.  Further details about the programs are given below.
       First, we show a shell session demonstrating their use.

       In one terminal window, we run the "reader" program, which creates a System V shared memory segment and a System V  semaphore  set.   The
       program prints out the IDs of the created objects, and then waits for the semaphore to change value.

           $ ./svshm_string_read
           shmid = 1114194; semid = 15

       In  another terminal window, we run the "writer" program.  The "writer" program takes three command-line arguments: the IDs of the shared
       memory segment and semaphore set created by the "reader", and a string.  It attaches the  existing  shared  memory  segment,  copies  the
       string to the shared memory, and modifies the semaphore value.

           $ ./svshm_string_write 1114194 15 'Hello, world'

       Returning  to the terminal where the "reader" is running, we see that the program has ceased waiting on the semaphore and has printed the
       string that was copied into the shared memory segment by the writer:

           Hello, world

   Program source: svshm_string.h
       The following header file is included by the "reader" and "writer" programs.

           #include <sys/types.h>
           #include <sys/ipc.h>
           #include <sys/shm.h>
           #include <sys/sem.h>
           #include <stdio.h>
           #include <stdlib.h>
           #include <string.h>

           #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                                   } while (0)

           union semun {                   /* Used in calls to semctl() */
               int                 val;
               struct semid_ds *   buf;
               unsigned short *    array;
           #if defined(__linux__)
               struct seminfo *    __buf;
           #endif
           };

           #define MEM_SIZE 4096

   Program source: svshm_string_read.c
       The "reader" program creates a shared memory segment and a semaphore set containing one semaphore.  It then attaches  the  shared  memory
       object  into its address space and initializes the semaphore value to 1.  Finally, the program waits for the semaphore value to become 0,
       and afterwards prints the string that has been copied into the shared memory segment by the "writer".

           /* svshm_string_read.c

              Licensed under GNU General Public License v2 or later.
           */
           #include "svshm_string.h"

           int
           main(int argc, char *argv[])
           {
               int semid, shmid;
               union semun arg, dummy;
               struct sembuf sop;
               char *addr;

               /* Create shared memory and semaphore set containing one
                  semaphore */

               shmid = shmget(IPC_PRIVATE, MEM_SIZE, IPC_CREAT | 0600);
               if (shmid == -1)
                   errExit("shmget");

               semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
               if (shmid == -1)
                   errExit("shmget");

               /* Attach shared memory into our address space */

               addr = shmat(shmid, NULL, SHM_RDONLY);
               if (addr == (void *) -1)
                   errExit("shmat");

               /* Initialize semaphore 0 in set with value 1 */

               arg.val = 1;
               if (semctl(semid, 0, SETVAL, arg) == -1)
                   errExit("semctl");

               printf("shmid = %d; semid = %d\n", shmid, semid);

               /* Wait for semaphore value to become 0 */

               sop.sem_num = 0;
               sop.sem_op = 0;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               /* Print the string from shared memory */

               printf("%s\n", addr);

               /* Remove shared memory and semaphore set */

               if (shmctl(shmid, IPC_RMID, NULL) == -1)
                   errExit("shmctl");
               if (semctl(semid, 0, IPC_RMID, dummy) == -1)
                   errExit("semctl");

               exit(EXIT_SUCCESS);
           }

   Program source: svshm_string_write.c
       The writer program takes three command-line arguments: the IDs of the shared memory segment and semaphore set that have already been cre‐
       ated  by  the  "reader",  and  a string.  It attaches the shared memory segment into its address space, and then decrements the semaphore
       value to 0 in order to inform the "reader" that it can now examine the contents of the shared memory.

           /* svshm_string_write.c

              Licensed under GNU General Public License v2 or later.
           */
           #include "svshm_string.h"

           int
           main(int argc, char *argv[])
           {
               int semid, shmid;
               struct sembuf sop;
               char *addr;
               size_t len;

               if (argc != 4) {
                   fprintf(stderr, "Usage: %s shmid semid string\n", argv[0]);
                   exit(EXIT_FAILURE);
               }

               len = strlen(argv[3]) + 1;  /* +1 to include trailing '\0' */
               if (len > MEM_SIZE) {
                   fprintf(stderr, "String is too big!\n");
                   exit(EXIT_FAILURE);
               }

               /* Get object IDs from command-line */

               shmid = atoi(argv[1]);
               semid = atoi(argv[2]);

               /* Attach shared memory into our address space and copy string
                  (including trailing null byte) into memory. */

               addr = shmat(shmid, NULL, 0);
               if (addr == (void *) -1)
                   errExit("shmat");

               memcpy(addr, argv[3], len);

               /* Decrement semaphore to 0 */

               sop.sem_num = 0;
               sop.sem_op = -1;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               exit(EXIT_SUCCESS);
           }

SEE ALSO
       brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.  A description of the project, information about  reporting  bugs,  and
       the latest version of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                              2020-04-11                                                           SHMOP(2)

man shmat

SHMOP(2)                                                    Linux Programmer's Manual                                                   SHMOP(2)

NAME
       shmat, shmdt - System V shared memory operations

SYNOPSIS
       #include <sys/types.h>
       #include <sys/shm.h>

       void *shmat(int shmid, const void *shmaddr, int shmflg);

       int shmdt(const void *shmaddr);

DESCRIPTION
   shmat()
       shmat()  attaches  the System V shared memory segment identified by shmid to the address space of the calling process.  The attaching ad‐
       dress is specified by shmaddr with one of the following criteria:

       • If shmaddr is NULL, the system chooses a suitable (unused) page-aligned address to attach the segment.

       • If shmaddr isn't NULL and SHM_RND is specified in shmflg, the attach occurs at the address equal to shmaddr rounded down to the nearest
         multiple of SHMLBA.

       • Otherwise, shmaddr must be a page-aligned address at which the attach occurs.

       In addition to SHM_RND, the following flags may be specified in the shmflg bit-mask argument:

       SHM_EXEC (Linux-specific; since Linux 2.6.9)
              Allow the contents of the segment to be executed.  The caller must have execute permission on the segment.

       SHM_RDONLY
              Attach  the  segment for read-only access.  The process must have read permission for the segment.  If this flag is not specified,
              the segment is attached for read and write access, and the process must have read and write permission for the segment.  There  is
              no notion of a write-only shared memory segment.

       SHM_REMAP (Linux-specific)
              This  flag specifies that the mapping of the segment should replace any existing mapping in the range starting at shmaddr and con‐
              tinuing for the size of the segment.  (Normally, an EINVAL error would result if a mapping already exists in this address  range.)
              In this case, shmaddr must not be NULL.

       The  brk(2)  value of the calling process is not altered by the attach.  The segment will automatically be detached at process exit.  The
       same segment may be attached as a read and as a read-write one, and more than once, in the process's address space.

       A successful shmat() call updates the members of the shmid_ds structure (see shmctl(2)) associated with the shared memory segment as fol‐
       lows:

       • shm_atime is set to the current time.

       • shm_lpid is set to the process-ID of the calling process.

       • shm_nattch is incremented by one.

   shmdt()
       shmdt()  detaches  the  shared  memory segment located at the address specified by shmaddr from the address space of the calling process.
       The to-be-detached segment must be currently attached with shmaddr equal to the value returned by the attaching shmat() call.

       On a successful shmdt() call, the system updates the members of the shmid_ds structure associated with the shared memory segment as  fol‐
       lows:

       • shm_dtime is set to the current time.

       • shm_lpid is set to the process-ID of the calling process.

       • shm_nattch is decremented by one.  If it becomes 0 and the segment is marked for deletion, the segment is deleted.

RETURN VALUE
       On success, shmat() returns the address of the attached shared memory segment; on error, (void *) -1 is returned, and errno is set to in‐
       dicate the cause of the error.

       On success, shmdt() returns 0; on error -1 is returned, and errno is set to indicate the cause of the error.

ERRORS
       When shmat() fails, errno is set to one of the following:

       EACCES The calling process does not have the required permissions for the requested attach type, and does not have the CAP_IPC_OWNER  ca‐
              pability in the user namespace that governs its IPC namespace.

       EIDRM  shmid points to a removed identifier.

       EINVAL Invalid  shmid  value,  unaligned (i.e., not page-aligned and SHM_RND was not specified) or invalid shmaddr value, or can't attach
              segment at shmaddr, or SHM_REMAP was specified and shmaddr was NULL.

       ENOMEM Could not allocate memory for the descriptor or for the page tables.

       When shmdt() fails, errno is set as follows:

       EINVAL There is no shared memory segment attached at shmaddr; or, shmaddr is not aligned on a page boundary.

CONFORMING TO
       POSIX.1-2001, POSIX.1-2008, SVr4.

       In SVID 3 (or perhaps earlier), the type of the shmaddr argument was changed from char * into const void *,  and  the  returned  type  of
       shmat() from char * into void *.

NOTES
       After a fork(2), the child inherits the attached shared memory segments.

       After an execve(2), all attached shared memory segments are detached from the process.

       Upon _exit(2), all attached shared memory segments are detached from the process.

       Using  shmat()  with shmaddr equal to NULL is the preferred, portable way of attaching a shared memory segment.  Be aware that the shared
       memory segment attached in this way may be attached at different addresses in different processes.  Therefore,  any  pointers  maintained
       within the shared memory must be made relative (typically to the starting address of the segment), rather than absolute.

       On Linux, it is possible to attach a shared memory segment even if it is already marked to be deleted.  However, POSIX.1 does not specify
       this behavior and many other implementations do not support it.

       The following system parameter affects shmat():

       SHMLBA Segment low boundary address multiple.  When explicitly specifying an attach address in a call to shmat(), the caller  should  en‐
              sure  that  the  address is a multiple of this value.  This is necessary on some architectures, in order either to ensure good CPU
              cache performance or to ensure that different attaches of the same segment have consistent views within the CPU cache.  SHMLBA  is
              normally some multiple of the system page size.  (On many Linux architectures, SHMLBA is the same as the system page size.)

       The implementation places no intrinsic per-process limit on the number of shared memory segments (SHMSEG).

EXAMPLES
       The  two  programs  shown  below  exchange  a  string using a shared memory segment.  Further details about the programs are given below.
       First, we show a shell session demonstrating their use.

       In one terminal window, we run the "reader" program, which creates a System V shared memory segment and a System V  semaphore  set.   The
       program prints out the IDs of the created objects, and then waits for the semaphore to change value.

           $ ./svshm_string_read
           shmid = 1114194; semid = 15

       In  another terminal window, we run the "writer" program.  The "writer" program takes three command-line arguments: the IDs of the shared
       memory segment and semaphore set created by the "reader", and a string.  It attaches the  existing  shared  memory  segment,  copies  the
       string to the shared memory, and modifies the semaphore value.

           $ ./svshm_string_write 1114194 15 'Hello, world'

       Returning  to the terminal where the "reader" is running, we see that the program has ceased waiting on the semaphore and has printed the
       string that was copied into the shared memory segment by the writer:

           Hello, world

   Program source: svshm_string.h
       The following header file is included by the "reader" and "writer" programs.

           #include <sys/types.h>
           #include <sys/ipc.h>
           #include <sys/shm.h>
           #include <sys/sem.h>
           #include <stdio.h>
           #include <stdlib.h>
           #include <string.h>

           #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                                   } while (0)

           union semun {                   /* Used in calls to semctl() */
               int                 val;
               struct semid_ds *   buf;
               unsigned short *    array;
           #if defined(__linux__)
               struct seminfo *    __buf;
           #endif
           };

           #define MEM_SIZE 4096

   Program source: svshm_string_read.c
       The "reader" program creates a shared memory segment and a semaphore set containing one semaphore.  It then attaches  the  shared  memory
       object  into its address space and initializes the semaphore value to 1.  Finally, the program waits for the semaphore value to become 0,
       and afterwards prints the string that has been copied into the shared memory segment by the "writer".

           /* svshm_string_read.c

              Licensed under GNU General Public License v2 or later.
           */
           #include "svshm_string.h"

           int
           main(int argc, char *argv[])
           {
               int semid, shmid;
               union semun arg, dummy;
               struct sembuf sop;
               char *addr;

               /* Create shared memory and semaphore set containing one
                  semaphore */

               shmid = shmget(IPC_PRIVATE, MEM_SIZE, IPC_CREAT | 0600);
               if (shmid == -1)
                   errExit("shmget");

               semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
               if (shmid == -1)
                   errExit("shmget");

               /* Attach shared memory into our address space */

               addr = shmat(shmid, NULL, SHM_RDONLY);
               if (addr == (void *) -1)
                   errExit("shmat");

               /* Initialize semaphore 0 in set with value 1 */

               arg.val = 1;
               if (semctl(semid, 0, SETVAL, arg) == -1)
                   errExit("semctl");

               printf("shmid = %d; semid = %d\n", shmid, semid);

               /* Wait for semaphore value to become 0 */

               sop.sem_num = 0;
               sop.sem_op = 0;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               /* Print the string from shared memory */

               printf("%s\n", addr);

               /* Remove shared memory and semaphore set */

               if (shmctl(shmid, IPC_RMID, NULL) == -1)
                   errExit("shmctl");
               if (semctl(semid, 0, IPC_RMID, dummy) == -1)
                   errExit("semctl");

               exit(EXIT_SUCCESS);
           }

   Program source: svshm_string_write.c
       The writer program takes three command-line arguments: the IDs of the shared memory segment and semaphore set that have already been cre‐
       ated  by  the  "reader",  and  a string.  It attaches the shared memory segment into its address space, and then decrements the semaphore
       value to 0 in order to inform the "reader" that it can now examine the contents of the shared memory.

           /* svshm_string_write.c

              Licensed under GNU General Public License v2 or later.
           */
           #include "svshm_string.h"

           int
           main(int argc, char *argv[])
           {
               int semid, shmid;
               struct sembuf sop;
               char *addr;
               size_t len;

               if (argc != 4) {
                   fprintf(stderr, "Usage: %s shmid semid string\n", argv[0]);
                   exit(EXIT_FAILURE);
               }

               len = strlen(argv[3]) + 1;  /* +1 to include trailing '\0' */
               if (len > MEM_SIZE) {
                   fprintf(stderr, "String is too big!\n");
                   exit(EXIT_FAILURE);
               }

               /* Get object IDs from command-line */

               shmid = atoi(argv[1]);
               semid = atoi(argv[2]);

               /* Attach shared memory into our address space and copy string
                  (including trailing null byte) into memory. */

               addr = shmat(shmid, NULL, 0);
               if (addr == (void *) -1)
                   errExit("shmat");

               memcpy(addr, argv[3], len);

               /* Decrement semaphore to 0 */

               sop.sem_num = 0;
               sop.sem_op = -1;
               sop.sem_flg = 0;

               if (semop(semid, &sop, 1) == -1)
                   errExit("semop");

               exit(EXIT_SUCCESS);
           }

SEE ALSO
       brk(2), mmap(2), shmctl(2), shmget(2), capabilities(7), shm_overview(7), sysvipc(7)

COLOPHON
       This page is part of release 5.10 of the Linux man-pages project.  A description of the project, information about  reporting  bugs,  and
       the latest version of this page, can be found at https://www.kernel.org/doc/man-pages/.

Linux                                                              2020-04-11                                                           SHMOP(2)

 Shared Memory Example 12.1c

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

char msg[] = "Hello world";

int main(void)
{
	int shmid;
	pid_t pid;

	shmid = shmget(IPC_PRIVATE, 1024, IPC_CREAT);

	pid = fork();

	if(pid > 0)
	{
		char *p_addr;
		p_addr = shmat(shmid, NULL, 0);

		memset(p_addr, '\0', sizeof(msg));
		memcpy(p_addr, msg, sizeof(msg));

		shmdt(p_addr);

		waitpid(pid, NULL, 0);
	}
	else if(pid == 0)
	{
		char *c_addr;
		c_addr = shmat(shmid, NULL, 0);

		printf("Child process waits a short time: \n");
		sleep(3);
		printf("Child Process reads from shared memory: %s\n", c_addr);
		shmdt(c_addr);
	}
	else
		perror("fork");

	return 0;
}

 

Guess you like

Origin blog.csdn.net/shelter1234567/article/details/129632732