UNIX programming FAQ
Process control
***********
1.1 Creating a new process: fork function
=========================
1.1.1 What is the FORK function?
----------------------
#include
#include
PID_T FORK (Void);
The 'fork ()' function is used to create a new process from existing processes. The new process is called a child process, and the original process is called
Parent process. You can know which is a child process by checking the return value of the 'fork ()' function. Which is a child process. father
The return value obtained by the process is the process number of the child, and the child process returns 0. The following sample program means its basics
Features:
PID_T PID;
Switch (PID = for ())
{
Case -1:
/ * Here the PID is -1, the FORK function failed * /
/ * Some possible reasons are * /
/ * Process number or virtual memory is charged * /
PERROR ("The fork failed!");
Break;
Case 0:
/ * PID is 0, sub-process * /
/ * Here, we are children, what do you want? * /
/ * ... * /
/ * But after doing, we need to do it like: * /
_exit (0);
DEFAULT:
/ * PID is greater than 0, the sub-process number obtained for the parent process * /
Printf ("Child's PID IS% D / N", PID);
}
Of course, someone can replace the 'switch ()' statement with 'IF () ... Else ...' statement, but the above form is
A useful customary method.
Know what the child's processes inherited or without inheritance will help us. The following list will be
Different UNIX achievements change, so perhaps the accuracy has a moisture. Please pay attention to the child's process is
The * copy * of these things is not them itself.
Since the child process from the father process:
* Process qualification (real (REAL) / Effective / Saved user number (UIDS) and group number (GIDS)
* Environment (environments)
* Stack
* RAM
* Open the descriptor of the file (note that the location of the corresponding file is shared by the parent child process, which will cause ambiguity)
* Close-on-exec (Translator Note: Close-on-EXEC flag can be drawn by fnctl ()
The character setting, POSIX.1 requires that all directory streams must be turned off when the exec function calls. More detailed instructions,
See << UNIX Environment Advanced Programming >> W. R. Stevens, 1993, Yugan Dynasty, etc. (hereinafter referred to as << Advanced Edge)
Cheng >>), Section 3.13 and 8.9)
* Signal Control Settings
* NICE value (Translator Note: The NICE value is set by the NICE function, which indicates the priority of the process, the smaller the value, excellent
The higher the first level)
* Process Scheduling Class (Translator Note: Process Scheduling Category Refiguration Process is scheduled during the system
The category, different categories have different priorities, depending on the process scheduling category and the NICE value, the process scheduler can be counted
Calculate the global priority of each process, priority execution of high priority
* Process group number
* Dialonic ID (Translator Note) (Translation: Detection " << Advanced Programming >>
Section 9.5)
* Current work catalog
* Root Catalog (Translator Note: The root directory is not necessarily "/", it can be changed by the chroot function)
* File mode creation shielding words (Translator Note: Translations from << Advanced
Cheng >>, refers to: Creating the default blockage of new files)
* Resource limit
* Control terminal
The child process is unique:
* Process number
* Different parent process numbers (translator Note: Different parent issues "of the child process and the parent process number of the parent process
The number can be obtained by the getppid function)
* Copy of its own file descriptor and directory stream (Translator Note: The directory stream is created by the OpenDir function, because it is
Read, admire "directory stream")
* The child process does not inherits the process of the Parent process, the text, data, and other lock memory (Memory Locks)
(Translator Note: Locking the memory finger to be locked by the virtual memory page, after locking, the kernel does not allow the kernel when necessary
Convert (Page Out) See << The GNU C Library Reference Manual >> 2.2,
1999, 3.4.2)
* System time in the TMS structure (Translator Note: The TMS structure can be obtained by the Times function, it saves four data
Time for recording processes using central processors (CPU: Central Processing Unit), including: user
Room, system time, user sub-process totals, system each sub-process total time)
* Resource Utilizations Set to 0
* Blocking signal set is initialized into empty set (translator Note: Originally unclear, translation according to the Fork function man page
Take a little modification)
* Do not inherit the timer created by the Timer_create function
* Do not inherit asynchronous input and output
1.1.2 Where is the difference between the FORK function and the vFork function?
-------------------------------------------
Some systems have a system call 'vfork ()', which was originally designed to have fewer additional expenditures of 'fork ()'
(Lower-overhead) version. Because 'fork ()' includes copying the address space of the entire process, it is very
"Expensive", this 'vFork ()' function is therefore introduced. (In 3.0BSD) (Translator Note: BSD:
Berkeley Software Distribution)
* But *, since 'vFork ()' is introduced, 'fork ()' implementation method has been greatly improved, the most worthy
Note that the introduction of "copy-on-write" is "written", it is accessible by allowing the parent-child process.
The same physical memory is camouflage (FAKE) has a real copy of the process space until there is a process change.
Copy the data in memory. This improvement is largely oblived to the reasons for 'vfork ()'; in fact,
A large part of the system completely lost the original function of 'vfork ()'. But in order to be compatible, they still provide
'VFork ()' function call, but it just simply calls 'fork ()' instead of simulating all 'vfork ()'
Semantics (Semantics, translations from << Advanced Programming >>, specified content and practices). The conclusion is that the difference between trying to use any 'fork ()' and 'vfork ()' is * very * unbroken. In fact,
Maybe use 'vfork ()' is unclexic, unless you know what you want *.
The basic difference between the two is when using 'vFork ()' to create a new process, the parent process will be temporarily blocked.
The child process can borrow the address space of the parent process. This strange state will continue until the child process either
Out, call 'execve ()', to this parent process continues.
This means that a child process created by 'vfork ()' must be careful to avoid making it unexpectedly changed.
variable. In particular, the child process must be returned from the function that contains 'vfork ()' calls, and must not be adjusted
Use 'exit ()' (if it needs to exit, it needs to use '_exit ()'; in fact, for normal use
The sub-process created by 'fork ()' is correct) (Translator Note: See 1.1.3)
1.1.3 Why use the _exit function in a sub-process branch of a Fork without using an exit function?
-------------------------------------------------- ---------------
'Exit ()' and '_exit ()' have a lot of differences in using 'fork ()', especially 'vfork ()'.
prominent.
'Exit ()' and '_exit ()' basic differences in the previous call implementation and call library user status structure
(user-mode constructs) related to clear work (Clean-Up), and call user-defined clearing programs
(Translator Note: Custom Clear Programs are defined by the ATEXIT function, can define multiple times, and perform in reverse order), relative
It should be, the latter function is only implemented for the process to implement the kernel cleanup.
In the sub-process branch created by 'fork ()', using 'exit ()' is incorrect, this is
Because it can cause standard input and output (Translator Note: stdio: standard input output) buffer
Empty twice, and temporary files are unexpected (translator Note: Temporary files are created by TMPFile functions
In the system temporary directory, the file name is randomly generated by the system). The situation will be worse in the C program, because the quiet
The destructors of Static Objects can be incorrectly executed. (There are some special conditions
For example, the daemon, their * parent process * needs to call '_exit () "instead of sub-process;
The basic rules of most cases are that 'exit ()' is only called once after each entry 'main' function. )
In the sub-process branch created by 'vFork ()', 'exit ()' will be more dangerous because it will affect
* Father * The status of the process.
1.2 environment variable
============
1.2.1 How to get / set the environment variable from the program?
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Getting an environment variable can be done by calling the 'getENV ()' function.
#include
Char * getenv (const char * name);
Setting an environment variable can be done by calling the 'Putenv ()' function.
#include
INT PUTENV (Char * String);
Variable String should comply with "name = value" format. Strings that have been passed to the Putenv function * not * can be released or become invalid because a pointer to it will be saved by 'Putenv ()'. This means it must be
In static data zones or allocated from the heap (HEAP). If this environment variable is another 'Putenv ()'
Call redefine or delete, the above string can be released.
/ * The translator increases:
Because Putenv () has such a limitations, it often causes some faults in use.
Mistakes, the GNU LIBC also includes two BSD-style functions:
#include
INT STENV (Const Char * Name, Const Char * Value, INT Replace);
Void unstenv (const char * name);
The setEnv () / unstenv () function can do all Putenv () can do. SetENV () can not be pointed
The new value is restricted to add a new value to the environment variable, but the incoming parameters cannot be empty (NULL). When Replace is 0, if
There is already an Name entry in the environment variable, and the function does not do (reserve the original), otherwise the original is overwritten.
Unsetenv () is used to delete the NAME item from the environment variable. Note: These two functions only exist in BSD and GNU
In the library, other such as the SunOS system do not include them, there will be some compatible issues. We can use
GetENV () / Putenv () is implemented:
Int setnv (const char * value, invicing)
{
Char * envstr;
IF (name == null || value == null)
Return 1;
IF (GetENV (Name)! = NULL)
{
Envstr = (char *) Malloc (Strlen (Name) Strlen (Value) 2);
Sprintf (EnvStr, "% s =% s", name, value);
IF (putenv (envstr));
Return 1;
}
Return 0;
}
* /
Remember that the environment variable is inherited; each process has a different environment variable table copy (Translator Note:
We can see this from the core file). As a result, you can't change the current from one other process.
The environment variable of the process, such as the shell process.
Suppose you want to get the value of environment variable 'term', you need to use the following program:
Char * envvar;
Envvar = GetENV ("Term");
Printf ("The Value for the Environment Variable Term IS");
IF (envvar)
{
Printf ("% s / n", envvar);
}
Else
{
Printf ("not set./n");
}
Now suppose you want to create a new environment variable, the variable is 'myvar', the value is 'myval'.
Here is what you will do:
Static char envbuf [256];
Sprintf (Envbuf, "MyVar =% S", "MyVal");
IF (Putenv (Envbuf))
{
Printf ("Sorry, Putenv () COULDN't Find The Memory For% S / N", Envbuf); / * might exit () or something here if you can't live without it * /
}
1.2.2 How do I read the entire environment variable table?
--------------------------------
If you don't know the name of the environment variable you want, then 'getenv ()' functions are not very useful.
In this case, you must have a deeper understanding of the storage method of the environment variable table.
Global variable, 'char ** envrion', contain pointers that point to the environment string pointer array, each word
The form of string is '"name = value"' (translator Note: "String" in Putenv ()).
This array ends in a 'empty' (NULL) pointer mark. Here is a list of printed current environment variables
A small program (similar to 'printenv').
#include
Extern char ** environ
int main ()
{
Char ** EP = environ;
Char * p;
While ((p = * EP )))
Printf ("% s / n", p);
Return 0;
}
In general, 'Envrion' variables are passed to 'main ()' as an optional third parameter; means speaking,
The above program can be written:
#include
Int main (int Argc, char ** argv, char ** envp)
{
Char * p;
While ((p = * envp )))
Printf ("% s / n", p);
Return 0;
}
Although this method is supported by a wide range of manipulation systems (translator Note: including DOS), this method is actually
Not defined by the POSIX (Translator Note: POSIX: Portable Operating System Intece)). (One
It is also useless.
1.3 How do I sleep less than one second?
=========================
The 'Sleep ()' function in all UNIXs allows only time intervals of seconds. If you want more
Refine, then you need to find a replacement method:
* Many systems have a 'Usleep ()' function
* You can use 'SELECT ()' or 'Poll ()' and set to no file descriptor and test;
The tip is based on one of the functions writes a 'usleep ()' function. (See Comp.unix.questions
Some examples of FAQ)
* If your system has itimers (a lot of yes) (Translator Note: SetitiMer and GetitiMer are two operations
ITIMERS function, use "Man SetitiMer" to confirm your system support), you can use them yourself
'Usleep ()'. (See how to know how to do it)
* If you have a POSIX real-time support, there will be a 'nanosleep () function.
The above method, 'SELECT ()' may be the best transplantability (straightforward, it often
'USLEP ()' is more effective based on ITIMER). However, the practice of capturing signals in sleep
Different; this can be or not a problem based on different applications.
No matter which path you choose, realize that you will be restricted by the system timer resolution (one
Some systems allow for a very short interval, while other systems have a resolution, such as 10 millions
Second, and always retest all set times to that value). Moreover, about 'Sleep ()', you set
The delay is just the minimum (the translator's note: the minimum delayed minimum); after this time delay, there will be
A middle time interval is never scheduled to your process.
1.4 How do I get a more segment time unit ALARM function version?
=============================================================================================================================================================================================================
Today's UNIX system tends to use the 'setitimer ()' function to achieve alarm, it is more than simple 'alarm ()'
The number has a higher resolution and more options. One user generally needs to assume 'alarm ()'
And 'setitimer (itimer_real) may be the same underlying timer, and assume that two two
The method will cause confusion.
ITIMERS can be used to achieve disposable or repeated signals; and generally there are three different timers available:
`Itimer_Real '
Count real (wall clock) time, then send 'SigalRM' signals
`Itimer_virtual '
Counting Process Virtual (User Central Processor) Time, then send 'sigvtalrm' signal
`Itimer_Prof '
Count user and system central processor time, then send 'sigprof' signals; it supplies
Profiling
However, ITIMERS is not part of many standards, although it is available since 4.2BSD. POSIX real-time standard
Quasi-expansion defines similar but different functions.
1.5 How does the father and a child communicate?
======================
A pair of parent-child processes can communicate between processes (pipelines, sockets, messages, total
Enjoy the memory), but can also have one by using them as the mutual relationship of the parent-child process.
Some special methods.
A most obvious way is that the parent process can get the exit status of the child process.
Since the child process inherits the file descriptor from its parent process, the parent process can open both ends of the pipe.
Then Fork, then the parent process closes the end of the pipe, the child process closes the other end of the pipe. This is what you are from you
The process call 'POPEN ()' function runs another program, that is, you can
'POPEN ()' returned file descriptor to write operation and sub-process as its own standard input, or
You can read this file descriptor to see what is written to the standard output. ('POPEN ()' function
Mode parameter defines your intent (Translator Note: Mode = "R" is read, mode = "w" is written); if you
I want to read and write, then you can use the pipe to do it yourself.)
Moreover, the child process inherits an anonymous shared memory segment (or passing the mapping) by the parent process with MMAP function mapping
Special file '/ dev / zero'; these shared memory segments cannot be accessed from unrelated processes. 1.6 How do I remove the zombie process?
=========================
1.6.1 What is the zombie process?
--------------------
The child process created by a program is completed in advance, and the kernel still saves some of its information so that the father
The process will need it - for example, the parent process may need to check the exit status of the child process. In order to get these letters
Interest, parent process call 'Wait ()'; when this call occurs, the kernel can discard this information.
After the child process is terminated to the parent process, the child process is called the 'zombie process'.
('Zombie'). (If you use 'ps', this sub-process will have a 'Z' appearing in its status area.
It is pointed out in this point. Even if it is not executed, it still occupies a position in the process table. (It does not consume it
It resources, but some tool programs displays the wrong numbers, such as the use of the central processor; this is
Because some parts of the Space Process Table are shared (Overlaid). )
This is not good, because the process table has a fixed upper limit for the number of processes, and the system will use light. Even if the system is not
Useful light, each user can perform the number of processes in the same time, it is always less than the system limit.
By the way, this is what you need to always check whether 'fork ()' failed.
If the parent process is not called the WAIT function, the child process will be charged by the 'init' process, it will control the child.
Remove the work after exiting. ('Init 'is a special system program, the process number is 1 - it actually
The first program running after the system is started),
1.6.2 How do I avoid their appearance?
----------------------------
You need to make a child process to terminate the termination of each child 'Wait ()' (or 'WaitPid ()',
'Wait3 ()', etc .; or, on some systems, you can instruct your system to exit the child process
Not interested in state. (Translator Note: On the SYSV system, you can call the Signal function and set the sigcld signal.
SIG_IGN, the system will not produce a zombie process, detailed description, see << Advanced Programming >> 10.7)
Another method is * twice * 'fork ()', and make the sub-process that keeps directly exit, thus causing grandson
The process turns into orphan process (Orphaned), so that the init process will be responsible for clearing it. I want to get this procedure, participate
See the function 'fork2 ()' of the paracene.
In order to ignore the child process, you need to do the following steps (query your system man page to know if this is
Often work):
Struct SigAction SA;
Sa.sa_handler = sig_ign;
#ifdef sa_nocldwait
sa.sa_flags = sa_nocldwait;
#ELSE
Sa.sa_flags = 0;
#ENDIF
SiGemptySet (& sa.sa_mask);
SigAction (Sigchld, & Sa, Null);
If this is successful, then 'Wait ()' function set will no longer work properly; if any of them
Call, they will wait until * all * child processes have been exited, then return to fail, and
'Errno == Echild'. Another trick is to capture the Sigchld signal, and then make the signal handler call 'WaitPID ()' or
'Wait3 ()'. See the full programs of the paramount section.
1.7 How do I make my program run as a daemon?
=======================================
A "daemon" process is usually defined as a background process, and it does not belong to any end
Session, (Terminal Session). Many system services are implemented by daemons; such as network services, printing, etc.
Simply launch a program in the background is not enough to be these long running programs; that method is not positive
It is determined from starting its terminal from starting its terminal (DETACH). Moreover, the universally accepted party of the daemon is started.
The method is simply manually executed or executed from the RC script (Translator Note: RC: Runcom); and hope this guard
The program places it * itself * to the background.
Here is the step of becoming a daemon:
1. Call 'fork ()' so that the parent process can exit, so that the control is returned to run your program.
Command line or shell program. Need this step to ensure that the new process is not a process group leader process (Process
Group Leader. Next, 'setsid ()' will fail because you are a process group leader process.
2. Call 'setsid ()' to become a process group and the header process of the session group. Due to a control terminal
Associated with a session, and this new session has not yet got a control terminal, our process is not
There is a control terminal, which is a good thing for daemons.
3. Call the 'fork ()' again so the parent process (session group header process) can exit. This means us, one
Non-session group leading processes never re-acquire control terminals.
4. Call 'chdir ("/")' Confirm that our process does not hold any directory in the usage. Don't do this guide
System administrators cannot uninstall a file system because it is our current working directory.
[Similar, we can change the current directory to the directory where important files running on the daemon]
5. Call 'umask (0)' so we have full control of anything we wrote. We don't know
Tao we inherited what kind of umask.
[This step is optional] (Translator Note: Here you refer 5, because the daemon does not necessarily need to write files)
6. Call 'Close ()' Close the file descriptors 0, 1 and 2. This way we released the label inherited from the parent process
Accounting, standard output, and standard error output. We can't know that these text descriptors may
Where is the redirection? Notice that many daemons use 'sysconf ()' to confirm
'_Sc_open_max' limit. '_Sc_open_max' tells you that each process can play
The maximum number of files opened. Then use a loop, the daemon can close all possible file sketches.
Character. You must decide what you need to do or don't do it. If you think there is a possible open file
Commissioning, you need to close them because the system has a limit on the number of files simultaneously.
7. Establish a new file descriptor for standard input, standard output, and standard error output. Even if you don't plan
Use them, open them, do not lose a good idea. Accurate operation These descriptors are based on each
Hobbies; for example, if you have a log file, you may want to use it as a standard output and standard
The wrong output is turned on, and the '/ dev / null' is opened as a standard input; as an alternative method, you can
Open '/ dev / console' as standard error output and / or standard output, and '/ dev / null' as standard input, or any other combination method for your daemon. (Translator Note: One
Use the DUP2 function atomicization and copy the file descriptor, see << Advanced Programming >> 3.12)
If your daemon is started by 'inetd', almost all these steps are not required (or not recommended
use). In that case, standard input, standard output, and standard error output are specified as a network.
Connect, and 'fork ()' calls and sessions should not be done (so as not to cause chaos of 'inetd'). only
There are two steps of 'chdir ()' and 'umask ()'.
1.8 How do I examine the process of the system like the PS program?
========================================
You really shouldn't want to do this.
So far, the optimization of transplantation is to call 'POPEN (PSCMD, "R")' and process its output. (PSCMD
It should be that '"ps -ef"' on the SYSV system, the BSD system has a lot of possible display options:
Choose one. )
There are two complete solutions for this problem in the template; an application for SunOS 4, it needs root rights
Limited to perform and use the 'KVM_ *' routine to read information from the kernel data; another suitable for SVR4 systems
(Including Sun OS 5), it uses the '/ proc' file system.
It is simpler in the system with SVR4.2 style '/ proc'; as long as you are from the process number of each of interest
File '/ proc / process number / psinfo' reads a PSINFO_T structure. However, this may be the clearest side
The law may be the least good way to support. (On FreeBSD '/ proc', you
'/ Proc / process number / status' reads a half-undocuMenTED printable word
Strings; Linux has some things like it)
1.9 Given a process number, how do I know that it is a running program?
============================================================================================================================================================================================================= ===
Use the 'kill ()' function, and there is 0 as a signal code.
There are four possible outcomes from this function:
* 'Kill ()' Returns 0
- This means that a process given for this process number exits, the system allows you to send a signal to it. This
Whether the process can be related to different systems.
* 'Kill ()' Return -1, 'errno == esrch'
- Either there is no process for a given process number, or an enhanced security mechanism causes the system to deny it.
in. (In some systems, this process may be a zombie process.)
* 'Kill ()' Returns -1, 'errno == eperm'
- The system does not allow you to kill this particular process. This means that the process exists (it may be a zombie process), or it is either strictly enhanced security mechanism (for example, your process is not allowed to send signals)
Give * any person *).
* 'Kill ()' Returns -1, with other 'errno' values
- You have trouble!
The most skills use it is to call "success" or "fail" with 'eperm' means process deposits.
And other mistakes mean it does not exist.
If you specifically write a program for systems (or all similar systems) of the '/ Proc' file system, a replacement
The method exists: Check if the 'proc / process number' exists is feasible.
1.10 System function, PCLOSE function, what is the return value of WaitPid functions?
============================================================================================================================================================================================================= =========
'System ()', 'pClose ()' The return value of 'WaitPid ()' is not like the exit value of my process (exit
Value) (Translator Note: Exit "The parameters given when mod () or _exit ()) ... or exit the left shift 8
Bit ... how is this?
The man page is right, you are also right! If you check the 'WaitPid ()' you will find the return of the process
The value is encoded. Under normal circumstances, the return value of the process is 16 bits high, while the remaining bit is used to do other things.
If you want to be portable, you can't rely on this, and suggestion is the macro you are using. These macros
It is illustrated in the documentation of 'Wait ()' or 'Wstat'.
For different purposes defined macros (in '
`Wifexited (stat) '
Returns non-0 if the child process is normal to exit
`WexitStatus (Stat) '
Exit code returned by the child process
`Wiferned (stat) '
Returns non-0 if the child process is terminated by the signal
`WTERMSIG (STAT) '
Termination sub-process signal code
`Wifstopped (STAT) '
If the child process is suspended, it returns non-0
`Wstopsig (stat) '
Signal code suspended by the sub-process
`Wifcontinued (Stat) '
If the status is to indicate that the child process continues to execute, return non-0
`Wcoredump (stat) '
If 'WifSignaled (Stat)' is non-0, if this process generates a memory mapping file
(CORE DUMP) returns non-0
1.11 How do I find out the memory usage of a process?
==============================================================================================================================================
If you provide, see 'getrusage ()' manual page
1.12 Why is the size of the process not reduced?
============================= When you use the 'Free ()' function to release the memory to the stack, almost all systems * Not * reducing your program
Use of memory. The memory released by 'free ()' is still part of the process address space and will
Required by the future 'malloc ()' requests.
If you really need to release the memory to the system, see the use of 'mmap ()' allocation private anonymous memory mapping
(Private Anonymous MapPings). When these memory maps are canceled, the memory is really released.
system. Some 'malloc ()' implementation methods (such as in the GNU C library) automatically use 'mmap ()'
Implement high-capacity assignments; these memory blocks are released back to the system with 'free ()'.
Of course, if your program is increased and you don't think it is like this, you may have a 'memory
Len '(' Memory Leak ') - It is defective in your program (bug) that causes unused memory.
1.13 How do I change my name (ie "PS" see the name)?
================================================================================★
In the BSD-style system, 'PS' programs actually examine the address space of the running process to find the current
'Argv []' and display it. This makes the program can change its by simple modification 'argv []'
first name.
In the sysv style system, the name of the command and the general header 80 byte of the parameter is stored in the U-area in the process (
U-area, so you can't be modified directly. There may be a system call to modify it (not this),
But other words, only one method is to implement a 'exec ()', or some core memory (danger,
And only root is likely).
Some systems (worth noting that solaris) can have two different versions of 'PS', one is
'/ Usr / bin / ps' has SysV behavior, while the other has a BSD in '/ usr / ucb / ps'. in
In these systems, if you change 'argv []', then the BSD version of 'PS' will reflect this change, and
The SYSV version will not.
Check if your system has a function 'setPROCTILE ()'.
1.14 How do I find the corresponding executable of the process?
=====================================
This problem can be used as a 'common unscading problem' ('frequently unanswered quintions')
A good candidate, because in fact, this problem often means that the program is designed to be defective. :)
The 'Best Guess' ('Best Guess') you can do is obtained by examining the value of 'argv [0]'. in case
It includes a '/', then it may be absolute or relative (for the current directory at the beginning of the program). If you don't include, then you can imitate the shell for the 'path' variable.
Inquiry to find this program. However, it cannot be guaranteed, because it is possible to execute the program 'argv [0]' is
Some any values nor exclude this executable may have been renamed or deleted after execution.
If all you want to do, you can print a suitable name that appears together with the error message, then it is best
The method saves the value of 'Argv [0]' in the 'Main ()' function to make the entire program
use. Although it is not guaranteed that 'argv [0]''s value is always meaningful, but in most cases it is the best
select.
The most common cause of this question is to intend to locate the profile of their programs. This is considered to be
Wild form; directory containing executables should * only contain executable files, and based on management
Requirements often attempts to place a configuration file in a file system different from the executable file.
Trying to do this is more uncommon but more formal reason to allow program call 'exec ()' to perform it.
Irself; this is a process for complete reinitialization (such as the version used for some 'Sendmail')
Measures (such as a daemon capture a 'Sighup' signal).
1.14.1 SO Where do I put my configuration files life?
-------------------------------------------------- ---
1.14.1 So, where do I put the configuration file?
Arrange the correct directory for the configuration file always depends on the characteristics of the UNIX system you use;
'/ Var / opt / package', '/ usr / local / lib', '/ usr / local / etc,, or any other one
Some possible places. User-defined profiles are usually hidden in "." At '$ HOME' (
For example, '$ home / .exrc').
It usually means any station from a package (package) that can be used in different systems.
The location of the SiteWide profile has a set default value, and it may be used in
Configure the '--prefix' option in the script (AutoConf script assembly to do this). You will want to allow
Xu this default is overloaded by an environment variable when executed. (If you don't use the configuration script,
So when you compile, this location default value is placed in a '-d' option into the project file (Makefile), or
Put it in a 'config.h' header file or do other similar work)
-
User-defined configuration The file name "." Header is placed in '$ home'. "
The subdirectory of the header "when multiple configuration files are required." (When the column directory, the file name is
"." The file or directory default is ignored. ) Avoiding multiple files in '$ home', because
It will cause very messy situations. Of course, you should also allow users to overload this through an environment variable.
position. Even if you cannot find a user's configuration file, the program should still be performed in a suitable manner.
1.15 When you die, my process does not get a SIGHUP signal?
=================================================
Because I have not imagined this.
'Sighup' is a signal that means that "terminal line is hanging" according to the practice. It is independent of the parent process and is usually generated by the TTY driver (and passed to the front desk).
However, as part of the session management system, there are two situations.
The next 'sighup' will send it when a process is dead:
* When a terminal device is associated with a session, and the session of the session is dead,
'Sighup' is sent to all front-end process groups of this terminal device.
* When a process dies, a process group becomes an orphan, and one or more processes in the process group
When it is * suspended * status, then 'Sighup' and 'Sigcont' are sent to this orphan process
All members of the group. (An orphan process group refers to there is no member process in the process group.
The parent process belongs to other process groups of the same session as the process group. )
1.16 How do I kill all the derived processes of a process?
========================================
There is no completely universal method to do this. Although you can determine the output of the output of 'ps'
The interruptity of the buildings, but because it only indicates a state of the system's instant, it is not reliable.
However, if you start a child process, and it may generate its own child process, and you intend to kill
Death the entire generated transaction (JOB), the solution is to place the first child process in a new process group,
When you need to kill the entire process group.
It is recommended to create a function to create a process group is 'setpgid ()'. Use this function in may be
Not using 'setpgrp ()', because the last one is different in different systems (in some system 'setgrp ();'
Equivalent to 'setpgid (0, 0);', on other systems, 'setpGRP ()' and 'setPgID ()' are identical).
See the transaction of the modal chapter - Control Example Program.
Placing a child has some impact on its own process group. Special unless you explicitly put the process
The group is placed on the front desk, which will be considered a background transaction and has the following results:
* Trying from the process read from the terminal will be paused by the 'sigttin' signal.
* If you set the terminal mode 'TOSTOP', the process that tries to write to the terminal will be filed by 'Sigttou'
Signal pause. (Attempt to change the terminal mode, it also leads to this result, and whether the current 'TOSTOP' is
Setting)
* The child process will not receive a keyboard signal emitted from the terminal (such as 'sigint' or 'sigquit')
In many applications, the input and output will always be redirected, so the most prominent impact will be lost keyboard
signal. The parent process needs to arrange the program at least capture 'siginit' and 'sigquit' (possibly in the case,
There is also 'sigterm') and clear background transactions in need.
Unix Programming FAQ Solution "2 General File Operation"
2. General file operations (including pipes and sockets)
***************************************
Please refer to the socket,
http://www.lcg.org/sock-faq/
2.1 How to manage multiple connections?
======================
"I want to monitor more than one file descriptor (FD) / connection (stream), what should I do?"
Use the SELECT () or Poll () function.
Note: SELECT () is introduced in BSD, while poll () is the product of SysV Stream stream control. therefore,
Here, there is a platform transplant: pure BSD system may still lack poll (), and earlier
There may be SVR3 systems may not be Select (), although it will be added in SVR4. At present, both are POSIX.
1G standard, (Translator Note: Therefore, both in Linux)
SELECT () and Poll () do the same thing in nature, just the completion method is different. Both
By verifying a set of file descriptors to detect if there is a specific time, it will occur and at a certain time.
Waiting for it.
[Important: Regardless of SELECT () or Poll () does not work greatly to ordinary files, they are highly used
For socket, pipe (PIPE), pseudo terminal (PTY), terminal device (TTY), and other characters
Equipment, but these operations are system-dependent. ]
2.2.1 How do I use the SELECT () function?
------------------------------
The interface of the select () function is mainly based on a type of fd_set 'type. It ('fd_set')
It is a set of file descriptors (FDs). Since the length of the fd_set type is different on different platforms,
This type of variable should be handled in a set of standard macros:
FD_SET SET;
FD_ZERO (& SET); / * Clear Set Clear * /
FD_SET (FD, & SET); / * Add FD to set * /
FD_CLR (FD, & SET); / * Clear FD from SET * /
FD_Isset (FD, & SET); / * If FD is true in SET * /
In the past, an fd_set usually contains less than or equal to 32 file descriptors because fd_set is actually only
Used an int to implement, in most cases, check FD_SET can include any value
The file descriptor is the responsibility of the system, but how much you can make your fd_set, you should check / repair
Change the value of the macro FD_SETSIZE. * This value is a system-related *, and checks SELECT () in your system.
MAN manual. There are some systems that support more than 1024 file descriptors. [Translator Note:
Linux is such a system! You will find the result of sizeof (fd_set) 128 (* 8 =
FD_setsize = 1024) Although few things you will encounter. ]
SELECT's basic interface is simple:
Int Select (int NFDS, fd_set * readset, fd_set * writeset,
FD_SET * EXCEPTSET, STRUCT TIMEVAL * TIMEOUT
among them:
NFDS: The number of file descriptors that need to be checked, the value should be the maximum number in three groups fd_set
Big big, not the total number of actual file descriptors.
Readset: A set of file descriptors used to check readability.
WriteSet: A set of file descriptors used to check the writeability.
ExceptSet: File descriptors used to check unexpected states. (Note: Error is not an unexpected state)
Timeout: NULL pointer represents an unlimited wait, otherwise it is pointer to the TimeVal structure, the most
Long wait time. (If the TV_SEC and TV_USEC are equal to 0, the file descriptor
The state is not affected, but the function does not hang)
The function will return the total number of corresponding operation file descriptors for response operation, and the three sets of data are modified properly, and only some of the response operations are not modified. Then you should use the fd_isset macro to find the returned text
Part descriptor group.
Here is an example of a simple test single file descriptor:
Int isready (int FD)
{
Int rc;
FD_SET FDS;
Struct TimeVal TV;
FD_ZERO (& FDS);
FD_SET (FD, & FDS);
TV.tv_sec = tv.tv_usec = 0;
RC = SELECT (FD 1, & FDS, NULL, NULL, & TV);
IF (RC <0)
Return -1;
RETURN FD_ISSET (FD, & FDS)? 1: 0;
}
Of course, if we use the NULL pointer into fd_set, this means that our happening to this operation.
Not interested, SELECT () will still wait until it occurs or exceeds the wait time.
[Translator Note: In Linux, Timeout refers to the time spent in a non-SLEEP state, not
In fact, the past time, this will cause problems with non-Linux platform transplantation. Transplantation
The title is also included in the System V style. Select () will set Timeout to undefined
Null status, and in the BSD is not the case, Linux follows System V at this point, so in retro
Pay attention to the Timeout pointer issue. ]
2.1.2 How do I use Poll ()?
---------------------------------------------------------------------------------------------------------------------------------------
Poll () accepts a pointer to the list of structural 'struct pollfd', including you want to test
Document descriptors and events. The event is determined by a bit mask in the event domain. Current structure
After calling, it will be filled out and returned after the event. In SVR4 (possated earlier versions earlier)
"Poll.h" file contains some macro definitions used to determine events. The waiting time of the event is accurate to milliseconds
(But confused is the type of waiting time is int), when the waiting time is 0, the Poll () function is immediately
Returns, -1 makes poll () all the way to a specified event. The following is the structure of POLLFD.
Struct pollfd {
INT fd; / * file descriptor * /
Short events; / * Waiting event * /
Short Revents; / * Actual event * /
}
It is similar to SELECT (). When returning a positive value, representing the number of file descriptors that meet the response event,
If it returns 0, the representative does not have an event in the specified event. If you find that you return to negotiate, you should check now.
Errno, because this representative has an error.
If there is no incident, Revents will be emptied, so you don't have to be more.
Here is an example:
/ * Detects two file descriptors, which are general data and high priority data, respectively. If the event occurs
The function handler (), no time limit, with related descriptors and priority.
Errors or descriptors hang. * /
#include
#include
#include
#include
#include
#include
#include
#include
#define normal_data 1
#define HiPri_Data 2
INT Poll_Two_NORMAL (int FD1, INT FD2)
{
Struct Pollfd Poll_List [2];
int Retval;
Poll_list [0] .fd = fd1;
Poll_list [1] .fd = fd2;
Poll_list [0]. Events = pollin | pollpri;
Poll_list [1] .Events = pollin | pollpri;
While (1)
{
Retval = poll (Poll_List, (unsigned long) 2, -1);
/ * Retval is always greater than 0 or is -1 because we work in blocking * /
IF (RetVal <0)
{
FPRINTF (stderr, "poll error:% s / n", strrror (errno));
Return -1;
}
IF (((Poll_List [0] .revents & pollhup) == PollHup) ||
((Poll_List [0] .revents & pollerr) == Pollerr) ||
((Poll_List [0] .revents & pollnval) == Pollnval) ||
((Poll_List [1] .revents & pollhup) == PollHup) ||
((Poll_List [1] .revents & pollerr) == Pollerr) ||
((Poll_List [1] .revents & pollnval) == pollnval))
Return 0;
IF ((Poll_List [0] .revents & pollin) == pollin)
Handle (poll_list [0] .fd, normal_data);
IF ((Poll_List [0] .revents & pollpri) == pollpri)
Handle (poll_list [0] .fd, hipri_data);
IF ((Poll_List [1] .Revents & Pollin) == Pollin)
Handle (poll_list [1] .fd, normal_data);
IF ((Poll_List [1] .Revents & Pollpri) == Pollpri)
Handle (poll_list [1] .fd, hipri_data);
}
}
2.1.3 Can I use SysV IPC and SELECT () / poll () at the same time?
-------------------------------------------------- -
* Cannot be. * (Unless it is on AIX, because it uses an incomparably weird method to implement this combination)
In general, you will bring a lot of trouble while using SELECT () or Poll () and SYSV message queues. Sysv
IPC objects are not handled by file descriptors, so they cannot be passed to SELECT () and
Poll (). There are several solutions here that have different degrees of roughness:
- Abandon the use of SYSV IPC. :-)
- Use fork (), then let the child process to process SYSV IPC, then use pipes or sockets and parent processes
speak. The parent process uses SELECT ().
- Same as above, but let the child process uses Select (), then communicates with the father with a message queue.
- Arrange the process Send a message to you, send a signal after sending a message. * Warning *: Be good
This is not simple, it is very easy to write the program that will lose the message or cause a deadlock.
...... There are other methods.
2.2 How can I know and the other party's connection is terminated? =======================================
If you read a pipe, socket, FIFO and other devices, when you turn off the connection, you will
Get a file end value (EOF) (READ () returns zero-byte read). If you try to bring a pipe or
Set of interfaces, when the read party is closed, you will get a Sigpipe signal, it will end the process
Think unless the processing method is specified. (If you choose to block or ignore the signal, Write () will return with EPIPE error
Out. )
2.3 What is the best way to read a directory?
===============================
There have been many different directory reading methods in history, but you should use the POSIX.1 standard.
The OpenDir () function opens a specified directory; readdir () reads the directory in a standard format;
CloseDir () Close the descriptor. There are some other Rewinddir (), telldir () and seekdir ()
Wait for functions, I believe it is not difficult to understand.
If you want to match the file match ('*', '?'), Then you can use glob () existing in most systems.
Function, or you can view the fnmatch () function to get the matching file name, or use ftw () to pass through
A directory tree.
2.4 How can I know that a file is opened by another process?
=================================================================================================================================================================================
This is another "issue that is often not answered", because in general, your program will not care about whether the file is
It is being opened by others. If you need to process concurrent operations, you should use the consultic file lock.
Generally speaking, it is difficult to do this, like Fuser or LSOF, you can tell you tools for file usage.
By analyzing kernel data to achieve the goal, this method is very unhealthy! And you can't get from your program
This is called to obtain information, because maybe when they perform completion, the use of files is instantaneous
There is a change, you can't guarantee the correctness of this information.
2.5 How do I lock a file?
=========================
There are three different file locks, these three are "consultative", that is to say they rely on programs
Cooperation, so all program blockade policies in a project is very important, when your program needs
Be careful when sharing files with third-party software.
Some programs use file lock files such as FileName.lock and then simply test whether such files exist. This method is obviously not very good, because when the process of generating a file is killed, the lock file still exists.
This file may be blocked permanently. The process number PID generated in the uUCP is stored in the file, but doing so
Still not insurance, because the use of PID is a recycled.
Here is three file lock functions:
FLOCK ();
LOCKF ();
FCNTL ();
FLOCK () is derived from BSD, but currently can be found on most UNIX systems, in a single master
FLOCK () is simple and effective, but it cannot work on NFS. There is also a bit confused in Perl.
FLOCK () function, but is implemented inside Perl.
Fcntl () is the only file lock that meets the POSIX standard, so it is also uniquely portable. It also
It is the most powerful file lock - is also the most difficult. On the NFS file system, the FCNTL () request will be handed
Hand it to the daemon called RPC.lockd, then is responsible for the LockD dialogue between the host, and flock ()
Different, FcntL () can enable the blockade on the recording layer.
Lockf () is just a simplified FCNTL () file lock interface.
No matter which file lock you use, please remember to update all your files with Sync before the lock take effect.
input Output.
LOCK (FD);
Write_to (Some_Function_OF (FD));
Flush_output_to (fd); / * must be flushing and output before the lock is displayed * /
UNLOCK (FD);
Do_something_else; / * Maybe another process will update it * /
LOCK (FD);
Seek (fd, somewhere); / * Because the original file pointer is not safe * /
Do_something_with (fd);
...
Some useful FCNTL () blocked methods (for simple omissions):
#include
#include
READ_LOCK (int FD) / * A shared file lock on the entire file * /
{
Fcntl (fd, f_setlkw, file_lock (f_rdlck, seek_set);
}
Write_lock (int FD) / * A exclusive file lock on the entire file * /
{
Fcntl (fd, f_setlkw, file_lock (f_wrlck, seek_set);
}
Append_lock (int FD) / * The lock end of the blocking file,
Other processes can access existing content * /
{
Fcntl (fd, f_setlkw, file_lock (f_wrlck, seek_ek_ek);
}
The File_lock function used earlier is as follows:
Struct Flock * file_lock (Short Type, Short Man)
{
STATIC STRUCT FLOCK RET;
RET.L_TYPE = TYPE;
RET.L_START = 0;
RET.L_WHENCE = De;
RET.L_LEN = 0;
RET.L_PID = getPid ();
Return & Ret;
}
2.6 How can I find a file has been updated by another process?
============================================== This is almost A question that is often not answered, because people who ask this question usually hope to have a department
The signage of the dominant reflects the current directory or file being modified, but there is no way to ensure the implementation of the transplantability.
IRIX has a non-standard function to monitor file operations, but never heard of other platforms.
Similar function.
In general, you can do our best to use the fstat () function, by monitoring the Mtime and CTIME
Can you learn that the file is modified or deleted / connected / renamed, it sounds complicated,
With you should reflect why you have to do this.
2.7 How do DU work?
=========================
DU only simply uses stat () (more accurately uses the lstat () function) to traverse each file in the directory structure.
And catalogs and the disk blocks they occupy.
If you want to know the details, you will always say this: "Read the source code, the old brother!"
The source code of BSD (FreeBSD, NetBSD, and OpenBSD) is in the source code of these FTP websites.
GNU version of the source code can of course find in any GNU mirror site - premise is that you know
What is the package.
2.8 How do I get the length of a file?
===============================
Use stat () or after the file is opened.
These two calls fill in file information in a structure, where you can find such as file owners, attributes,
Size, final access time, last modification time, etc. About this file.
The following program generally demonstrates how to use Stat () to get the file size.
#include
#include
#include
#include
INT GET_FILE_SIZE (Char * Path, Off_t * Size)
{
Struct stat file_stats;
IF (Stat (Path, & File_Stats))
Return -1;
* size = file_stats.st_size;
Return 0;
}
2.9 How do I expand in the file name in the SHELL?
==============================================
The standard usage of '~' is as follows: If you use or follow one '/' alone or later, '~' is treated as the home directory of the current user, [Translator Note: In fact '~' is replaced with $ HOME environment Variables], if '~'
It is the home directory of that user directly with one user name. If there is no suitable match
With, the shell will not do any changes.
Please note that there may be some files to be headed by '~', regardless of the green sauce, will replace you will make you
The program cannot open these files. In general, pass from the shell through the command line or environment variable.
The order of the file does not need to be replaced, because the shell has been doing it for you, and the program is generated,
The household is entered, or it should be replaced from the configuration file.
Here is a C implementation with a standard String class:
String expand_path (const string & pat)
{
IF (path.length () == 0 || Path [0]! = '~')
Return Path;
Const char * pfx = NULL;
String :: size_type pos = path.find_first_of ('/');
IF (path.length () == 1 || POS == 1)
{
Pfx = getenv ("home");
IF (! pfx)
{
// We want to replace "~ /", but $ home is not set
Struct Passwd * Pw = getPwUID (getuid ());
IF (PW)
PFX = PW-> PW_DIR;
}
}
Else
{
String User (Path, 1, (POS == String :: Npos)? String :: Npos: POS-1);
Struct Passwd * pw = getpwnam (user.c_str ());
IF (PW)
PFX = PW-> PW_DIR;
}
// If we can't find the option to replace, return PATH.
IF (! pfx)
Return Path;
String Result (PFX);
IF (POS == String :: Npos)
Return Result;
if (Result.Length () == 0 || Result [Result.Length () - 1]! = '/')
RESULT = '/';
Result = path.substr (POS 1);
Return Result;
}
2.10 What can I do in a well-known pipe (FIFO)?
==============================
2.10.1 What is a famous pipe?
-----------------------
A well-known pipe is a special document that can transmit data between mutual related processes. One or more processes
The data is written within the other end by one process. A well-known pipeline is visible in the file system,
That is to say that LS can be seen directly. (The famous pipe is also known as FIFO, that is, first out.)
A well-known pipeline can link unrelated processes, and unknown ordinary pipelines can only connect the father and son process - unless you try to try - of course, you can contact two unrelated processes. A famous pipe is
Strictly unidirectional, although the unnamed pipe in some systems is two-way.
2.10.2 How do I build a famous pipe?
-------------------------------
Establish a famous pipe in mutual interaction, you can use the mknod or mkfifo command. In some systems
In the middle, MkNod may be in the / etc directory, that is, may not appear in your directory,
So please check the MAN manual in your system. [Translator Note: Under Linux, you can take a look at FIFO (4)]
To create a famous pipe in the program:
/ * Clearly set Umask, because you don't know who will read and write the pipeline * /
Umask (0);
IF (MKFIFO ("Test_fifo", S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))
{
PERROR ("MKFIFO");
Exit (1);
}
You can also use MKNOD. [Translator Note: MkNod is not recommended under Linux, because there are many bugs
Be more careful under NFS, you can use MKFIFO to use MKNOD because MKFIFO () is POSIX.1
standard. ]
/ * Clearly set Umask, because you don't know who will read and write the pipeline * /
Umask (0);
IF (MKNOD ("Test_fifo",
S_IFIFO | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP,
0))
{
PERROR ("mknod");
Exit (1);
}
2.10.3 How do I use a famous pipe?
-------------------------------
It is very simple to use a famous pipe: you open it as an ordinary file, use read () and
Write () is operated. However, it may cause blocking when using open () using open (), and some common laws can be
Reference.
* If you open it in a reading method (o_rdwr), it will not cause blocking.
* If you open in a read-only mode (o_rdonly), open () will block until you have a write side.
Open the pipe unless you specify o_nonblock to ensure that it is successful.
* The same writes (O_WRONLY) will also block the read party to open the pipe, if
O_nonblock is specified Open () will end in failure.
When reading and writing a famous pipe, the attention points and the normal pipeline and socket: when writing
Turn off the connection READ () returns EOF if there is no listener Write () gets a signal of Sigpipe,
Shielding or ignoring this signal will trigger an EPIPE error exit.
2.10.4 Can I use a famous pipe on NFS?
--------------------------------
No, there is no relevant function in the NFS protocol. (You may be able to use a famous tube in an NFS file system
Duan contact two processes in the client. )
2.10.5 Can you let multiple processes to write data to a famous pipe?
-----------------------------------------------
If the data written is less than the size of the PIPE_BUF, the data intersection does not occur. but
Since there is no limit to writing, the read () operation reads as much as possible, so you can't
Know that the data is written.
The size of the PIPE_BUF cannot be less than 512 according to the POSIX standard, and some systems are defined in
Consulting for individual pipes.
2.10.6 Application of a famous pipeline
---------------------
"How do I time the server and the two-way communication of multiple clients?"
A pair of forms often appear, as long as the instructions issued to the server each time the client is smaller than PIPE_BUF, it
They can send data to the server via a famous pipe. The client can easily know the server
Pipe name of the data.
But the problem is that the server cannot be derived with a pipeline with all customers. If more than one customer is
Reading a pipe, it is impossible to ensure that each customer gets its own reply.
One way is that each customer builds its own reading pipe before sending information to the server, or let the service
A pipeline is established after obtaining the data. Using the customer's process number (PID) as a pipe name is a common side
law. Customers can tell the server first, then to the tube named by the process number
Reading reply in the road
3. Terminal input / output
*****************
3.1 How do I make my program not referred to?
=======================================================================================================================================================
How can I make my program don't refer to input, as I ask my password when logging in?
There is a simple method, there is also a slightly complex point method:
The simple method is to use the 'getPass ()' function, which can be found on all UNIX systems. It takes one
A given string parameter as a prompt ("PROMPT). It reads input until you read a 'EOF' or change
Tarer (Translator Note: 'EOF' Entering with '^ D', and the newline character is '^ m' or carriage return) and then return one
Pointing a string pointer in a static memory area that contains a hit character. (Translator Note: String does not include a wrap
symbol)
The complex method is to use the 'tcgetattr ()' function and the 'tcsetttr ()' function, both functions are used.
A 'Struct Termios' structure operates the terminal. Below these two sections should be able to set the reflective state and
No reflective state.
#include
#include
#include
#include
Static struct termios stored_setting;
Void echo_off (void)
{
Struct Termios New_Settings;
Tcgetattr (0, & stored_setting);
New_settings = stored_setting;
New_SETTINGS.C_LFLAG & = (~ ECHO);
TcSetattr (0, Tcsanow, & new_settings);
Return;
}
Void echo_on (void)
{
TcSetattr (0, tcsanow, & stored_settings);
Return;
}
Both the two programs used are defined in the POSIX standard.
3.2 How do I read a single character from the terminal?
============================== How do I read a single character from the terminal? My program is always waiting for the user to press Enter.
The terminal is typically in the standard mode, and in this mode input is always read in line after editing. You can
Set the terminal as a non-standard mode, and in this mode you can set it in the input delivery
How many characters are read before your program. You can also set the timer of non-standard mode to 0, this timer
Empty your buffer according to the set time interval. Do you make you use 'getc ()' functions
Get the user's button input. The 'tcgetattr ()' function we use and 'tcsetttr () function
It is defined in POSIX to manipulate the 'Termios' structure.
#include
#include
#include
#include
Static struct termios stored_setting;
Void Set_KeyPress (VOID)
{
Struct Termios New_Settings;
Tcgetattr (0, & stored_setting);
New_settings = stored_setting;
/ * Disable canonical mode, and set buffer size to 1 byte * /
New_SETTINGS.C_LFLAG & = (~ ICANON);
NEW_SETTINGS.C_CC [VTIME] = 0;
NEW_SETTINGS.C_CC [VMIN] = 1;
TcSetattr (0, Tcsanow, & new_settings);
Return;
}
Void Reset_KeyPress (Void)
{
TcSetattr (0, tcsanow, & stored_settings);
Return;
}
3.3 How do I check if a button is 摁?
=======================================================================================================================================================
How do I check if a button is 摁? I use 'kbhit ()' functions on DOS, but in UNIX
It seems that there is no function of the same role?
If you set the terminal as a single character mode (see the previous question solution), then (in most systems)
You can use the 'SELECT ()' function or 'poll ()' function to test the input whether it is readable.
3.4 How do I move the cursor in the screen?
===============================
How do I move the cursor in the screen? I don't want to make full screen editing without Curses. (Translator Note: Curses
It is a humen / c programming tool library, which provides many function calls that can be used without care type
Under the case of manipulating the display of the terminal).
Don't joking, you may not want to do this. Curses Tools Library knows how to control different terminal types
The exhibited strange things; of course Termcap / Terminfo data will tell you any terminal type
These strange things, but you may find that all of these strange combinations is a difficult
jobs. (Translator Note: On Linux system, Termcap data is located in / etc / termcap, and Terminfo data is located
/ usr / share / terminfo Press different files stored in different terminal types, the number of terminal types has been over.
Two thousand)
However, you must have to study your own hand, then getting your hand, then study it.
The set of 'Termcap', especially 'TPUTS ()', 'tparm ()' and 'tgoto ()' functions.
What is PTTYS?
==================
Pseudo-Teletypes (PTTYS, PTYS, or other abbreviations) is a pseudo device with two parts:
One side is "master", you can think that it is a 'user', and the other is "servant",
It works like a standard TTY device.
They exist is to provide a method of simulating serial behavior under program control. ratio
For example, 'telnet' uses a pseudo terminal at the remote system; the remote login shell program of the server is just from "servant
"People" TTY equipment look forward to getting operational behavior, and the fake end end on the "master" by a guard
Preface control, and the daemon forwards all data through the network. PTTYS is also used by other programs, such as
'Xim', 'Expect', 'script', 'screen', 'Emacs' and many other programs.
3.6 How to control a serial port and modem?
=====================================
The control of the serial port under the UNIX system is largely affected by the use of the serial terminal. in the past,
The combination of different IOCTLS function calls and other hackers can control the correct operation of a serial device.
Fun, fortunately, POSIX has made some efforts in this respect.
If you use the system does not support 'header file,' tcsetttr () 'and other related functions,
Then you can only find some information (or upgrade your old antique system).
However, different systems still have a significant difference, mainly in the operation of device name, hardware flow control, and
Modem signals of the modem. (As long as it may, try to make the device driver to make a handshake (Handshaking)
Work without trying to manipulate the handshake signal directly. )
The basic steps for opening and initial Chinese serial devices are:
* Call the 'open ()' function to open the device; and may need to use a specific flag as a parameter:
`O_nonblock '
Open a Dial-in or modem control unless you use this flag.
Spare will cause 'open ()' calls to block until the line turn-on (Carrier is present). A non-
Blocking Operations For you to control the failure of time-saving modems. (See
Clocal)
`O_nOctTy 'This logo on the system evolved from 4.4BSD is redundant, but it controls serial in other systems.
Whether the device becomes a session control terminal. In most cases, you may not want to get a control.
Terminal, so set this flag, but there are exceptions.
* Call the 'tcgetattr () function Get the current device mode. Although some people will often cancel (ignore)
Most or all of the initial settings, but it still does not lose an initialization 'struct Termios' structure
Convenience method.
* Sets 'c_iflag', 'c_oflag', 'c_flag', 'c_lfag' and 'c_cc' in the Termios structure
For the right value. (See section below.)
* Calls' cfsetispeed () and 'cfsetospeed ()' setting the baud rate envisaged. Few systems allow you
Set different input and output speed, so the normal law is the value you need to set up.
* Call 'TCSetattr ()' Setting the device mode.
* If you are open with the 'o_nonblock' flag, you may want to call 'FCNTL ()'
The function is reset to close the 'o_nonblock' flag. Because different systems seem to be pairs
The non-blocking open port has different processing on the future 'read ()' call;
It is best to explicitly set up.
Once you open and set the port, you can call the 'read ()' function and the 'write ()' function normally.
Note that the behavior of the 'read ()' function will be set by the flag parameter when you call 'tcsetttr ()' functions
control.
'Tcflush ()', 'tcdrain ()', 'tcsendbreak ()' and 'tcflow ()' is some other you should
Note the function.
When you use the end to want to close, pay attention to some hazardous little dangers on the system; if there is any
Output data is waiting to be written to the device (such as the output stream is suspended by hardware or software, your process will
Suspended for the 'close ()' function call until the output data is emptied, and the process is * not
Kill * (unkillably). So call the 'TCFLUSH ()' function to discard the data to be issued may be a wise move.
(In my experience, the output data that is blocked on the TTY device is the most common cause of the non-killing process.)
3.6.1 Serial Equipment and Types
--------------------
Different systems are used in the serial port equipment. The following is some examples of different systems:
* '/ Dev / tty [0-9] [a-z]' As a direct access device,
'/ Dev / tty [0-9] [a-z]' As the modem control device (such as SCO UNIX)
* '/ Dev / cua [0-9] p [0-9]' As direct access equipment, '/ dev / cul [0-9] p [0-9]' as a dial
Prepare, and '/ dev / ttyd [0-9] p [0-9]' As the dialing device (such as HP-UX)
* '/ Dev / cua [a-z] [0-9]' As the dial-out device, '/ dev / tty [a-z] [0-9]' acts as a dial-in device (such as
FreeBSD)
Whether it is correctly interacting with the device name used, and the corresponding effect is generated on any hardware handshake signal line.
It is related to the system, configuration, and hardware, but almost always follow these rules (assuming hardware is
RS-232 DTE):
- DTR and RTS should be set for a successful opening operation of any device
- A block opening operation of the device controlled or dial-in from the modem will wait for DCD (and
Maybe DSR and / or CTS are also required to be set, usually after setting DTR / RTS.
- If an open operation for dialing devices is happily caught up with an open operation for the corresponding dial-in device
Because the wait line is turned on, then the open-out operation * maybe * causing the open dial-in operation.
Cheng, but * may not be caused. Some systems implements a simple sharing scheme for the dial-in and dial-out ports. When the dial-out port is used, the dial-in port is effectively set to sleep state ("PUT to Sleep");
It is not doing so, in this system, to avoid competition (content), need external assistance
Enable the dial-in and allocate shared ports (such as UUCP locking files).
3.6.2 Set the logo of Termios
-------------------------
Here is some tips for setting the Termios flag when using the serial devices you open by yourself (ie,
Use the existing control TTY)
3.6.2.1 c_iflag
...............
You may want to set * all * 'c_iflag''s logo is 0 unless you want to use software flow control (ICK),
In this case you set 'ixon' and 'ixoff'. (Translator Note: There are three logo control flow control:
IXON, IXOFF, and IXANY, if IXON is set, then the TTY input queue software flow control
be set to. When the program can't keep up with the speed of the queue, TTY transmits a STOP character, and when entering the team
Send a START character when the column is almost empty. If IXON is set, the software flow control of TTY output queue
System is set. When the device connected to the TTY does not keep the output speed, TTY will block the program to write the TTY. in case
IXANY is set, then once TTY receives any character from the device, the tentative output will continue - translated from the SCO
Unix online documentation
http://uw7doc.sco.com/sdk_sysprog/c...ntl.html, "tty flow
"chapter, fifth, sixth paragraph)
3.6.2.2 c_oflag
...............
Most 'c_oflag''s logo is in order to make the output of slow terminals can work properly.
Or such hackers, thereby, some newer systems think that almost all of these signs have passed
This is thus abandoned (especially all blood "output alignment alignment options).
Like 'c_iflag', set it all 0 to most applications is reasonable.
3.6.2.3 c_cflag
...............
When setting the size of characters, remember to first use 'csize' mask, such as setting 8-bit characters, need:
Attr.c_cflag & = ~ csize;
Attr.c_cflag | = CS8;
Others in 'c_cflag' you may need to be set to * true *, including 'cread' and
'Hupcl'.
If you need an even check, set the 'PARODD' and clear 'parodd'; if you
You need to produce aqi check, then set 'Parenb' and 'Parodd'. If you don't
Want to set the verification, then confirm the 'Parenb'.
Clear 'Cstopb' unless you really need to generate two stop bits.
Setting the hardware stream control can also be found in 'c_cflag', but they are not standardized (
This is a regret)
3.6.2.4 c_lflag
...............
Most applications may need to close 'Icanon' (standard status, although row, and lose
In-process), 'echo' and 'isig'.
'Iexten' is a more complex problem. If you don't turn it off, the specific implementation allows you to do something
Standards (such as defining adding control characters in 'c_cc') can result in unpredictable connections
Fruit, but on some systems, you may need to keep the 'Iexten' flag is true to get some useful features, such as hardware flow control.
3.6.2.5 c_cc
..........
This is an array including a special meaning character in the input. These characters are named 'VINTR'.
'VStop', etc .; these names are indexed by this array.
(The two in these characters are actually not characters, but when 'icanon' is turned off
'Read ()' Control of function behavior; they are 'Vmin' and 'VTIME'. )
The way these index names are often mentioned will make people think they are real variables, such as "settings.
Vmin is 1 "actually means" setting C_CC [vmin] is 1 ". This shorthand is useful and is only
Occasion occasionally misunderstand.
Many variables of 'c_cc' are only used when other flags are set.
Only 'icanon' is set, only the following variables are used:
'Veof', 'veol', 'Verase', 'vkill' (if defined
'Ipten' is set, then 'veol2', 'vStatus' and 'Vwerase'
Also used)
Only 'isig' is set, only the following variables are used:
'Vintr', 'vquit', 'vsusp' (if defined, 'ipten' is set,
So 'vdsusp' is also used)
Only 'ixon' or 'ixoff' is set, only the following variables are used:
'VStop', 'vstart'
Only 'icanon' is canceled, only the following variables are used:
'Vmin', 'vTIME'
Different system implementations will define an increased 'c_cc' variable. Cautious practice is to set the value you want to use.
Previously, use '_posix_vdisable' to initialize these variables (constant 'nccs')
Group size)
'Vmin' and 'vtime' (according to different implementation methods, they may be and 'veof' and 'veol'
Sharing the same two variables) has the following meaning. The value of 'vTIME' (if not 0) is always interpreted as ten
The timer unit of one second) (the translator Note: VTIME variable is a byte length, so 1 represents 0.1 seconds,
The maximum is 255, indicating 25.5 seconds)
`c_cc [vmin]> 0, c_cc [vTIME]> 0 '
As long as you have a VMIN byte length, or if you have at least one character, you read the last word.
The VTIME has expired before, or is interrupted, 'read ()' will return.
`c_cc [vmin]> 0, c_cc [vTIME] == 0 '
As long as the input already has a VMIN byte length, or by the signal interrupt, 'read ()' will return. Otherwise,
Unlimited waiting.
`c_cc [vmin] == 0, c_cc [vTIME]> 0 '
As long as you have input 'read ()', return; if VTIME expires, there is no data, it will return not read
To the character. (This is a little conflict with the file end sign when the modem is hung; use 1 as a VMIN,
Call the 'alarm ()' or 'select () function and give the timeout parameter to avoid this problem. )
`c_cc [vmin] == 0, c_cc [vTIME] == 0 '
'Read ()' always returns immediately; if there is no data, it returns a character. (With the above problem
the same)
UNIX programming FAQ Solution "4 System Information"
4. System information
***********
4.1 How do I know how much memory capacity in my system?
====================================== This is another 'often an unassigned problem'. In most cases, you should not try to find the answer.
If you must get an answer, the answer to the question is usually, but it is very dependent on different operating systems.
For example, in Solaris, you can use `sysconf (_sc_pages_pages) 'and` sysconf (_sc_pagesize)';
In FreeBSD, you can use `sysctl () '; you can get and process` / proc / meminfo' in Linux.
(Be careful when using this file, it should accept any different legitimate formats in history). Other operations
The system has its own way, I have not realized more portable methods.
In HP-UX (9 Edition and 10 Edition), you can use the following code:
Struct PST_STATIC PST;
IF (Pstat_getStatic (& PST, SizeOf (PST), (SIZE_T) 1, 0)! = -1)
{
Printf ("Page Size:% lu / n", pst.page_size;
Printf ("Phys Pages:% lu / n", pst.physical_memory;
}
4.2 How do I check a password of a user?
==============================
4.2.1 How do I get a password of a user?
-------------------------------
In most UNIX systems, the user password is usually stored in the `/ etc / passwd 'file. This file is generally
This format:
Username: Password: User number: User Group Number: Note: User Directory: Login Shell
(Username
Assword: Uid: GID: Gecos Field: Home Directory: Login Shell)
But these standards continue to change over time, and now user information may be stored on other machines, or
It is not stored in the `/ etc / passwd 'file. Today's system implementation also uses `shadow 'file save
Household order and some sensitive information. This file only allows users with specific permissions to read.
For safety reasons, the user password is typically encrypted, not the expressive manner.
POSIX defines a set of functions to access user information. The fastest way to get a user information is to use` getPwnam () '
And `getPwuid () 'functions. Both functions returns a structural pointer, which contains all the users' letters.
Interest. `GetPwnam () 'Receive username strings,` getpwuid ()' Receive user number (UID),
(`uid_t 'type is defined in POSIX). Returns NULL if the call is fails.
However, as mentioned earlier, today's operating system has a shadow file to store sensitive information, ie user passwords.
Some systems returns a user password when the caller user number is a super user, and other users require you to use other ways to access
Shadow file. You can use `getspnam () 'to get a structure about user information by entering the username. Moreover, in order to be able to complete this, you need to have some permissions. (In some systems, such as HP-UX and
SCO, you can replace it with `getprpwnam () '. )
4.2.2 How do I get the password in the shadow password file via the user number?
-----------------------------------------------
My system uses a set of getsp * functions to get important user information. However, I don't have `getspuid () ',
Only `getspnam () '. How can I get user information through the user number?
Workaround is relatively easy. The following functions can be placed directly into your personal application library:
#include
#include
#include
#include
Struct SPWD * GetSpuid (UID_T PW_UID)
{
Struct SPWD * shadow;
Struct Passwd * ppasswd;
IF ((pPasswd = getpwuid (pw_uid)) == NULL)
|| ((shadow = getspnam (ppasswd-> pw_name)) == null))
Return NULL;
Return shadow;
}
The problem now is that some systems do not save the user number (UID) and other information in the shadow file.
4.2.3 How do I check a user's password?
-------------------------------
A basic problem is that there are a variety of certification systems, so passwords are not always like they look.
Like that. In traditional systems, using UNIX style encryption algorithms, encryption algorithms are different, some systems make
With DES (Translator Note: des: Data Encryption Standard, for NIST [National Institute of
Standard and technology] The standard encryption algorithm is confirmed, the latest news shows that NIST will use a new
Encryption Standard Rijndael Gradually Replace DES) Algorithm, other systems, such as FreeBSD International Using MD5 (Translator
Note: MD5 is the most widely used single hash algorithm, invented by Ron Rudest, see RFC 1321
Http://www.faqs.org/rfcs/rfc1321.html) Algorithm.
The most common method is to use a single encryption algorithm (translator's note: ie a single hash [Hash] algorithm). Input
The express text port is encrypted, then the encryption password stored in the file is then compared. How to encrypt more details
View `Crypt () 'man page, here there is a usual version:
/ * Enter a clear text password and encryption password, check if it matches,
* Successfully returns 1, and other situations return 0.
* /
INT CHECK_PASS (Const Char * Plainpw, Const Char * Cryptpw)
{
Return StrCMP (Crypt (PlainPW, CryptPW), CRYPTPW) == 0;
}
The reason why this function can work is because the added (SALT) string used by the encryption function is stored on the front of the encrypted port string.
* WARNING: * In some systems, password encryption is a variant of 'CRYPT ()', the 'Bigcrypt ()' function.
UNIX programming FAQ "5 Programming Acrobatics"
5. Programming acrobatics
***********
5.1 How do I use a wild character comparison string?
================================== For its answer, depending on your so-called 'wild characters' The exact meaning.
There are two very different concepts that are identified as 'wild characters'. They are:
* File name wild mode * (filename patterns)
This is a (Expansion) (or 'globbing') for the shell to match the file name.
* Regular expression *
This is for editors, such as 'grep', and so on. It is used to match the text, and they are normal
Not applied to the file name in the case.
5.1.1 How do I use the file name wild mode comparison string?
------------------------------------------
Unless you are not lucky, your system should have a function 'fnmatch ()' to match the file name. One
Only the Bourne Shell style is allowed. It recognizes '*', '[...] and'? ', But may not
A more mysterious (Arcane) model is supported under the Korn and Bourne-Again shell programs.
If you don't have this function, better way to make a car than the door is you can start from the BSD or GNU
There is a copy (snarfing) one.
Moreover, in the case of ordinary matching actual file name, consult the 'Glob ()' function, it will search match
All existence files for a given mode.
5.1.2 How do I use a regular expression to compare the string?
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
There are a lot of regular expressions that have different syntax; most systems are at least two: one is 'ed'
The program can be identified, sometimes it is recorded as 'basic regular expression', the other is 'EGREP' program
It can be identified, which is recorded to 'expand regular expression'. Perl (Translator Note: Perl: Practical Extract and
Report Language, practical analysis and report language) Language has its own slightly different styles, Emacs
Also.
In order to support so many formats, there is a lot of implementations. System generally has a regular expression match function (pass
Often available for 'regcomp ()' functions and 'regexec ()' functions, but should be used well; some systems
There are more than one implementation available, with a different interface. In addition, there are many available software libraries.
Now (by way, it is generally compiled a regular expression into internal form and then use because
Always assume that you have a lot of strings to be more symptoms. )
A available software library is the 'RX' software library, which can be obtained from the mirrored site of the GNU. It seems to be
In the development, this is a good and bad thing based on your different views :-)
5.2 What is the best way to send an email in the program?
=================================================================================================================================================================================
There are several ways to send emails from UNIX programs. The best choice according to different situations is different.
So I will provide two ways. There is also the third method, here didn't say, is the SMTP connected to the local host.
(Translator Note: SMTP: SIMPLE MAIL Transfer Protocol Simple Mail Transfer Protocol) Port and use the SMTP protocol directly, see RFC 821 (Translator Note: RFC: Request for Comments).
5.2.1 Simple Method: / BIN / MAIL
-------------------------
For simple applications, executing 'mail' programs is enough (usually '/ bin / mail', but some systems
It is possible to be '/ usr / bin / mail').
* WARNING: * Some versions of the UCB Mail program are also executed in the message in non-interactive mode (Message
Body) '~! 'Or' ~ | 'The command expressed by the line of the head. This may have a safe risk.
Imagine this: 'mail -s' Title 'Recipient Address ...', the program will input the standard input as a message body.
And provide the default message header (which includes set titles), then pass the entire message to 'sendmail'
During delivery.
This sample program sends a test message on the local host to 'root':
#include
#define mailprog "/ bin / mail"
int main ()
{
File * mail = pop (mailprog "-s 'test message' root", "w");
IF (! mail)
{
PERROR ("Popen");
Exit (1);
}
FPRINTF (Mail, "This Is A Test./N");
IF (PClose (Mail))
{
FPRINTF (stderr, "mail failed! / n");
Exit (1);
}
}
If the body to send is saved in a file, then you can do this:
System (Mailprog "-s 'file contents' root tmp / filename);
This method can be extended to more complex situations, but we have a lot of potential hazards (Pitfalls):
* If you use system () or popen (), you must be very serious to enclose parameters to protect them
The wrong file name matches replacement or word segmentation.
* Based on user setting data to construct command lines is the universal original for buffer crosses errors and other security vulnerabilities
because.
* This method is not allowed to set CC: (Translator Note: CC: Carbon Copy) or BCC: (Translator Note:
BCC: Blind Carbon Copy: Blind delivery, refers to the recipient of the delivery address is not in the message.
(Some version of the / bin / mail program allows, others are not allowed)
5.2.2 Launching Mail Transfer Agents Direct (Translator Note: MTA: Mail Transfer Agent): / usr / bin / sendmail
-------------------------------------------------- -----------------------------
The 'Mail' program is an example of "Mail User Agent", which is designed for users
Execute to send and receive emails, but it is not responsible for actual transmission. A program for transmitting mail
"Mail Transport Agent" (MTA), and the mail transfer agent that is universally found in the UNIX system is called
'Sendmail'. There are other mail transport agents, such as 'mmdf', but these programs
It usually includes a program to simulate a common practice of 'Sendmail'. History, 'sendmail' is usually found in '/ usr / lib', but now the trend is to use the application library program from
'/ Usr / lib' is moved out and torched such as '/ usr / sbin' or '/ usr / libexec' and other directories. As a result, general
Always start the 'Sendmail' program in absolute path, and the path is determined by the system.
In order to understand how the 'Sendmail' program work, you usually need to understand the overview of "Envelope"
Read. This is very similar to written letters; defining this news on the envelope, and indicate who is issued (
In order to report the purpose of the error). In the envelope is "message head" and "message body", between one
Dark line spaced apart. The format of the message head is mainly provided in RFC 822; and see the RFC documentation of MIME. (
Translator Note: MIME's documentation includes: RFC1521, RFC1652)
There are two main methods to use the 'Sendmail' program to generate a message: Either envelope recipient
Explicitly provided, or 'sendmail' programs can be indicated in the message header in the header. Two types
The law has advantages and disadvantages.
5.2.2.1 Explicitly provide envelope content
.......................
The envelope content of the message can be set simple in the command line. Its disadvantage is that the email address may contain
Characters will cause 'system ()' and 'popen () program to be outlined, such as single quotes,
The enclosed string, etc. Pass these instructions to the shell program and successfully explain the potential dangers
risk. (You can replace any single quotes in the command into single quotes, backslash, single quadrature, single quotes
Sequential combination, then enclose the entire address. Terrible, oh? )
Some unpleasant can be used to avoid using 'system ()' or 'popen () function and help
Fork () 'and' exec () 'functions are avoided. This sometimes needs it; for example, users
Customized to the SIGCHLD signal typically interrupt the 'pClose ()' function to affect
Or large or small range.
Here is an example program:
#include
#include
#include
#include
#include
#include
/ * #include
#ifndef _path_sendmail
#define _path_sendmail "/ usr / lib / sendmail"
#ENDIF
/ * -oi means "do not look at '.' Termination of the message"
* Delete this option, "-" If you use the Sendmail version Previous version (and hope no one)
* Has used a recipient address that started with minus
* You may want to add -Oem (Report Errors by Mail, report errors in email)
* /
#define sendmail_opts "-oi", "-"
/ * The following is a macro of the number of members returned in an array * /
#define countof (a) ((SizeOf (a)) / sizeof ((a) [0]))
/ * Send the contents of the files that are opened by the FD to the settings to the set recipient;
* File contains the message header and message body defined by RFC822, and the recipient list is made of NULL pointer.
* The flag ends; if the error is found, -1, otherwise returns the return value of Sendmail (it uses
*
Int Send_message (int FD, Const Char ** Recipient)
{
Static const char * argv_init [] = {_path_sendmail, sendmail_opts};
Const char ** argvec = null;
INT NUM_RECIP = 0;
PID_T PID;
Int rc;
Int status;
/ * Calculate the number of recipients * /
While (Recipients [Num_recip])
Num_Recip;
IF (! Num_recip)
Return 0; / * Regardless no recipient success * /
/ * Assign space to the parameter vector * /
Argvec = malloc ((SizeOf char *) * (Num_recip Countof (argv_init) 1);
IF (! argvec)
Return -1;
/ * Initialization parameter vector * /
Memcpy (argvec, argv_init, sizeof (argv_init);
Memcpy (argvec countof (argv_init),
Recipients, Num_Recip * Sizeof (char *));
Argvec [NUM_RECIP Countof (argv_init)] = NULL;
/ * You need to add some signal blocking * /
/ * Generation sub-process * /
Switch (PID = for ())
{
Case 0: / * child process * /
/ * Establish a pipeline * /
IF (fd! = stdin_fileno)
DUP2 (fd, stdin_fileno);
/ * Other places have been defined - close all parameters of file descriptors for all> = parameters * /
Closeall (3);
/ * Send: * /
Execv (_path_sendmail, argvec);
_exit (ex_osfile);
Default: / * Parent Process * /
Free (argvec);
RC = WaitPid (PID, & status, 0);
IF (RC <0)
Return -1;
IF (Wifexited (status))
Return WexitStatus (Status);
Return -1;
Case -1: / * error * /
Free (argvec);
Return -1;
}
}
5.2.2.2 Allowing the sendmail program to reasonabate the recipient
.....................................
'Sendmail' '' 'option instruction' sendmail 'program handles the header information of the message, and uses all
The recipient is included in the header information of the recipient (ie: 'to:', 'cc:' and 'bcc:'). Its excellent
Point is to simplify the command line of 'sendmail', but also make it set outside the message header information.
Recipients become impossible. (This is usually not a problem)
As an example, the following program sends standard input as a file to the MIME attachment.
Set the recipient. I went to some error checks for the simplicity. This program needs to call 'metamail'
The 'mimecode' program of the distribution package.
#include
#include
#include
/ * #include
#ifndef _path_sendmail
#define _path_sendmail "/ usr / lib / sendmail"
#ENDIF
#define sendmail_opts "-oi" #define country countof (a) (SizeOf (a)) / sizeof ((a) [0]))
Char tfilename [l_tmpnam];
Char Command [128 L_TMPNAM];
Void Cleanup (Void)
{
UNLINK (TFileName);
}
INT main (int Argc, char ** argv)
{
File * msg;
INT I;
IF (Argc <2)
{
FPRINTF (stderr, "usage:% s recipients ... / n", argv [0]);
EXIT (2);
}
IF (TMPNAM (TFileName) == NULL
|| (MSG = FOPEN (TFileName, "W")) == NULL)
EXIT (2);
Atexit (Cleanup);
Fclose (MSG);
Msg = fopen (tfilename, "a");
IF (! msg)
EXIT (2);
/ * Create a list of recipients * /
FPRINTF (MSG, "To:% S", Argv [1]);
For (i = 2; i FPRINTF (MSG, ", / N / T% S", Argv [I]); FPUTC ('/ n', msg); / * Title * / FPRINTF (MSG, "Subject: File Sent By Mail / N"); / * Sendmail program automatically adds from :, date :, message-id: etc * / / * MIME processing * / FPRINTF (MSG, "MIME-VERSION: 1.0 / N"); FPRINTF (MSG, "Content-Type: Application / OcTet-Stream / N"); FPRINTF (MSG, "Content-Transfer-Encoding: Base64 / N"); / * End of the message head, plus an empty line * / FPUTC ('/ n', msg); Fclose (MSG); / * Perform an encoding program * / Sprintf (Command, "MIMENCODE -B >>% S", TFileName); IF (System (Command)) Exit (1); / * Execute a messenger program * / Sprintf (Command, "% s% s -t <% s", _Path_sendmail, sendmail_opts, tfilename; IF (System (Command)) Exit (1); Return 0; } UNIX programming FAQ Solution "6 Tools" 6. Use of tools ************ 6.1 How do I debug a child process generated by the FORK function? ===================================== There are two different methods based on available tools: Your debugger may have allow you to choose whether to track the parent or sub-process after the call () ' The option is enough for some purposes. The replacement method is that your debugger may have an option to allow you to attach it (Attach) to a program being executed. This allows you to rely on a child process that has already begun. If you don't need it from The child process starts testing at the beginning, which is usually enough. Otherwise, you will want 'fork ()' in the child process. Insert a 'Sleep ()' call, or insert the following loop: { Volatile INT f = 1; While (f); } Such a process will continue to perform down this loop until you set 'f' to 0 with a debugger. And remember that using the debugger is not the only way to find errors in your program; in many UNIX systems Some tool programs can be used to track system calls and signals, and rich logs often have use. 6.2 How to establish a new library file through other library files? ======================================= The premise is what we are talking is archive (static) library, the easiest way is to put all the libraries File Use the 'AR X' command to split into their original single target file (Object) in an empty directory. Then merge into one. Of course, the file name may be repeated, but if this library file is large, You may not want to merge them together at the beginning. 6.3 How to create a dynamic connection library (Shared library) / dlls? ================================================================================= The method of creating a dynamic link libraries is different depending on the system. This process is mainly Two steps; the first step requires that the goal in the dynamic connection library must first be compiled, usually To have a compilation option indicating that the string code is position-independene; the second step is These goals are connected together to form a library file. Here is a demonstration of demonstrations: / * Shrobj.c file * / Const char * myfunc () { Return "Hello World"; } / * Shrobj.c end * / / * Hello.c file * / #include EXTERN Const char * myfunc (); Main () { Printf ("% s / n", myfunc ()); Return 0; } / * Hello.c end * / $ gcc-fpic -c shrobj.c $ gcc -shared -o lobshared.so shrobj.o $ GCC Hello.c Libshared.so $ ./a.out Hello World So far, if you want the library file and its creation process, it is best to transplant. The way is to use the GNU Libtool package. It is a small toolbox, these tools Know the platform of establishing a dynamic connection library unrelated; you can only post your program necessary part, thus When an installer configures your package, he can decide what library is generated. LibTool package is not supported Dynamically connect the library's system well. It also knows with GNU AutoConf program and GNU Automake program hook (if you use these tools to manage the compilation process of your program). If you don't want to use the libtool package, then the compiler other than GCC, you need to follow Column Modify Compiler Parameters: AIX 3.2 uses XLC (not confirmed) Cancel the '-fpic' option, with '-Bm: Sre -be: libshared.exp' replaces '-shared'. You and Need to create a file called 'libshared.exp' Save all output symbols (Symbols to export) Lists, such as the above sample programs, you need to output the 'MyFunc' symbol. In addition, in the connection library Use the '-e _nostart' parameter (on the newer AIX version, I believe it should turn it into '-BnoEntry'). SCO OpenServer 5 uses the SCO development system (unconfirmed) If you use ELF (Translator Note: ELF: Performing with Connection Format Executable and Linking Forrmat, A UNIX executable target file format) format, then shared libraries can only be available on OS5, and it Need '-belf' options. And replaced '-fpic' in '-kpic', using 'cc -belf -g' when connecting. Solaris uses SparcWorks compiler Replacing '-fpic' with '-PIC', and replaces 'gcc -shared' with 'ld -g'. (Encourage you to provide more materials to enrich the above list) Others should be careful: * AIX and (I believe) Digital Unix does not need the -FPIC option, because all code is unrelated. * AIX usually requires you to create a 'output file', that is, a save all dynamic connection libraries List of symbols. Some connector (Linker) (may have a SLHS connector, is SVLD?) Has one Options can output all symbols. * If you want to use a popular '-l' parameter for the connector to reference your dynamic connection library, you must Understand how your system is actually running to find a dynamic connection library. The most common method is to use 'Ld_library_path' environment variables, but there is usually one other option when connecting set up. * Most implementation methods are where the desired dynamic connection library in the program is recorded in the program. This Make a dynamic connection library from a directory to another directory will cause the program to work. a lot of The system has an option for the connector to set the location where you want to run the dynamic connection library (for example The Solaris system is the '-r' connector option, or the 'ld_run_path' environment variable). * ELF and A. Excovement method may have a connector option '-Bsymbolic', which leads to the library The reference to the inside is explained. Otherwise, in these systems, all symbols explains will be postponed to the most After the connection is connected, then the single function in the main program will be the corresponding function in the heavy load library. 6.4 Can I change the goal in a dynamic connection library? ======================================= Generally can't. On most systems (except AIX), when you connect the target and form a dynamic connection library, it is like An executable program; the goal does not retain their single feature. As a result, a single goal is generally not removed from a dynamic connection library. 6.5 Can I generate a stack image in a running program? ============================================== Some system provides library functions to extract (unwinding), so that you can be in an error. A stack image is generated in the handler, but only a small part of the system has these functions. One possible workaround is a debugger debugging for your program * it * - The detailed approach is still slightly different according to different systems, but the general concept is this: Void dump_stack (void) { CHAR S [160]; Sprintf (s, "/ bin / echo 'where / ndetach' | dbx -a% d", getpid ()); SYSTEM (S); Return; } You need to process DBX parameters and commands based on your different systems, or even change another debugging For example, 'gdb', but this is still my most common solution for this problem. to this end, Honor grant Ralph Corderoy. The following list contains the command lines that need to be used on some systems: Most of the systems using DBX `" / bin / echo 'where / ndetach' | dbx / path / to / program% d "" AIX `" / bin / echo 'where / ndetach' | dbx -a% d " Irix `" / bin / echo 'where / ndetach' | dbx -p% d "" ? Sample program ********* Capture SigchLD signal ================== #include #include #include #include #include Void Sig_chld (int); / * Prototype * / int main () { Struct SigAction Act; PID_T PID; / * Set the SIG_CHLD function as the processing function of our SIGCHLD signal * / Act.sa_handler = SIG_CHLD; / * In this example program, we don't want to block other signals * / SiGemptySet (& Act.sa_mask); / * * We are only trying to resize the troops to the child's sub-process (such as the user presses Control-Z on the terminal) * / Act.sa_flags = sa_nocldStop; / * * Make these set values take effect. If we are writing a real app, * We may keep these original values, not to pass a NULL. * / IF (SigAction (Sigchld, & Act, NULL) <0) { FPRINTF (stderr, "sigaction failed / n"); Return 1; } / * fork * / Switch (PID = for ()) { Case -1: FPRINTF (stderr, "fork failed / n"); Return 1; Case 0: / * is a child process, directly end * / _exit (7); / * Exit status = 7 * / Default: / * Parent Process * / Sleep (10); / * Time to complete the child process * / } Return 0; } / * * Signal Processing Function - Only when a SIGCHLD signal is received, * There is a child process to terminate * / Void Sig_Chld (int Signo) { Int status, child_val; / * Non-blockedly waiting for any child process to end * / IF (Waitpid (-1, & status, wnohang <0) { / * * Not recommended to call standard input / output functions in the signal processing function, * But there is no problem in a toy program like this. * / FPRINTF (stderr, "waitpid failed / n"); Return; } / * * We now have a child process to exit information in the 'status' variable and you can use * Macro defined in Wait.h to operate * / IF (Wifexited) / * child process is normal to exit? * / { Child_val = WexitStatus (status); / * Get an exit status of sub-process * / Printf ("Child's EXITED NORMALLY with Status% D / N", Child_VAL); } } Read Process Table - SunOS 4 Edition ===================================================================================================================================================== #define _kmemuser #include #include #include Char regexpstr [256]; #define init register char * sp = regexpstr; #define getc () (* sp ) #define peekc () (* sp) #define ungetc (c) (--SP) #define return (Pointer) Return (Pointer); #define error (VAL) #include PID_T getPidByname (Char * Name, PID_T SKIPIT) { KVM_T * kd; Char ** arg; Int error; Char * p_name = NULL; Char ExpBuf [256]; Char ** freeme; Int curpid; Struct User * Cur_user; Struct User myuse; Struct Proc * Cur_Proc; IF ((kd = kVm_open (null, null, null, o_rdonly, null) == null) {RETURN (-1); } Sprintf (Regexpstr, "^. * /% s $", name); Compile (NULL, EXPBUF, ExpBuf 256, '/ 0'); While (cur_proc = kVm_nextproc (kd)) { Curpid = CUR_PROC-> P_PID; IF ((cur_user = kVm_getu (kd, cur_proc))! = null) { Error = KVM_GETCMD (KD, Cur_Proc, Cur_user, & arg, null); IF (Error == - 1) { IF (Cur_User-> U_COMM [0]! = '/ 0') { p_name = cur_user-> u_comm; } } Else { p_name = arg [0]; } } IF (p_name) { IF (! strcmp (p_name, name) { IF (Error! = - 1) { Free (arg); } IF (Skipit! = - 1 && totetval == Skipit) { Ourretval = -1; } Else { Close (FD); Break; } Break; } Else { IF (Step (p_name, expBuf) { IF (Error! = - 1) { Free (arg); } Break; } } } IF (Error! = - 1) { Free (arg); } p_name = NULL; } KVM_Close (kd); IF (p_name! = null) { Return (Curpid); } Return (-1); } Read process table - SYSV version ==================== PID_T getPidByname (Char * Name, PID_T SKIPIT) { DIR * DP; Struct Dirent * DIRP; PRPSINFO_T RETVAL; Int fd; PID_T urretval = -1; IF ((DP = OPENDIR ("/ proc")) == null) { Return -1; } ChDIR ("/ proc"); While ((DIRP = ReadDir (DP))! = null) { IF (DIRP-> D_NAME [0]! = '.') { IF ((fd = open (DIRP-> D_NAME, O_RDONLY))! = - 1) { IF (IOCTL (FD, Piocpsinfo, & RetVal)! = - 1) { IF (! strcmp (retval.pr_fname, name) { OURRETVAL = (PID_T) ATOI (DIRP-> D_NAME); IF (Skipit! = - 1 && totetval == Skipit) { Ourretval = -1; } Else { Close (FD); Break; } } } Close (FD); } } } CloseDir (DP); Return OurretVal; } Reading Process Table - AIX version 4.2 ======================== # include #include Int getProcs (struct procsinfo *, int, struct fdsinfo *, INT, PID_T *, INT); PID_T getPidByname (char * name, pid_t * nextpid) { Struct procsinfo pi; PID_T RETVAL = (PID_T) -1; PID_T PID; PID = * NextPID; While (1) { IF (GetProcs (& Pi, SizeOf Pi, 0, 0, & PID, 1)! = 1) Break; IF (! strcmp (name, pi.pi_comm)) { RetVal = pi.pi_pid; * nextPid = PID; Break; } } Return RetVal; } Int main (int Argc, char * argv []) { Int curag; PID_T PID; PID_T nextPID; IF (argc == 1) { Printf ("Syntax:% s Exit (1); } For (curarg = 1; curarg { Printf ("Process IDs for% S / N", Argv [Curarg]); For (NextPID = 0, PID = 0; PID! = -1;) IF ((pid = getpidbyname (argv [curarg], & nextpid)! = -1) Printf ("/ T% D / N", PID); } } Read the process table using the POPEN function and the PS command ================================ #include #include #include #include #include Char * procname (PID_T PID) { Static Char line [133], Command [80], * LineP, * token, * cmd; File * fp; Int status; IF (0 == PID) Return (char *) 0; Sprintf (Command, "PS -P% D 2> / dev / null", PID); FP = POPEN (Command, "R"); IF ((file *) 0 == fp) return (char *) 0; / * Read title line * / IF ((char *) 0 == FGETS (Line, SizeOf line, fp)) { PCLOSE (FP); Return (char *) 0; } / * Analyze the list of command names from the title bar. * (BSD style system will indicate the "command" string of commands in column 5, SYSV seems to * Indicates the "CMD" or "Command" string of the command in column 4) * / For (linep = line;; linep = (char *) 0) { IF ((char *) 0 == (Token = STRTOK (LineP, "/ T / N")))))) { PCLOSE (FP); Return (char *) 0; } IF (0 == Strcmp ("Command", token) || 0 == Strcmp ("cmd", token)) {/ * We found Command Listing * / cmd = token; Break; } } / * Read PS (1) Output Row * / IF ((char *) 0 == FGETS (Line, SizeOf line, fp)) { PCLOSE (FP); Return (char *) 0; } / * Catch the word below the Command title ... * / IF ((char *) 0 == (token = STRTOK (CMD, "/ T / N")))))))) { PCLOSE (FP); Return (char *) 0; } Status = PClose (FP); IF (! Wifexited (status) || 0! = WexitStatus (status)) Return (char *) 0; Return token; } Int main (int Argc, char * argv []) { PUTS (Procname))))) Exit (exit_success); } Daemon tool function ================ #include #include #include #include #include #include #include / * Closeall () - Close all> = given file descriptor * / Void Closeall (INT FD) { INT fdlimit = sysconf (_sc_open_max); While (fd Close (fd ); } / * daemon () - Detach the process from the user and disappears to enter the background, if the failure returns -1, * But in that case you can only exit because we may have generated a child process. * This is based on the BSD version, so the call party needs to be similar to other work similar to Umask. * / / * I believe you can work on all POSIX systems * / Int daemon (int Nochdir, int NoClose) { Switch (fork ()) { Case 0: Break; Case -1: return -1; Default: _exit (0); / * Original process exit * / } IF (setsid () <0) / * should not fail * / Return -1; / * If you want to get a control TTY in the future, exclude (DYKE) Switch statement * / / * - Normal situation is not recommended for daemon * / Switch (fork ()) { Case 0: Break; Case -1: return -1; DEFAULT: _EXIT (0); } IF (! nochdir) chdir ("/"); IF (! noclose) { Closeall (0); Open ("/ dev / null", o_rdwr); DUP (0); DUP (0); } Return 0; } / * fork2 () - Similar to the fork function, the child process immediately into a orphan process * (Do not produce a zombie process when it exits) * Returns 1 to the parent process, not any meaningful process number. * The parent process cannot use the WAIT function waiting for the sub-process (they are independent). * / / * This version assumes that you did not capture and ignore the sigchld signal. * / / * If you have a setting, no matter how you should use the Fork function * / Int fork2 () { PID_T PID; Int rc; Int status; IF (! (pid = for ()))))) { Switch (fork ()) { Case 0: Return 0; Case -1: _exit (errno); / * Assume that the error code is less than 256 * / DEFAULT: _EXIT (0); } } IF (PID <0 || Waitpid (PID, & Status, 0) <0) Return -1; IF (Wifexited (status)) IF (WexitStatus (status) == 0) Return 1; Else Errno = WexitStatus (status); Else Errno = Eintr; / * Hey, like this :-) * / Return -1; } A sample program for using the above functions: #include #include #include #include #include #include #include INT daemon (int, int); Int fork2 (void); Void Closeall (int); #define TCP_Port 8888 Void Errexit (Const Char * STR) { Syslog (log_info, "% s failed:% d (% m)", str, errno; Exit (1); } Void Errreport (Const Char * STR) { Syslog (log_info, "% s failed:% d (% m)", str, errno; } / * The actual child process here. * / Void Run_Child (int Sock) { FILE * IN = FDOPEN (SOCK, "R"); FILE * OUT = FDOPEN (SOCK, "W"); int CH; Setvbuf (in, null, _iofbf, 1024); Setvbuf (Out, NULL, _IOLBF, 1024); While ((ch = fgetc (in))! = EOF) FPUTC (TouPper (CH), OUT) Fclose (OUT); } / * This is the main job of the daemon - listening connection and generates sub-process * / void process () { Struct SockAddr_in Addr; Int Addrlen = SizeOf (AddR); INT SOCK = Socket (AF_INET, SOCK_STREAM, 0); INT flag = 1; Int rc = setsockopt (SOCK, SOL_SOCKET, SO_REUSEADDR, & flag, Sizeof (Flag)); IF (RC <0) Errexit ("setsockopt"); Addr.sin_Family = AF_INET; Addr.sin_Port = HTONS (TCP_PORT); Addr.sin_addr.s_addr = incdr_any; RC = Bind (Sock, (Struct SockAddr *) & addr, addrlen; IF (RC <0) Errexit ("bind"); RC = Listen (Sock, 5); IF (RC <0) Errexit ("Listen"); For (;;) { Rc = accept (Sock, (Struct SockAddr *) & addr, & address; IF (rc> = 0) Switch (fork2 ()) { Case 0: Close (SOCK); Run_Child (RC); _exit (0); Case -1: Errreport ("fork2"); Close (RC); Break; DEFAULT: Close (RC); } } } int main () { IF (daemon (0, 0) <0) { PERROR ("daemon"); EXIT (2); } OpenLog ("Test", Log_PID, LOG_DAEMON); PROCESS (); Return 0; } Modem Control Example Program ====================== / * Send some simple modem command * The device name of the serial device (preferably all dialing the device, * Or non-modem control device) as its unique parameters. * If you don't have a copier-out device that can be shared, then replace clocal with cflags_to_set. * / #include #include #include #include #include #include #include #include #include #include #include #define cflags_to_set (cread | hupcl) #define cflags_to_clear (CStopb | Parenb | Clocal) ENUM FLOWMODE {Noflow, Hardflow, SoftFlow}; / * Related to the system * / #define cflags_hardflow (crtscts) #define eXample_baud b19200 #define example_flow hardflow Static void Die (const char * msg) { FPRINTF (stderr, "% s / n", msg); Exit (1); } Static Int Close_and_complain (int FD, Const Char * MSG, INT ERR) { FPRINTF (stderr, "% s:% s / n", msg, strrror (err)); IF (fd> = 0) Close (FD); Errno = ERR; Return -1; } INT OPEN_PORT (const char * name, speted_t baud, enum flowmode flow) { Int flags; Struct Termios Attr; INT fd = open (name, o_rdwr | o_nonblock | o_nock); IF (fd <0) Return Close_and_complain (-1, "open", errno); / * Set some unclear if sensitive value * / IF (Tcgetattr (FD, & Attr) <0) Return Close_and_complain (fd, "tcgetattr", errno; / * No special input or output processing * / Attr.c_iflag = (FLOW == SoftFlow)? (ixon | ixoff): 0; Attr.c_oflag = 0; / * Setting 8 characters wide and some miscellaneous control mode * / Attr.c_cflag & = ~ (CSIZE | CFLAGS_TO_TO_CLAR | CFLAGS_HARDFLOW); Attr.c_cflag | = (CS8 | CFLAGS_TO_SET); IF (Flow == Hardflow) Attr.c_cflag | = cflags_hardflow; / * Normal mode * / Attr.c_lflag & = ~ (Icanon | Echo | Echoe | Echok | Isig); / * Special characters - Many have been canceled by previous settings * / { INT I; #ifdef _POSIX_VDISABLE Attr.c_cc [0] = _POSIX_VDISABLE; #ELSE Attr.c_cc [0] = fpathconf (fd, _pc_vdisable); #ENDIF For (i = 1; i Attr.c_cc [i] = attr.c_cc [0]; } Attr.c_cc [vStart] = 0x11; Attr.c_cc [vStop] = 0x13; / * Time control of the READ () function * / Attr.c_cc [vmin] = 1; Attr.c_cc [vTIME] = 0; / * Portrates * / Cfsetispeed (& Attr, Baud); Cfsetospeed (& Attr, Baud); / * Write settings * / IF (TcSetattr (FD, TCSANOW, & Attr) <0) RETURN CLOSE_AND_COMPLAIN (FD, "TcSetattr", Errno); / * If the system remembers the previous O_NONBLOCK setting, cancel it * / Flags = fcntl (fd, f_getfl, 0); IF (Flags <0) Return Close_and_Complain (FD, "FCNTL (Getfl)", Errno); IF (FCNTL (FD, F_SETFL, FLAGS & ~ O_NONBLOCK <0) Return Close_and_complain (fd, "FCNTL (SETFL)", Errno; Return fd; } / * Some simple timing tool functions * / / * 向 * TV plus second and useless * / Static Void Timeradd (Struct Timeval * TV, Long Secs, long useless { TV-> TV_SEC = SECS; IF ((TV-> TV_USEC = USECS)> = 1000000) { TV-> TV_sec = TV-> TV_USEC / 1000000; TV-> TV_USEC% = 1000000; } } / * Set * res = * a - * b, return result of symbol * / Static int Timersub (struct timeval * res, Const struct timeval * a, const struct timeval * b) { Long sec = a-> TV_sec - b-> tv_sec; Long usec = a-> TV_USEC - B-> TV_USEC; IF (USEC <0) Usec = 1000000, - SEC; RES-> TV_SEC = sec; RES-> TV_USEC = USEC; RETURN (SEC <0)? (-1): ((sec == 0 && usec == 0)? 0: 1); } / * This function does not attempt to handle abnormal strings (such as ABABC) * Time-time * A more common approach is to use the ALARM () function to process timeout. * This function is a simply starting without using signal processing and attempts to provide an alternative method. * / Int Expect (int FD, Const Char * STR, INT TIMEO) { Int matchlen = 0; INT LEN = Strlen (STR); Struct TimeVal now, end, left; FD_SET FDS; Char C; GetTimeOfDay (& End, NULL); TIMERADD (& End, Timeo / 1000, Timeo% 1000); While (Matchlen { GetTimeOfDay (& NOW, NULL); IF (Timersub (& LEFT, & End, & now) <= 0) Return -1; FD_ZERO (& FDS); FD_SET (FD, & FDS); IF (SELECT (FD 1, & fds, NULL, NULL, & LEFT) <= 0) Return -1; IF (READ, & C, 1)! = 1) Return -1; IF (isprint (unsigned char) || c == '/ n' || c == '/ r') PUTCHAR (C); Else Printf ("// x% 02x", c); IF (c == Str [matchlen]) Matchlen; Else Matchlen = 0; } Return 0; } INT main (int Argc, char ** argv) { Int fd; UNSIGNED CHAR C; IF (Argc <2) DIE ("NO Port Specified"); Setvbuf (stdout, null, _ionbf, 0); FD = Open_PORT (Argv [1], Example_baud, Example_Flow); IF (fd <0) Die ("Cannot Open Port"); Write (FD, "AT / R", 3); IF (Expect (FD, "OK", 5000) <0) { Write (FD, "AT / R", 3); IF (Expect (FD, "OK", 5000) <0) { Tcflush (FD, TCIOFLUSH); Close (FD); Die ("No Response to At"); } } Write (FD, "ATI4 / R", 5); EXPECT (FD, "OK", 10000); PUTCHAR ('/ n'); Tcflush (FD, TCIOFLUSH); Close (FD); Return 0; } Transaction control sample program ================ / * Generate a function of reception / background transaction * / #include #include #include #include #include #include #include #include / * Some of the following functions will fail because it cannot locate the TTY and the caller are not in the front desk. * When the first case, we assume that a front program will have a CTTTTTTY that is output for standard input, standard output, or standard error output. * And if it is not returned to enotty. * The second case, except for the special case of the Foreground_self () function, * If a non-front program intends to output something to the front desk, we return to Eperm. * (Maybe too much) * / / * A terminal (open a ctty) is scheduled for a given PGRP. * This tcsetpgrp () housing is only required for the place in POSIX (BOGUSITY); * Pass Sigttou when the standard system calls TCSetPGRP function in a non-front process * Signal (almost always like this). This is a victory of false consistency. * / INT Assign_Terminal (int Ctty, PID_T PGRP) { Sigset_t sigs; Sigset_t oldsigs; Int rc; SiGemptySet (& SIGS); SigaddSet (& SIGS, SIGTTOU); SigProcmask (SIG_BLOCK, & SIGS, & OLDSIGS); RC = TcSetPGRP (CTTY, PGRP); SigProcmask (SIG_SETMASK, & OLDSIGS, NULL); Return RC; } / * Similar to the Fork function, but do transaction control. If the newly established process is placed in the front desk, it will be true. * (This implicitly places the caller process to the background, so it is necessary to do this after this is the input / output of TTY) * Set PGRP to -1 to create a new transaction, and the process number returned in this case is the process group number of the new transaction. * Or set a transaction existing in the same session (generally only the second or second or second in the pipe operation * Process). * / PID_T SPAWN_JOB (int FG, PID_T PGRP) { INT CTTY = -1; PID_T PID; / * If you generate a * new * front desk transaction, at least the standard input, standard output or * One of the standard error output points to control TTY, and the current process is on the front desk. * Check the TTY in the control only when a new reception process is started in the transaction. * A session that does not control TTY can only have a background transaction. * / IF (fg) { PID_T CURPGRP; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; IF (PGRP <0 && curpgrp! = getpgrp ()) Return Errno = Eperm, (PID_T) -1; } Switch (PID = for ()) { Case -1: / * fork failed * / Return PID; Case 0: / * child process * / / * Establish a new process group, put us in the front desk if needed * I don't know what if the setpgid function call fails ("will not happen") * / IF (PGRP <0) PGRP = getPid (); IF (setpgid (0, pgrp) == 0 && fg) Assign_Terminal (CTTY, PGRP); Return 0; Default: / * Parent Process * / / * Here, you are also built. * / IF (PGRP <0) PGRP = PID; Setpgid (PID, PGRP); Return PID; } / * Will not be executed here * / } / * Kill the transaction representing PGRP with SIGNO * / INT KILL_JOB (PID_T PGRP, INT Signo) { Return Kill (-pgrp, sIGNO); } / * Interrupt PGRP represented transaction * / INT SUSPEND_JOB (PID_T PGRP) { Return Kill_Job (PGRP, Sigstop); } / * Continue to perform PGRP represented in the background * / Int resume_job_bg (PID_T PGRP) { Return Kill_Job (PGRP, Sigcont); } / * Continue to perform PGRP represented in the front desk * / Int resume_job_fg (PID_T PGRP) { PID_T CURPGRP; int Ctty; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (Curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; IF (curpgrp! = getpgrp ()) Return Errno = Eperm, (PID_T) -1; IF (Assign_Terminal (CTTTY, PGRP) <0) Return -1; Return Kill_Job (PGRP, Sigcont); } / * Place us to the front desk, such as calling a forefront of a foreground transaction * / Int product_self () { PID_T CURPGRP; int Ctty; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; Return Assign_Terminal (ctty, getpgrp ()); } / * Closeall () - Close All> = Document Descriptor for a given FD * / Void Closeall (INT FD) { INT fdlimit = sysconf (_sc_open_max); While (fd Close (fd ); } / * Similar to the system () function, but the given command is executed as a background transaction, returning the shell process * The process number (and also the process group number of this transaction, suitable for kill_job, etc.). * If the parameters INFD, OUTFD, or ERRFD is non-NULL, open a pipe and a file description * The * is saved with the parent processes associated with the pipe, and then it will be oriented to / dev / null in the child process. * Close all> 2 file descriptors in the child process (a work that is often over-estimated) * / PID_T SPAWN_BACKGROUND_COMMAND (Const Char * CMD, INT * INFD, INT * OUTFD, INT * ERRFD) { INT NULLFD = -1; INT PIPEFDS [3] [2]; INT Error = 0; IF (! cmd) Return Errno = Einval, -1; PIPEFDS [0] [0] = PIPEFDS [0] [1] = -1; PIPEFDS [1] [0] = PIPEFDS [1] [1] = -1; PIPEFDS [2] [0] = PIPEFDS [2] [1] = -1; IF (INFD && PIPE (PIPEFDS [0]) <0) Error = errno; Else IF (Outfd && Pipe (Pipefds [1]) <0) Error = errno; Else IF (Errfd && Pipe (Pipefds [2]) <0) Error = errno; IF (! Error &&! (Infd && Outfd &&fd)) { NULLFD = Open ("/ dev / null", o_rdwr); IF (NULLFD <0) Error = errno; } IF (! error) { PID_T PID = SPAWN_JOB (0, -1); Switch (PID) { Case -1: / * fork failed * / Error = errno; Break; Case 0: / * child process * / DUP2 (INFD? PIPEFDS [0] [0]: NULLFD, 0); DUP2 (OUTFD? PIPEFDS [1] [1]: NULLFD, 1); DUP2 (ERRFD? PIPEFDS [2] [1]: NULLFD, 2); Closeall (3); Execl ("/ Bin / SH", "SH", "- C", cmd, (char *) null; _exit (127); Default: / * Parent Process * / Close (NULLFD); IF (INFD) Close (Pipefds [0] [0]), * INFD = PIPEFDS [0] [1]; IF (OUTFD) Close (Pipefds [1] [1]), * OUTFD = PIPEFDS [1] [0]; IF (errfd) Close (Pipefds [2] [1]), * Errfd = PIPEFDS [2] [0]; Return PID; } } / * Execute it here * / { INT I, J; FOR (i = 0; i <3; i) For (j = 0; j <2; J) IF (PIPEFDS [I] [J]> = 0) Close (PIPEFDS [I] [J]); } IF (NULLFD> = 0) Close (NULLFD); Return Errno = Error, (PID_T) -1; } / * --------------------------------------- * / / * Here is a small example of using the above functions. * / PID_T BGJOB = -1; Volatile Int Signo = 0; #ifndef wcoredump / * If there is no WcoredUmp, you may want to set an accurate definition for it on your platform. * (This is usually (status & 0x80) but not always like this), or bet without Core Dumps. * Is what you do this program) * / # define wcoredump (status) (0) #ENDIF Int check_children () { PID_T PID; Int status; INT count = 0; While ((PID = WaitPID (-1, & status, wnohang | wuntraced)> 0) { IF (PID == BGJOB &&! Wifstopped (status)) BGJOB = -1; COUNT; IF (Wifexited (status)) FPRINTF (stderr, "process% ld exited with return code% d / n", (long) PID, WexitStatus (status); Else IF (WiFSignaled (status)) FPRINTF (stderr, "process% ld killed by signal% D% S / N", (long) PID, WTERMSIG (STATUS), WCOREDUMP (STATUS)? ":" ""; Else IF (status)) fprintf (stderr, "process% ld stopped by signal% d / n", (long) PID, Wstopsig (Status); Else FPRINTF (stderr, "Unexpected Status - PID =% LD, Status = 0x% x / N", (long) PID, STATUS; } Return count; } Void Sighandler (Int Sig) { IF (SIG! = SIGCHLD) SIGNO = SIG; } int main () { Struct SigAction Act; INT Sigcount = 0; Act.sa_handler = sighandler; Act.sa_flags = 0; SiGemptySet (& Act.sa_mask); SigAction (Sigint, & Act, Null); SigAction (SIGQUIT, & ACT, NULL); SigAction (Sigterm, & Act, Null); SigAction (SIGTSTP, & ACT, NULL); Sigction (Sigchld, & Act, Null); FPrintf (stderr, "starting background job 'sleep 60' / n"); BGJOB = SPAWN_BACKGROUND_COMMAND ("Sleep 60", NULL, NULL, NULL); IF (BGJOB <0) { PERROR ("spawn_background_command); Exit (1); } FPRINTF (stderr, "background job started with id% ld / n", (long) bgjob); While (BGJOB> = 0) { IF (SIGNO) { FPRINTF (stderr, "signalr% d caught / n", sIGNO); IF (Sigcount ) Kill_Job (BGJOB, SIGKILL); Else { Kill_Job (BGJOB, SIGTERM); Kill_Job (BGJOB, SIGCONT); } } IF (! Check_children ()) Pause (); } FPRINTF (stderr, "done - exiting / n"); Return 0; } UNIX programming FAQ Solution "6 Tools" 6. Use of tools ************ 6.1 How do I debug a child process generated by the FORK function? ===================================== There are two different methods based on available tools: Your debugger may have allow you to choose whether to track the parent or sub-process after the call () ' The option is enough for some purposes. The replacement method is that your debugger may have an option to allow you to attach it (Attach) to a prompt program of. This allows you to rely on a child process that has already begun. If you don't need it from The child process starts testing at the beginning, which is usually enough. Otherwise, you will want to insert a 'Sleep ()' call after the 'fork ()' call, or insert the following loop: { Volatile INT f = 1; While (f); } Such a process will continue to perform down this loop until you set 'f' to 0 with a debugger. And remember that using the debugger is not the only way to find errors in your program; in many UNIX systems Some tool programs can be used to track system calls and signals, and rich logs often have use. 6.2 How to establish a new library file through other library files? ======================================= The premise is what we are talking is archive (static) library, the easiest way is to put all the libraries File Use the 'AR X' command to split into their original single target file (Object) in an empty directory. Then merge into one. Of course, the file name may be repeated, but if this library file is large, You may not want to merge them together at the beginning. 6.3 How to create a dynamic connection library (Shared library) / dlls? ================================================================================= The method of creating a dynamic link libraries is different depending on the system. This process is mainly Two steps; the first step requires that the goal in the dynamic connection library must first be compiled, usually To have a compilation option indicating that the string code is position-independene; the second step is These goals are connected together to form a library file. Here is a demonstration of demonstrations: / * Shrobj.c file * / Const char * myfunc () { Return "Hello World"; } / * Shrobj.c end * / / * Hello.c file * / #include EXTERN Const char * myfunc (); Main () { Printf ("% s / n", myfunc ()); Return 0; } / * Hello.c end * / $ gcc-fpic -c shrobj.c $ gcc -shared -o lobshared.so shrobj.o $ GCC Hello.c Libshared.so $ ./a.out Hello World So far, if you want the library file and its creation process, it is best to transplant. The way is to use the GNU Libtool package. It is a small toolbox, these tools Know the platform of establishing a dynamic connection library unrelated; you can only post your program necessary part, thus When an installer configures your package, he can decide what library is generated. LibTool package is not supported Dynamically connect the library's system well. It knows to hook with the GNU AutoConf program and the Gnuautomake program (if you use these tools to manage your program's compilation process). If you don't want to use the libtool package, then the compiler other than GCC, you need to follow Column Modify Compiler Parameters: AIX 3.2 uses XLC (not confirmed) Cancel the '-fpic' option, with '-Bm: Sre -be: libshared.exp' replaces '-shared'. You and Need to create a file called 'libshared.exp' Save all output symbols (Symbols to export) Lists, such as the above sample programs, you need to output the 'MyFunc' symbol. In addition, in the connection library Use the '-e _nostart' parameter (on the newer AIX version, I believe it should turn it into '-BnoEntry'). SCO OpenServer 5 uses the SCO development system (unconfirmed) If you use ELF (Translator Note: ELF: Performing with Connection Format Executable and Linking Forrmat, A UNIX executable target file format) format, then shared libraries can only be available on OS5, and it Need '-belf' options. And replaced '-fpic' in '-kpic', using 'cc -belf -g' when connecting. Solaris uses SparcWorks compiler Replacing '-fpic' with '-PIC', and replaces 'gcc -shared' with 'ld -g'. (Encourage you to provide more materials to enrich the above list) Others should be careful: * AIX and (I believe) Digital Unix does not need the -FPIC option, because all code is unrelated. * AIX usually requires you to create a 'output file', that is, a save all dynamic connection libraries List of symbols. Some connector (Linker) (may have a SLHS connector, is SVLD?) Has one Options can output all symbols. * If you want to use a popular '-l' parameter for the connector to reference your dynamic connection library, you must Understand how your system is actually running to find a dynamic connection library. The most common method is to use 'Ld_library_path' environment variables, but there is usually one other option when connecting set up. * Most implementation methods are where the desired dynamic connection library in the program is recorded in the program. This Make a dynamic connection library from a directory to another directory will cause the program to work. a lot of The system has an option for the connector to set the location where you want to run the dynamic connection library (for example The Solaris system is the '-r' connector option, or the 'ld_run_path' environment variable). * ELF and A. Excovement method may have a connector option '-Bsymbolic', which leads to the library The reference to the inside is explained. Otherwise, in these systems, all symbols explains will be postponed to the most After the connection is connected, then the single function in the main program will be the corresponding function in the heavy load library. 6.4 Can I change the goal in a dynamic connection library? ======================================= Generally can't. On most systems (except AIX), when you connect the target and form a dynamic connection library, it is like An executable program; the goal does not retain their single feature. As a result, it is generally not possible to move from one move. A single goal is taken out or replaced by the Log Library. 6.5 Can I generate a stack image in a running program? ============================================== Some system provides library functions to extract (unwinding), so that you can be in an error. A stack image is generated in the handler, but only a small part of the system has these functions. One possible workaround is a debugger debugging for your program * it * - The detailed approach is still slightly different according to different systems, but the general concept is this: Void dump_stack (void) { CHAR S [160]; Sprintf (s, "/ bin / echo 'where / ndetach' | dbx -a% d", getpid ()); SYSTEM (S); Return; } You need to process DBX parameters and commands based on your different systems, or even change another debugging For example, 'gdb', but this is still my most common solution for this problem. to this end, Honor grant Ralph Corderoy. The following list contains the command lines that need to be used on some systems: Most of the systems using DBX `" / bin / echo 'where / ndetach' | dbx / path / to / program% d "" AIX `" / bin / echo 'where / ndetach' | dbx -a% d " Irix `" / bin / echo 'where / ndetach' | dbx -p% d "" ? Sample program ********* Capture SigchLD signal ================== #include #include #include #include #include Void Sig_chld (int); / * Prototype * / int main () { Struct SigAction Act; PID_T PID; / * Set the SIG_CHLD function as the processing function of our SIGCHLD signal * / Act.sa_handler = SIG_CHLD; / * In this example program, we don't want to block other signals * / SiGemptySet (& Act.sa_mask); / * * We are only trying to resize the troops to the child's sub-process (such as the user presses Control-Z on the terminal) * / Act.sa_flags = sa_nocldstop; / * * Make these set values take effect. If we are writing a real app, * We may keep these original values, not to pass a NULL. * / IF (SigAction (Sigchld, & Act, NULL) <0) { FPRINTF (stderr, "sigaction failed / n"); Return 1; } / * fork * / Switch (PID = for ()) { Case -1: FPRINTF (stderr, "fork failed / n"); Return 1; Case 0: / * is a child process, directly end * / _exit (7); / * Exit status = 7 * / Default: / * Parent Process * / Sleep (10); / * Time to complete the child process * / } Return 0; } / * * Signal Processing Function - Only when a SIGCHLD signal is received, * There is a child process to terminate * / Void Sig_Chld (int Signo) { Int status, child_val; / * Non-blockedly waiting for any child process to end * / IF (Waitpid (-1, & status, wnohang <0) { / * * Not recommended to call standard input / output functions in the signal processing function, * But there is no problem in a toy program like this. * / FPRINTF (stderr, "waitpid failed / n"); Return; } / * * We now have a child process to exit information in the 'status' variable and you can use * Macro defined in Wait.h to operate * / IF (Wifexited) / * child process is normal to exit? * / { Child_val = WexitStatus (status); / * Get an exit status of sub-process * / Printf ("Child's EXITED NORMALLY with Status% D / N", Child_VAL); } } Read Process Table - SunOS 4 Edition ===================================================================================================================================================== #define _kmemuser #include #include #include Char regexpstr [256]; #define init register char * sp = regexpstr; #define getc () (* sp ) #define peekc () (* sp) #define ungetc (c) (--SP) #define return (Pointer) Return (Pointer); #define error (VAL) #include PID_T getPidByname (Char * Name, PID_T SKIPIT) { KVM_T * kd; Char ** arg; Int error; Char * p_name = NULL; Char ExpBuf [256]; Char ** freeme; Int curpid; Struct User * Cur_user; Struct User myuse; Struct Proc * Cur_Proc; IF ((kd = kvm_open (null, null, null, o_rdonly, null) == NULL) { Return (-1); } Sprintf (regexpstr, "^. * /% s $", name); Compile (NULL, EXPBUF, ExpBuf 256, '/ 0'); While (cur_proc = kVm_nextproc (kd)) { Curpid = CUR_PROC-> P_PID; IF ((cur_user = kVm_getu (kd, cur_proc))! = null) { Error = KVM_GETCMD (KD, Cur_Proc, Cur_user, & arg, null); IF (Error == - 1) { IF (Cur_User-> U_COMM [0]! = '/ 0') { p_name = cur_user-> u_comm; } } Else { p_name = arg [0]; } } IF (p_name) { IF (! strcmp (p_name, name) { IF (Error! = - 1) { Free (arg); } IF (Skipit! = - 1 && totetval == Skipit) { Ourretval = -1; } Else { Close (FD); Break; } Break; } Else { IF (Step (p_name, expBuf) { IF (Error! = - 1) { Free (arg); } Break; } } } IF (Error! = - 1) { Free (arg); } p_name = NULL; } KVM_Close (kd); IF (p_name! = null) { Return (Curpid); } Return (-1); } Read process table - SYSV version ==================== PID_T getPidByname (Char * Name, PID_T SKIPIT) { DIR * DP; Struct Dirent * DIRP; PRPSINFO_T RETVAL; Int fd; PID_T urretval = -1; IF ((DP = OPENDIR ("/ proc")) == null) { Return -1; } ChDIR ("/ proc"); While ((DIRP = ReadDir (DP))! = null) { IF (DIRP-> D_NAME [0]! = '.') { IF ((fd = open (DIRP-> D_NAME, O_RDONLY))! = - 1) { IF (IOCTL (FD, Piocpsinfo, & RetVal)! = - 1) { IF (! strcmp (retval.pr_fname, name) { OURRETVAL = (PID_T) ATOI (DIRP-> D_NAME); IF (Skipit! = - 1 && totetval == Skipit) { Ourretval = -1; } Else { Close (FD); Break; } } } Close (FD); } } } CloseDir (DP); Return OurretVal; } Reading Process Table - AIX version 4.2 ======================== # include #include Int getProcs (struct procsinfo *, int, struct fdsinfo *, INT, PID_T *, INT); PID_T getPidByname (char * name, pid_t * nextpid) { Struct procsinfo pi; PID_T RETVAL = (PID_T) -1; PID_T PID; PID = * NextPID; While (1) { IF (GetProcs (& Pi, SizeOf Pi, 0, 0, & PID, 1)! = 1) Break; IF (! strcmp (name, pi.pi_comm)) { RetVal = pi.pi_pid; * nextPid = PID; Break; } } Return RetVal; } Int main (int Argc, char * argv []) { Int curag; PID_T PID; PID_T nextPID; IF (argc == 1) { Printf ("Syntax:% s Exit (1); } For (curarg = 1; curarg { Printf ("Process IDs for% S / N", Argv [Curarg]); For (NextPID = 0, PID = 0; PID! = -1;) IF ((pid = getpidbyname (argv [curarg], & nextpid)! = -1) Printf ("/ T% D / N", PID); } } Read the process table using the POPEN function and the PS command ================================ #include #include #include #include #include Char * procname (PID_T PID) { Static Char line [133], Command [80], * LineP, * token, * cmd; File * fp; Int status; IF (0 == PID) Return (char *) 0; Sprintf (Command, "PS -P% D 2> / dev / null", PID); FP = POPEN (Command, "R"); IF ((file *) 0 == fp) return (char *) 0; / * Read title line * / IF ((char *) 0 == FGETS (Line, SizeOf line, fp)) { PCLOSE (FP); Return (char *) 0; } / * Analyze the list of command names from the title bar. * (BSD style system will indicate the "command" string of commands in column 5, SYSV seems to * Indicates the "CMD" or "Command" string of the command in column 4) * / For (linep = line;; linep = (char *) 0) { IF ((char *) 0 == (Token = STRTOK (LineP, "/ T / N")))))) { PCLOSE (FP); Return (char *) 0; } IF (0 == Strcmp ("Command", token) || 0 == Strcmp ("cmd", token)) {/ * We found Command Listing * / cmd = token; Break; } } / * Read PS (1) Output Row * / IF ((char *) 0 == FGETS (Line, SizeOf line, fp)) { PCLOSE (FP); Return (char *) 0; } / * Catch the word below the Command title ... * / IF ((char *) 0 == (token = STRTOK (CMD, "/ T / N")))))))) { PCLOSE (FP); Return (char *) 0; } Status = PClose (FP); IF (! Wifexited (status) || 0! = WexitStatus (status)) Return (char *) 0; Return token; } Int main (int Argc, char * argv []) { PUTS (Procname))))) Exit (exit_success); } Daemon tool function ================ #include #include #include #include #include #include #include / * Closeall () - Close all> = given file descriptor * / Void Closeall (INT FD) { INT fdlimit = sysconf (_sc_open_max); While (fd Close (fd ); } / * daemon () - Detach the process from the user and disappears to enter the background, if the failure returns -1, * But in that case you can only exit because we may have generated a child process. * This is based on the BSD version, so the call party needs to be similar to other work similar to Umask. * / / * I believe you can work on all POSIX systems * / Int daemon (int Nochdir, int NoClose) { Switch (fork ()) { Case 0: Break; Case -1: return -1; Default: _exit (0); / * Original process exit * / } IF (setsid () <0) / * should not fail * / Return -1; / * If you want to get a control TTY in the future, exclude (DYKE) Switch statement * / / * - Normal situation is not recommended for daemon * / Switch (fork ()) { Case 0: Break; Case -1: return -1; DEFAULT: _EXIT (0); } IF (! nochdir) chdir ("/"); IF (! noclose) { Closeall (0); Open ("/ dev / null", o_rdwr); DUP (0); DUP (0); } Return 0; } / * fork2 () - Similar to the fork function, the child process immediately into a orphan process * (Do not produce a zombie process when it exits) * Returns 1 to the parent process, not any meaningful process number. * The parent process cannot use the WAIT function waiting for the sub-process (they are independent). * / / * This version assumes that you did not capture and ignore the sigchld signal. * / / * If you have a setting, no matter how you should use the Fork function * / Int fork2 () { PID_T PID; Int rc; Int status; IF (! (pid = for ()))))) { Switch (fork ()) { Case 0: Return 0; Case -1: _exit (errno); / * Assume that the error code is less than 256 * / DEFAULT: _EXIT (0); } } IF (PID <0 || Waitpid (PID, & Status, 0) <0) Return -1; IF (Wifexited (status)) IF (WexitStatus (status) == 0) Return 1; Else Errno = WexitStatus (status); Else Errno = Eintr; / * Hey, like this :-) * / Return -1; } A sample program for using the above functions: #include #include #include #include #include #include #include INT daemon (int, int); Int fork2 (void); Void Closeall (int); #define TCP_Port 8888 Void Errexit (Const Char * STR) { Syslog (log_info, "% s failed:% d (% m)", str, errno; Exit (1); } Void Errreport (Const Char * STR) { Syslog (log_info, "% s failed:% d (% m)", str, errno; } / * The actual child process here. * / Void Run_Child (int Sock) { FILE * IN = FDOPEN (SOCK, "R"); FILE * OUT = FDOPEN (SOCK, "W"); int CH; Setvbuf (in, null, _iofbf, 1024); Setvbuf (Out, NULL, _IOLBF, 1024); While ((ch = fgetc (in))! = EOF) FPUTC (TouPper (CH), OUT) Fclose (OUT); } / * This is the main job of the daemon - listening connection and generates sub-process * / void process () { Struct SockAddr_in Addr; Int Addrlen = SizeOf (AddR); INT SOCK = Socket (AF_INET, SOCK_STREAM, 0); INT flag = 1; Int rc = setsockopt (SOCK, SOL_SOCKET, SO_REUSEADDR, & flag, Sizeof (Flag)); IF (RC <0) Errexit ("setsockopt"); Addr.sin_Family = AF_INET; Addr.sin_Port = HTONS (TCP_PORT); Addr.sin_addr.s_addr = incdr_any; RC = Bind (Sock, (Struct SockAddr *) & addr, addrlen; IF (RC <0) Errexit ("bind"); RC = Listen (Sock, 5); IF (RC <0) Errexit ("Listen"); For (;;) { Rc = accept (Sock, (Struct SockAddr *) & addr, & address; IF (rc> = 0) Switch (fork2 ()) { Case 0: Close (SOCK); Run_Child (RC); _exit (0); Case -1: Errreport ("fork2"); Close (RC); Break; DEFAULT: Close (RC); } } } int main () { IF (daemon (0, 0) <0) { PERROR ("daemon"); EXIT (2); } OpenLog ("Test", Log_PID, LOG_DAEMON); PROCESS (); Return 0; } Modem Control Example Program ====================== / * Send some simple modem command * The device name of the serial device (preferably all dialing the device, * Or non-modem control device) as its unique parameters. * If you don't have a copier-out device that can be shared, then replace clocal with cflags_to_set. * / #include #include #include #include #include #include #include #include #include #include #include #define cflags_to_set (cread | hupcl) #define cflags_to_clear (CStopb | Parenb | Clocal) ENUM FLOWMODE {Noflow, Hardflow, SoftFlow}; / * Related to the system * / #define cflags_hardflow (crtscts) #define eXample_baud b19200 #define example_flow hardflow Static void Die (const char * msg) { FPRINTF (stderr, "% s / n", msg); Exit (1); } Static Int Close_and_complain (int FD, Const Char * MSG, INT ERR) { FPRINTF (stderr, "% s:% s / n", msg, strrror (err)); IF (fd> = 0) Close (FD); Errno = ERR; Return -1; } INT OPEN_PORT (const char * name, speted_t baud, enum flowmode flow) { Int flags; Struct Termios Attr; INT fd = open (name, o_rdwr | o_nonblock | o_nock); IF (fd <0) Return Close_and_complain (-1, "open", errno); / * Set some unclear if sensitive value * / IF (Tcgetattr (FD, & Attr) <0) Return Close_and_complain (fd, "tcgetattr", errno; / * No special input or output processing * / Attr.c_iflag = (FLOW == SoftFlow)? (ixon | ixoff): 0; Attr.c_oflag = 0; / * Setting 8 characters wide and some miscellaneous control mode * / Attr.c_cflag & = ~ (CSIZE | CFLAGS_TO_TO_CLAR | CFLAGS_HARDFLOW); Attr.c_cflag | = (CS8 | CFLAGS_TO_SET); IF (Flow == Hardflow) Attr.c_cflag | = cflags_hardflow; / * Normal mode * / Attr.c_lflag & = ~ (Icanon | Echo | Echoe | Echok | Isig); / * Special characters - Many have been canceled by previous settings * / { INT I; #ifdef _POSIX_VDISABLE Attr.c_cc [0] = _POSIX_VDISABLE; #ELSE Attr.c_cc [0] = fpathconf (fd, _pc_vdisable); #ENDIF For (i = 1; i Attr.c_cc [i] = attr.c_cc [0]; } Attr.c_cc [vStart] = 0x11; Attr.c_cc [vStop] = 0x13; / * Time control of the READ () function * / Attr.c_cc [vmin] = 1; Attr.c_cc [vTIME] = 0; / * Portrates * / Cfsetispeed (& Attr, Baud); Cfsetospeed (& Attr, Baud); / * Write settings * / IF (TcSetattr (FD, TCSANOW, & Attr) <0) RETURN CLOSE_AND_COMPLAIN (FD, "TcSetattr", Errno); / * If the system remembers the previous O_NONBLOCK setting, cancel it * / Flags = fcntl (fd, f_getfl, 0); IF (Flags <0) Return Close_and_Complain (FD, "FCNTL (Getfl)", Errno); IF (FCNTL (FD, F_SETFL, FLAGS & ~ O_NONBLOCK <0) Return Close_and_complain (fd, "FCNTL (SETFL)", Errno; Return fd; } / * Some simple timing tool functions * / / * 向 * TV plus second and useless * / Static Void Timeradd (Struct Timeval * TV, Long Secs, long useless { TV-> TV_SEC = SECS; IF ((TV-> TV_USEC = USECS)> = 1000000) { TV-> TV_sec = TV-> TV_USEC / 1000000; TV-> TV_USEC% = 1000000; } } / * Set * res = * a - * b, return result of symbol * / Static int Timersub (struct timeval * res, Const struct timeval * a, const struct timeval * b) { Long sec = a-> TV_sec - b-> tv_sec; Long usec = a-> TV_USEC - B-> TV_USEC; IF (USEC <0) Usec = 1000000, - SEC; RES-> TV_SEC = sec; RES-> TV_USEC = USEC; RETURN (SEC <0)? (-1): ((sec == 0 && usec == 0)? 0: 1); } / * This function does not attempt to handle abnormal strings (such as ABABC) * Time-time * A more common approach is to use the ALARM () function to process timeout. * This function is a simply starting without using signal processing and attempts to provide an alternative method. * / Int Expect (int FD, Const Char * STR, INT TIMEO) { Int matchlen = 0; INT LEN = Strlen (STR); Struct TimeVal now, end, left; FD_SET FDS; Char C; GetTimeOfDay (& End, NULL); TIMERADD (& End, Timeo / 1000, Timeo% 1000); While (Matchlen { GetTimeOfDay (& NOW, NULL); IF (Timersub (& LEFT, & End, & now) <= 0) Return -1; FD_ZERO (& FDS); FD_SET (FD, & FDS); IF (SELECT (FD 1, & fds, NULL, NULL, & LEFT) <= 0) Return -1; IF (READ, & C, 1)! = 1) Return -1; IF (isprint (unsigned char) || c == '/ n' || c == '/ r') PUTCHAR (C); Else Printf ("// x% 02x", c); IF (c == Str [matchlen]) Matchlen; Else Matchlen = 0; } Return 0; } INT main (int Argc, char ** argv) { Int fd; UNSIGNED CHAR C; IF (Argc <2) DIE ("NO Port Specified"); Setvbuf (stdout, null, _ionbf, 0); FD = Open_PORT (Argv [1], Example_baud, Example_Flow); IF (fd <0) Die ("Cannot Open Port"); Write (FD, "AT / R", 3); IF (Expect (FD, "OK", 5000) <0) { Write (FD, "AT / R", 3); IF (Expect (FD, "OK", 5000) <0) { Tcflush (FD, TCIOFLUSH); Close (FD); Die ("No Response to At"); } } Write (FD, "ATI4 / R", 5); EXPECT (FD, "OK", 10000); PUTCHAR ('/ n'); Tcflush (FD, TCIOFLUSH); Close (FD); Return 0; } Transaction control sample program ================ / * Generate a function of reception / background transaction * / #include #include #include #include #include #include #include #include / * Some of the following functions will fail because it cannot locate the TTY and the caller are not in the front desk. * When the first case, we assume that a front program will have a CTTTTTTY that is output for standard input, standard output, or standard error output. * And if it is not returned to enotty. * The second case, except for the special case of the Foreground_self () function, * If a non-front program intends to output something to the front desk, we return to Eperm. * (Maybe too much) * / / * A terminal (open a ctty) is scheduled for a given PGRP. * This tcsetpgrp () housing is only required for the place in POSIX (BOGUSITY); * Pass Sigttou when the standard system calls TCSetPGRP function in a non-front process * Signal (almost always like this). This is a victory of false consistency. * / INT Assign_Terminal (int Ctty, PID_T PGRP) { Sigset_t sigs; Sigset_t oldsigs; Int rc; SiGemptySet (& SIGS); SigaddSet (& SIGS, SIGTTOU); SigProcmask (SIG_BLOCK, & SIGS, & OLDSIGS); RC = TcSetPGRP (CTTY, PGRP); SigProcmask (SIG_SETMASK, & OLDSIGS, NULL); Return RC; } / * Similar to the Fork function, but do transaction control. If the newly established process is placed in the front desk, it will be true. * (This implicitly places the caller process to the background, so it is necessary to do this after this is the input / output of TTY) * Set PGRP to -1 to create a new transaction, and the process number returned in this case is the process group number of the new transaction. * Or set a transaction existing in the same session (generally only the second or second or second in the pipe operation * Process). * / PID_T SPAWN_JOB (int FG, PID_T PGRP) { INT CTTY = -1; PID_T PID; / * If you generate a * new * front desk transaction, at least the standard input, standard output or * One of the standard error output points to control TTY, and the current process is on the front desk. * Check the TTY in the control only when a new reception process is started in the transaction. * A session that does not control TTY can only have a background transaction. * / IF (fg) { PID_T CURPGRP; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; IF (PGRP <0 && curpgrp! = getpgrp ()) Return Errno = Eperm, (PID_T) -1; } Switch (PID = for ()) { Case -1: / * fork failed * / Return PID; Case 0: / * child process * / / * Establish a new process group, put us in the front desk if needed * I don't know what if the setpgid function call fails ("will not happen") * / IF (PGRP <0) PGRP = getPid (); IF (setpgid (0, pgrp) == 0 && fg) Assign_Terminal (CTTY, PGRP); Return 0; Default: / * Parent Process * / / * Here, you are also built. * / IF (PGRP <0) PGRP = PID; Setpgid (PID, PGRP); Return PID; } / * Will not be executed here * / } / * Kill the transaction representing PGRP with SIGNO * / INT KILL_JOB (PID_T PGRP, INT Signo) { Return Kill (-pgrp, sIGNO); } / * Interrupt PGRP represented transaction * / INT SUSPEND_JOB (PID_T PGRP) { Return Kill_Job (PGRP, Sigstop); } / * Continue to perform PGRP represented in the background * / Int resume_job_bg (PID_T PGRP) { Return Kill_Job (PGRP, Sigcont); } / * Continue to perform PGRP represented in the front desk * / Int resume_job_fg (PID_T PGRP) { PID_T CURPGRP; int Ctty; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (Curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; IF (curpgrp! = getpgrp ()) Return Errno = Eperm, (PID_T) -1; IF (Assign_Terminal (CTTTY, PGRP) <0) Return -1; Return Kill_Job (PGRP, Sigcont); } / * Place us to the front desk, such as calling a forefront of a foreground transaction * / Int product_self () { PID_T CURPGRP; int Ctty; IF ((curpgrp = tcgetpgrp (ctty = 2)) <0 && (curpgrp = tcgetpgrp (ctty = 0)) <0 && (curpgrp = tcgetpgrp (ctty = 1)) <0) Return Errno = Enotty, (PID_T) -1; Return Assign_Terminal (ctty, getpgrp ()); } / * Closeall () - Close All> = Document Descriptor for a given FD * / Void Closeall (INT FD) { INT fdlimit = sysconf (_sc_open_max); While (fd Close (fd ); } / * Similar to the system () function, but the given command is executed as a background transaction, returning the shell process * The process number (and also the process group number of this transaction, suitable for kill_job, etc.). * If the parameters INFD, OUTFD, or ERRFD is non-NULL, open a pipe and a file description * The * is saved with the parent processes associated with the pipe, and then it will be oriented to / dev / null in the child process. * Close all> 2 file descriptors in the child process (a work that is often over-estimated) * / PID_T SPAWN_BACKGROUND_COMMAND (Const Char * CMD, INT * INFD, INT * OUTFD, INT * ERRFD) { INT NULLFD = -1; INT PIPEFDS [3] [2]; INT Error = 0; IF (! cmd) Return Errno = Einval, -1; PIPEFDS [0] [0] = PIPEFDS [0] [1] = -1; PIPEFDS [1] [0] = PIPEFDS [1] [1] = -1; PIPEFDS [2] [0] = PIPEFDS [2] [1] = -1; IF (INFD && PIPE (PIPEFDS [0]) <0) Error = errno; Else IF (Outfd && Pipe (Pipefds [1]) <0) Error = errno; Else IF (Errfd && Pipe (Pipefds [2]) <0) Error = errno; IF (! Error &&! (Infd && Outfd &&fd)) { NULLFD = Open ("/ dev / null", o_rdwr); IF (NULLFD <0) Error = errno; } IF (! error) { PID_T PID = SPAWN_JOB (0, -1); Switch (PID) { Case -1: / * fork failed * / Error = errno; Break; Case 0: / * child process * / DUP2 (INFD? PIPEFDS [0] [0]: NULLFD, 0); DUP2 (OUTFD? PIPEFDS [1] [1]: NULLFD, 1); DUP2 (ERRFD? PIPEFDS [2] [1]: NULLFD, 2); Closeall (3); Execl ("/ Bin / SH", "SH", "- C", cmd, (char *) null; _exit (127); Default: / * Parent Process * / Close (NULLFD); IF (INFD) Close (Pipefds [0] [0]), * INFD = PIPEFDS [0] [1]; IF (OUTFD) Close (Pipefds [1] [1]), * OUTFD = PIPEFDS [1] [0]; IF (errfd) Close (Pipefds [2] [1]), * Errfd = PIPEFDS [2] [0]; Return PID; } } / * Execute it here * / { INT I, J; FOR (i = 0; i <3; i) For (j = 0; j <2; J) IF (PIPEFDS [I] [J]> = 0) Close (PIPEFDS [I] [J]); } IF (NULLFD> = 0) Close (NULLFD); Return Errno = Error, (PID_T) -1; } / * --------------------------------------- * / / * Here is a small example of using the above functions. * / PID_T BGJOB = -1; Volatile Int Signo = 0; #ifndef wcoredump / * If there is no WcoredUmp, you may want to set an accurate definition for it on your platform. * (This is usually (status & 0x80) but not always like this), or bet without Core Dumps. * Is what you do this program) * / # define wcoredump (status) (0) #ENDIF Int check_children () { PID_T PID; Int status; INT count = 0; While ((PID = WaitPID (-1, & status, wnohang | wuntraced)> 0) { IF (PID == BGJOB &&! Wifstopped (status)) BGJOB = -1; COUNT; IF (Wifexited (status)) FPRINTF (stderr, "process% ld exited with return code% d / n", (long) PID, WexitStatus (status); Else IF (WiFSignaled (status)) FPRINTF (stderr, "process% ld killed by signal% D% S / N", (long) PID, WTERMSIG (STATUS), WCOREDUMP (STATUS)? ":" ""; Else IF (status)) fprintf (stderr, "process% ld stopped by signal% d / n", (long) PID, Wstopsig (Status); Else FPRINTF (stderr, "Unexpected Status - PID =% LD, Status = 0x% x / N", (long) PID, STATUS; } Return count; } Void Sighandler (Int Sig) { IF (SIG! = SIGCHLD) SIGNO = SIG; } int main () { Struct SigAction Act; INT Sigcount = 0; Act.sa_handler = sighandler; Act.sa_flags = 0; SiGemptySet (& Act.sa_mask); SigAction (Sigint, & Act, Null); SigAction (SIGQUIT, & ACT, NULL); SigAction (Sigterm, & Act, Null); SigAction (SIGTSTP, & ACT, NULL); Sigction (Sigchld, & Act, Null); FPrintf (stderr, "starting background job 'sleep 60' / n"); BGJOB = SPAWN_BACKGROUND_COMMAND ("Sleep 60", NULL, NULL, NULL); IF (BGJOB <0) { PERROR ("spawn_background_command); Exit (1); } FPRINTF (stderr, "background job started with id% ld / n", (long) bgjob); While (BGJOB> = 0) { IF (SIGNO) { FPRINTF (stderr, "signalr% d caught / n", sIGNO); IF (Sigcount ) Kill_Job (BGJOB, SIGKILL); Else { Kill_Job (BGJOB, SIGTERM); Kill_Job (BGJOB, SIGCONT); } } IF (! Check_children ()) Pause (); } FPRINTF (stderr, "done - exiting / n"); Return 0; }