[Win7 vs2010]
Q: What is the relationship between C library and system APIs?
A: for example, a simple example:
It can be seen that part of the C library uses system APIs to implement its own functions (such as file operations), and the other part does not directly rely on system APIs to implement functions (such as string processing) independently ). In addition, for the driver module, according to different understandings, you can also put it inside the operating system or the lower operating system. If you think of the operating system as an invisible CPU and memory driver, it can also be seen that the hardware drivers in the general sense are of a level. In theory, the C library does not have to be dependent on the driver. Of course, the way to access the operating system is not only the APIS provided by the operating system, but also the way to access the operating system.
Q: What is the relationship between C library and multithreading?
A: The use of multithreading in the operating system has resulted in a lot of libraries, including the previous single-threaded Version C library, which must also be modified to ensure that the operation will not encounter any problems. For example, the following is part of the source code in fgets. C that comes with vs2010:
_TSCHAR * __cdecl _fgetts ( _TSCHAR *string, int count, FILE *str ){ REG1 FILE *stream; REG2 _TSCHAR *pointer = string; _TSCHAR *retval = string; int ch; _VALIDATE_RETURN(( string != NULL ) || ( count == 0 ), EINVAL, NULL); _VALIDATE_RETURN(( count >= 0 ), EINVAL, NULL); _VALIDATE_RETURN(( str != NULL ), EINVAL, NULL); if (count == 0) { return NULL; } /* The C Standard states the input buffer should remain unchanged if EOF is encountered immediately. Hence we do not blank out the input buffer here */ /* Init stream pointer */ stream = str; _lock_str(stream); __try {#ifndef _UNICODE _VALIDATE_STREAM_ANSI_SETRET(stream, EINVAL, retval, NULL);#endif /* _UNICODE */ if (retval!=NULL) { while (--count) { if ((ch = _fgettc_nolock(stream)) == _TEOF) { if (pointer == string) { retval=NULL; goto done; } break; } if ((*pointer++ = (_TSCHAR)ch) == _T('\n')) break; } *pointer = _T('\0'); }/* Common return */done: ; } __finally { _unlock_str(stream); } return(retval);}
It can be seen that _ lock_str will be called first during the call process:
#define _lock_str(s) _lock_file(s)
Internal implementation of _ lock_file:
void __cdecl _lock_file ( FILE *pf ){ /* * The way the FILE (pointed to by pf) is locked depends on whether * it is part of _iob[] or not */ if ( (pf >= _iob) && (pf <= (&_iob[_IOB_ENTRIES-1])) ) { /* * FILE lies in _iob[] so the lock lies in _locktable[]. */ _lock( _STREAM_LOCKS + (int)(pf - _iob) ); /* We set _IOLOCKED to indicate we locked the stream */ pf->_flag |= _IOLOCKED; } else /* * Not part of _iob[]. Therefore, *pf is a _FILEX and the * lock field of the struct is an initialized critical * section. */ EnterCriticalSection( &(((_FILEX *)pf)->lock) );}
For the _ Lock function:
void __cdecl _lock ( int locknum ){ /* * Create/open the lock, if necessary */ if ( _locktable[locknum].lock == NULL ) { if ( !_mtinitlocknum(locknum) ) _amsg_exit( _RT_LOCK ); } /* * Enter the critical section. */ EnterCriticalSection( _locktable[locknum].lock );}
It can be seen that no matter the locking method, it will actually call the original API provided by the system for exclusive access, so as to avoid the read or write error caused by multi-thread access to shared resources.
Q: How do I set a single-threaded C library or a multi-threaded C library?
A: for example, it is set to use multi-threaded C library in vs2010:
According to Microsoft, the single-threaded C library has been removed from vs2005, so you don't have to worry about using the single-threaded C library. If vc6 is used, you can still use the single-threaded Version C library. If the IDE tool is not found, you can use the command line tool to find related options:
Of course, grep requires cygwin support.
Q: The printf function can handle variable parameters. What should I do internally?
A: The parameter-to-stack principle well supports variable parameter processing. That is to say, when an address in the parameter is determined, the address of other parameters changes as the address changes according to the type. As follows:
#define va_start _crt_va_start#define va_end _crt_va_end
The declaration of _ crt_va_start and _ crt_va_end is as follows:
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )#define _crt_va_end(ap) ( ap = (va_list)0 )
As you can see, va_start obtains the first address of the variable parameter, and va_end sets the data of the variable parameter to 0 to end processing. Of course, the macro definition above is a special scenario under the platform, and the definitions under different platforms will be different. By the way, the _ addressof macro and _ intsizeof macro are listed:
#ifdef __cplusplus#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )#else#define _ADDRESSOF(v) ( &(v) )#endif
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
The macro above indicates the size of the sizeof (INT) alignment.
Q: What Operating System APIs does the printf function call internally?
A: From the surface analysis, the system console output API should be called. The following is an analysis of executable files generated by the following code hello. C using IDA:
#include <stdio.h>#include <wchar.h>int main(){ char ch = 'a'; wchar_t wch = (wchar_t)ch; printf("%C\n", wch); return 0; }
Use Cl hello.cto compile hello.exe.
You can see the name of the system API called internally. Of course, the method of using the hook writeconsolew function can also draw a conclusion.
Q: What are the symbols stdin, stdout, and stderr of file input and output?
A: They are file * type variables. Definition:
#define stdin (&__iob_func()[0])#define stdout (&__iob_func()[1])#define stderr (&__iob_func()[2])
_ Iob_func is defined as follows:
/* * FILE descriptors; preset for stdin/out/err (note that the __tmpnum field * is not initialized) */FILE _iob[_IOB_ENTRIES] = { /* _ptr, _cnt, _base, _flag, _file, _charbuf, _bufsiz */ /* stdin (_iob[0]) */ { _bufin, 0, _bufin, _IOREAD | _IOYOURBUF, 0, 0, _INTERNAL_BUFSIZ }, /* stdout (_iob[1]) */ { NULL, 0, NULL, _IOWRT, 1, 0, 0 }, /* stderr (_iob[3]) */ { NULL, 0, NULL, _IOWRT, 2, 0, 0 },};/* These functions are for enabling STATIC_CPPLIB functionality */_CRTIMP FILE * __cdecl __iob_func(void){ return _iob;}
Different platforms and even different environments on the same platform have different understandings of the name meanings such as descriptors, handles, and pointers. Therefore, here we use English to describe. File * is a file operation pointer abstracted by C language, And the _ file member in the file structure can be regarded as handle, which is an integer data, just as stdin corresponds to file handle 0, stdout corresponds to file handle 1, and stderr corresponds to file handle 2.
For file * and file handle, you can use fileno and fdopen to obtain corresponding values.
In addition, the above Code is extracted from the header file _ file. C. stderr (_ iob [3]) should be its error and should be stderr (_ iob [2]).
Q: How does the strtok function Save the intermediate state?
A: The strtok function of a Single-threaded version can store the location information of intermediate processing through static variables, so that subsequent calls can continue to work. For the strtok function of the multi-threaded version, this will not work. In fact, it uses TLS to save the intermediate data. As follows:
struct _tiddata { unsigned long _tid; /* thread ID */ uintptr_t _thandle; /* thread handle */ int _terrno; /* errno value */ unsigned long _tdoserrno; /* _doserrno value */ unsigned int _fpds; /* Floating Point data segment */ unsigned long _holdrand; /* rand() seed value */ char * _token; /* ptr to strtok() token */ wchar_t * _wtoken; /* ptr to wcstok() token */ unsigned char * _mtoken; /* ptr to _mbstok() token */ /* following pointers get malloc'd at runtime */ char * _errmsg; /* ptr to strerror()/_strerror() buff */ wchar_t * _werrmsg; /* ptr to _wcserror()/__wcserror() buff */ char * _namebuf0; /* ptr to tmpnam() buffer */ wchar_t * _wnamebuf0; /* ptr to _wtmpnam() buffer */ char * _namebuf1; /* ptr to tmpfile() buffer */ wchar_t * _wnamebuf1; /* ptr to _wtmpfile() buffer */ char * _asctimebuf; /* ptr to asctime() buffer */ wchar_t * _wasctimebuf; /* ptr to _wasctime() buffer */ void * _gmtimebuf; /* ptr to gmtime() structure */ char * _cvtbuf; /* ptr to ecvt()/fcvt buffer */ unsigned char _con_ch_buf[MB_LEN_MAX]; /* ptr to putch() buffer */ unsigned short _ch_buf_used; /* if the _con_ch_buf is used */ /* following fields are needed by _beginthread code */ void * _initaddr; /* initial user thread address */ void * _initarg; /* initial user thread argument */ /* following three fields are needed to support signal handling and * runtime errors */ void * _pxcptacttab; /* ptr to exception-action table */ void * _tpxcptinfoptrs; /* ptr to exception info pointers */ int _tfpecode; /* float point exception code */ /* pointer to the copy of the multibyte character information used by * the thread */ pthreadmbcinfo ptmbcinfo; /* pointer to the copy of the locale informaton used by the thead */ pthreadlocinfo ptlocinfo; int _ownlocale; /* if 1, this thread owns its own locale */ /* following field is needed by NLG routines */ unsigned long _NLG_dwCode; /* * Per-Thread data needed by C++ Exception Handling */ void * _terminate; /* terminate() routine */ void * _unexpected; /* unexpected() routine */ void * _translator; /* S.E. translator */ void * _purecall; /* called when pure virtual happens */ void * _curexception; /* current exception */ void * _curcontext; /* current exception context */ int _ProcessingThrow; /* for uncaught_exception */ void * _curexcspec; /* for handling exceptions thrown from std::unexpected */#if defined (_M_IA64) || defined (_M_AMD64) void * _pExitContext; void * _pUnwindContext; void * _pFrameInfoChain; unsigned __int64 _ImageBase;#if defined (_M_IA64) unsigned __int64 _TargetGp;#endif /* defined (_M_IA64) */ unsigned __int64 _ThrowImageBase; void * _pForeignException;#elif defined (_M_IX86) void * _pFrameInfoChain;#endif /* defined (_M_IX86) */ _setloc_struct _setloc_data; void * _reserved1; /* nothing */ void * _reserved2; /* nothing */ void * _reserved3; /* nothing */#ifdef _M_IX86 void * _reserved4; /* nothing */ void * _reserved5; /* nothing */#endif /* _M_IX86 */ int _cxxReThrow; /* Set to True if it's a rethrown C++ Exception */ unsigned long __initDomain; /* initial domain used by _beginthread[ex] for managed function */};typedef struct _tiddata * _ptiddata;
We can see that there is a token pointer position to be saved in the intermediate state of the strtok function. The implementation code of strtok can also be seen:
#ifdef _SECURE_VERSION#define _TOKEN *context#else /* _SECURE_VERSION */#define _TOKEN ptd->_token#endif /* _SECURE_VERSION */#ifdef _SECURE_VERSIONchar * __cdecl strtok_s ( char * string, const char * control, char ** context )#else /* _SECURE_VERSION */char * __cdecl strtok ( char * string, const char * control )#endif /* _SECURE_VERSION */{ unsigned char *str; const unsigned char *ctrl = control; unsigned char map[32]; int count;#ifdef _SECURE_VERSION /* validation section */ _VALIDATE_RETURN(context != NULL, EINVAL, NULL); _VALIDATE_RETURN(string != NULL || *context != NULL, EINVAL, NULL); _VALIDATE_RETURN(control != NULL, EINVAL, NULL); /* no static storage is needed for the secure version */#else /* _SECURE_VERSION */ _ptiddata ptd = _getptd();#endif /* _SECURE_VERSION */ /* Clear control map */ for (count = 0; count < 32; count++) map[count] = 0; /* Set bits in delimiter table */ do { map[*ctrl >> 3] |= (1 << (*ctrl & 7)); } while (*ctrl++); /* Initialize str */ /* If string is NULL, set str to the saved * pointer (i.e., continue breaking tokens out of the string * from the last strtok call) */ if (string) str = string; else str = _TOKEN; /* Find beginning of token (skip over leading delimiters). Note that * there is no token iff this loop sets str to point to the terminal * null (*str == '\0') */ while ( (map[*str >> 3] & (1 << (*str & 7))) && *str ) str++; string = str; /* Find the end of the token. If it is not the end of the string, * put a null there. */ for ( ; *str ; str++ ) if ( map[*str >> 3] & (1 << (*str & 7)) ) { *str++ = '\0'; break; } /* Update nextoken (or the corresponding field in the per-thread data * structure */ _TOKEN = str; /* Determine if a token has been found. */ if ( string == str ) return NULL; else return string;}
At the end of the function, _ token = STR; is the operation of tiddata.
If you want to obtain how tiddata is initialized, view the source code (part) of the _ beginthreadex function ):
/* * Allocate and initialize a per-thread data structure for the to- * be-created thread. */ if ( (ptd = (_ptiddata)_calloc_crt(1, sizeof(struct _tiddata))) == NULL ) goto error_return; /* * Initialize the per-thread data */ _initptd(ptd, _getptd()->ptlocinfo); ptd->_initaddr = (void *) initialcode; ptd->_initarg = argument; ptd->_thandle = (uintptr_t)(-1);
Q: How should I write the asserted code?
A: Its core lies in how to output error information and end the program.
#define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
In _ wassert, an error message is assembled and output, and the program is terminated.
Xichen
16:54:53