3. 系统编程概念

这一章讲述了关于system calls入门知识以及在执行过程中的具体细节。无论在system call还是使用library function的情况下,我们都应该检查调用的返回状态以确认是否成功。

目录

3.1 System Calls

3.2 Library Functions

3.3 标准C库,GNU C库glibc

3.4 system call和library functions的错误处理

处理system call errors

处理库函数错误

3.5 使用这本书的一些文件

3.6 移植问题

3.6.1 Feature Test Macros

3.6.2 System Data Types

3.8 练习


3.1 System Calls

对于系统调用而言,有一些点是比较重要的,如下:

  • 一个system call的调用会将处理器状态由user mode转为kernel mode,这样子CPU就可以进入被保护的kernel内存
  • system calls的数量是固定的,所以每一个system call都会被一个唯一的号码所确定
  • 每一个系统调用都可能会有自己的参数等特殊信息由user space传递给kernel space

从程序的角度来讲调用一个system call就像调用一个普通的C函数一样,但是实际在执行system call的过程中有很多其余步骤。我们这里考虑x86-32下的硬件执行,

对照着图3.1,我们可以做一个简要的分析:(以execve为例)

  1. 应用软件会调用execve function,这是一个在提供在C library中的函数(这个时候还没有真正进入内核空间)
  2. wrapper函数execve会根据trap所要求的形式,比如设置eax寄存器为trap number 11(0x0B),将所需对应位置的arguments放在正确的registers里,再调用中断0x80使得触发软中断。具体的一些过程可以参考底下的链接。之后CPU则进入kernel mode做下面的工作。
  3. mov 0x0B, eax
    ...
    int 0x80
    Linux syscall过程分析(万字长文) - 腾讯云开发者社区-腾讯云
  4. 作为对该软件中断的响应,CPU则会进入entry.S中system_call的段落中以处理所触发的trap。这里会存储寄存器里的数据在kernel stack中,检查system call number是否有效,根据sys_call_table而找到下一步的routine,进入到所对应的function中进行下一步,最终service routine给到system_call一个结果。最终通过register返回结果后恢复系统到user mode上。
  5. 如果系统调用不成功,则wrapper function返回errno(一般来讲是负数,具体来说-1)。

一般来讲,对system call的调用的开销会大于对普通user mode下程序使用的开销。

3.2 Library Functions

首先并不是所有的library都会涉及到system call, 比如操作string的函数就是只是发生在user mode下的函数。

但是其他的一些在system call之上的library functions则会使用system call去完成一些任务。比如fopen()里面会调用open()来完成打开文件。这里library function的目的是为了提供可兼容以及用户友好的接口。printf()可以一次性输出很多不同类型的不同数据,但是它里面实际使用的system call write()则是只能输出字节块。

3.3 标准C库,GNU C库glibc

C语言库背后可以有多种实现,但是我们这里讨论linux即基于GNU C语言库glibc。关于查阅树莓派上的版本可以如以下操作:

pi@raspberrypi:/lib/arm-linux-gnueabihf $ /lib/arm-linux-gnueabihf/libc.so.6 
GNU C Library (Debian GLIBC 2.28-10+rpi1) stable release version 2.28.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 8.3.0.
libc ABIs: UNIQUE ABSOLUTE
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.

或者可以通过ldd实现

pi@raspberrypi:/lib/arm-linux-gnueabihf $ ldd --version
ldd (Debian GLIBC 2.28-10+rpi1) 2.28
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

还有一种可能性即在写程序过程中直接从header file中读取,libc-version.h如下

pi@raspberrypi:/usr/include/arm-linux-gnueabihf/gnu $ cat libc-version.h
/* Interface to GNU libc specific functions for version information.
   Copyright (C) 1998-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _GNU_LIBC_VERSION_H
#define	_GNU_LIBC_VERSION_H	1

#include <features.h>

__BEGIN_DECLS

/* Return string describing release status of currently running GNU libc.  */
extern const char *gnu_get_libc_release (void) __THROW;

/* Return string describing version of currently running GNU libc.  */
extern const char *gnu_get_libc_version (void) __THROW;

__END_DECLS

#endif	/* gnu/libc-version.h */

测试程序

#include <gnu/libc-version.h>
#include <stdio.h>

int main()
{
  printf("version is %s\n", gnu_get_libc_version());
  printf("release is %s\n", gnu_get_libc_release());
  printf("__GLIBC__ = %d\n", __GLIBC__);
  printf("__GLIBC_MINOR__=%d\n",__GLIBC_MINOR__);
}

结果可得到

pi@raspberrypi:~/sysprog $ ./test
version is 2.28
release is stable
__GLIBC__ = 2
__GLIBC_MINOR__=28

平时会用到第三方程序的时候版本是非常重要的,这时候可以检查它是否符合我们所需要的版本。当然也可以在程序当中使用__GLIBC__以及__GLIBC_MINOR__的#ifdef 判定来决定它的pre-compile。

3.4 system call和library functions的错误处理

几乎每一个system call和library function都会返回一个处理结果。这些状态值应该每次都被检查,出现错误时要么做对应的处理,要么将它在某个地方展示出来。

而且相比于多花的一点写检查程序的时间,如果软件出了问题,在debug的时候会花费巨大时间来检查。

处理system call errors

一般来说, 如果在有错误的情况下,system call function会返回-1作为标志。我们则可以利用这一点来写错误处理的代码。

为了不必要的问题,建议如果可以的话,在terminal里可以输入man open (举例open system call)。在这里可以找到所对应的return value等等信息。

RETURN VALUE
       open(), openat(), and creat() return the new file descriptor, or -1  if
       an error occurred (in which case, errno is set appropriately).

在上述return value中,我们可以通过-1知道有错误,但是具体是什么我们并不知道,这个时候上述的errno就是我们了解具体问题的来源。它是一个全局整型变量,可以通过include <errno.h>来获得它的声明访问,以及它众多的错误类型定义。

ERRORS
       open(), openat(), and creat() can fail with the following errors:

       EACCES The  requested access to the file is not allowed, or search per‐
              mission is denied for one of the directories in the path  prefix
              of  pathname,  or the file did not exist yet and write access to
              the parent directory is not  allowed.   (See  also  path_resolu‐
              tion(7).)

       EDQUOT Where  O_CREAT  is  specified,  the file does not exist, and the
              user's quota of disk blocks or inodes on the filesystem has been
              exhausted.

       EEXIST pathname already exists and O_CREAT and O_EXCL were used.

       EFAULT pathname points outside your accessible address space.

       EFBIG  See EOVERFLOW.

       EINTR  While  blocked  waiting  to  complete  an  open of a slow device
              (e.g., a FIFO; see fifo(7)), the call was interrupted by a  sig‐
              nal handler; see signal(7).

       EINVAL The  filesystem  does  not support the O_DIRECT flag.  See NOTES
              for more information.

       EINVAL Invalid value in flags.

       EINVAL O_TMPFILE was specified  in  flags,  but  neither  O_WRONLY  nor
              O_RDWR was specified.

       EINVAL O_CREAT  was  specified in flags and the final component ("base‐
              name") of the new file's pathname is invalid (e.g., it  contains
              characters not permitted by the underlying filesystem).

       EISDIR pathname refers to a directory and the access requested involved
              writing (that is, O_WRONLY or O_RDWR is set).

       EISDIR pathname refers to an existing directory, O_TMPFILE and  one  of
              O_WRONLY or O_RDWR were specified in flags, but this kernel ver‐
              sion does not provide the O_TMPFILE functionality.

       ELOOP  Too many symbolic links were encountered in resolving pathname.

       ELOOP  pathname was a symbolic link, and flags specified O_NOFOLLOW but
              not O_PATH.

       EMFILE The per-process limit on the number of open file descriptors has
              been reached (see the  description  of  RLIMIT_NOFILE  in  getr‐
              limit(2)).

       ENAMETOOLONG
              pathname was too long.

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

       ENODEV pathname refers to a device special file  and  no  corresponding
              device  exists.   (This is a Linux kernel bug; in this situation

       ENOENT O_CREAT is not set and the named file does not exist.  Or, a di‐
              rectory  component  in  pathname does not exist or is a dangling
              symbolic link.

       ENOENT pathname refers to a nonexistent directory, O_TMPFILE and one of
              O_WRONLY or O_RDWR were specified in flags, but this kernel ver‐
              sion does not provide the O_TMPFILE functionality.

       ENOMEM The named file is a FIFO, but memory for the FIFO  buffer  can't
              be  allocated  because the per-user hard limit on memory alloca‐
              tion for pipes has been reached and the  caller  is  not  privi‐
              leged; see pipe(7).

       ENOMEM Insufficient kernel memory was available.

       ENOSPC pathname  was  to  be created but the device containing pathname
              has no room for the new file.

       ENOTDIR
              A component used as a directory in pathname is not, in  fact,  a
              directory,  or  O_DIRECTORY was specified and pathname was not a
              directory.

       ENXIO  O_NONBLOCK | O_WRONLY is set, the named file is a FIFO,  and  no
              process has the FIFO open for reading.

       ENXIO  The  file  is  a device special file and no corresponding device
              exists.

       EOPNOTSUPP
              The filesystem containing pathname does not support O_TMPFILE.

       EOVERFLOW
              pathname refers to a regular  file  that  is  too  large  to  be
              opened.  The usual scenario here is that an application compiled
              on a 32-bit platform  without  -D_FILE_OFFSET_BITS=64  tried  to
              open  a  file  whose  size  exceeds  (1<<31)-1  bytes;  see also
              O_LARGEFILE above.  This is the error specified by  POSIX.1;  in
              kernels before 2.6.24, Linux gave the error EFBIG for this case.

       EPERM  The  O_NOATIME  flag was specified, but the effective user ID of
              the caller did not match the owner of the file  and  the  caller
              was not privileged.

       EPERM  The operation was prevented by a file seal; see fcntl(2).

       EROFS  pathname  refers  to  a file on a read-only filesystem and write
              access was requested.

       ETXTBSY
              pathname refers to an executable image which is currently  being
              executed and write access was requested.

       ETXTBSY
              pathname  refers  to  a  file that is currently in use as a swap
              file, and the O_TRUNC flag was specified.

       ETXTBSY
              pathname refers to a file that is currently being  read  by  the
              kernel  (e.g. for module/firmware loading), and write access was
              requested.

       EWOULDBLOCK
              The O_NONBLOCK flag was specified, and an incompatible lease was
              held on the file (see fcntl(2)).

       The following additional errors can occur for openat():

       EBADF  dirfd is not a valid file descriptor.

       ENOTDIR
              pathname  is  a relative pathname and dirfd is a file descriptor
              referring to a file other than a directory.

下面是errno.h的存储位置以及内容。

pi@raspberrypi:/usr/include $ cat errno.h
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

/*
 *	ISO C99 Standard: 7.5 Errors	<errno.h>
 */

#ifndef	_ERRNO_H
#define	_ERRNO_H 1

#include <features.h>

/* The system-specific definitions of the E* constants, as macros.  */
#include <bits/errno.h>

/* When included from assembly language, this header only provides the
   E* constants.  */
#ifndef __ASSEMBLER__

__BEGIN_DECLS

/* The error code set by various library functions.  */
extern int *__errno_location (void) __THROW __attribute_const__;
# define errno (*__errno_location ())

# ifdef __USE_GNU

/* The full and simple forms of the name with which the program was
   invoked.  These variables are set up automatically at startup based on
   the value of argv[0].  */
extern char *program_invocation_name;
extern char *program_invocation_short_name;

#include <bits/types/error_t.h>

# endif /* __USE_GNU */

__END_DECLS

#endif /* !__ASSEMBLER__ */
#endif /* errno.h */

利用return -1我们可以初步得到错误的判断。但如果想要在错误的情况下获得更细节的原因或者相对应更细节的处理,则可以使用errno global variable来做到这一点。

fd = open(pathname,flags,mode); /* system call to open a file */
if(fd = -1)
{
  /* Code to handle the error */
  if (errno == EEXIST)
  {
    /* corresponding error handling */
    fprintf(stderr, "already exist \n"); /* can either add some error message */
  }
  ...
}

但是,有的system call在成功调用的情况下也会返回 -1,比如getpriority(),它的return描述如下:

RETURN VALUE
       On success, getpriority() returns the calling thread's nice value, which may be a negative  number.   On  error, it returns-1 and sets errnoto indicate the cause of the error.  Since a successfulcall to getpriority() can legitimately return the value -1, it is necessary to clear the external  variable errno prior to the call, then check it afterward to determine if -1 is an error or a legitimate value.

       setpriority() returns 0 on success.  On error, it returns -1 and  sets  errno  to  indicate  the cause of the error.
 

除过上述打印errno错误信息可以使用fprintf,也可以用 perror(char* msg)来打印问题在哪里,这里的好处是不需要像fprintf一样显性打印具体错误,这个会由perror根据现在errno中的值来自动输出。

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
  errno = EEXIST;
  perror("hallo");
  exit(EXIT_FAILURE);
  printf("Not exit!!");
}

运行结果

pi@raspberrypi:~/sysprog $ ./test
hallo: File exists

另外我们也发现有很多类型的错误,如果我们只想打印出来是什么错误的话,除了使用perror以外就还有一种方法,可以只提取出来错误是什么(也就是上面结果的File exists,而不用统一格式msg: 错误)并且按照自己的方法做自己想要做的事情。也就是使用*char strerror(int errornum)

#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
  errno = 5;
  printf("error is %s\n", strerror(errno));
}

运行结果

pi@raspberrypi:~/sysprog $ ./test
error is Input/output error

在CSDN中找到了一篇不错的文章,将所有errno error类型都打印出来,平时可以做为一个对照表参阅。

当然所有的error define都可以在/usr/include/asm-generic 中找到。其中基础常用error定义在errno_base.h:

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

#endif

其余的数字定义在errno.h(并非之前的/usr/include/errno.h,这个errno.h以带目录的方式引用了下面的这个errno.h)。如headerfile中自己所述,都是一些额外特殊的错误类型:

/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define	EDEADLK		35	/* Resource deadlock would occur */
#define	ENAMETOOLONG	36	/* File name too long */
#define	ENOLCK		37	/* No record locks available */

/*
 * This error code is special: arch syscall entry code will return
 * -ENOSYS if users try to call a syscall that doesn't exist.  To keep
 * failures of syscalls that really do exist distinguishable from
 * failures due to attempts to use a nonexistent syscall, syscall
 * implementations should refrain from returning -ENOSYS.
 */
#define	ENOSYS		38	/* Invalid system call number */

#define	ENOTEMPTY	39	/* Directory not empty */
#define	ELOOP		40	/* Too many symbolic links encountered */
#define	EWOULDBLOCK	EAGAIN	/* Operation would block */
#define	ENOMSG		42	/* No message of desired type */
#define	EIDRM		43	/* Identifier removed */
#define	ECHRNG		44	/* Channel number out of range */
#define	EL2NSYNC	45	/* Level 2 not synchronized */
#define	EL3HLT		46	/* Level 3 halted */
#define	EL3RST		47	/* Level 3 reset */
#define	ELNRNG		48	/* Link number out of range */
#define	EUNATCH		49	/* Protocol driver not attached */
#define	ENOCSI		50	/* No CSI structure available */
#define	EL2HLT		51	/* Level 2 halted */
#define	EBADE		52	/* Invalid exchange */
#define	EBADR		53	/* Invalid request descriptor */
#define	EXFULL		54	/* Exchange full */
#define	ENOANO		55	/* No anode */
#define	EBADRQC		56	/* Invalid request code */
#define	EBADSLT		57	/* Invalid slot */

#define	EDEADLOCK	EDEADLK

#define	EBFONT		59	/* Bad font file format */
#define	ENOSTR		60	/* Device not a stream */
#define	ENODATA		61	/* No data available */
#define	ETIME		62	/* Timer expired */
#define	ENOSR		63	/* Out of streams resources */
#define	ENONET		64	/* Machine is not on the network */
#define	ENOPKG		65	/* Package not installed */
#define	EREMOTE		66	/* Object is remote */
#define	ENOLINK		67	/* Link has been severed */
#define	EADV		68	/* Advertise error */
#define	ESRMNT		69	/* Srmount error */
#define	ECOMM		70	/* Communication error on send */
#define	EPROTO		71	/* Protocol error */
#define	EMULTIHOP	72	/* Multihop attempted */
#define	EDOTDOT		73	/* RFS specific error */
#define	EBADMSG		74	/* Not a data message */
#define	EOVERFLOW	75	/* Value too large for defined data type */
#define	ENOTUNIQ	76	/* Name not unique on network */
#define	EBADFD		77	/* File descriptor in bad state */
#define	EREMCHG		78	/* Remote address changed */
#define	ELIBACC		79	/* Can not access a needed shared library */
#define	ELIBBAD		80	/* Accessing a corrupted shared library */
#define	ELIBSCN		81	/* .lib section in a.out corrupted */
#define	ELIBMAX		82	/* Attempting to link in too many shared libraries */
#define	ELIBEXEC	83	/* Cannot exec a shared library directly */
#define	EILSEQ		84	/* Illegal byte sequence */
#define	ERESTART	85	/* Interrupted system call should be restarted */
#define	ESTRPIPE	86	/* Streams pipe error */
#define	EUSERS		87	/* Too many users */
#define	ENOTSOCK	88	/* Socket operation on non-socket */
#define	EDESTADDRREQ	89	/* Destination address required */
#define	EMSGSIZE	90	/* Message too long */
#define	EPROTOTYPE	91	/* Protocol wrong type for socket */
#define	ENOPROTOOPT	92	/* Protocol not available */
#define	EPROTONOSUPPORT	93	/* Protocol not supported */
#define	ESOCKTNOSUPPORT	94	/* Socket type not supported */
#define	EOPNOTSUPP	95	/* Operation not supported on transport endpoint */
#define	EPFNOSUPPORT	96	/* Protocol family not supported */
#define	EAFNOSUPPORT	97	/* Address family not supported by protocol */
#define	EADDRINUSE	98	/* Address already in use */
#define	EADDRNOTAVAIL	99	/* Cannot assign requested address */
#define	ENETDOWN	100	/* Network is down */
#define	ENETUNREACH	101	/* Network is unreachable */
#define	ENETRESET	102	/* Network dropped connection because of reset */
#define	ECONNABORTED	103	/* Software caused connection abort */
#define	ECONNRESET	104	/* Connection reset by peer */
#define	ENOBUFS		105	/* No buffer space available */
#define	EISCONN		106	/* Transport endpoint is already connected */
#define	ENOTCONN	107	/* Transport endpoint is not connected */
#define	ESHUTDOWN	108	/* Cannot send after transport endpoint shutdown */
#define	ETOOMANYREFS	109	/* Too many references: cannot splice */
#define	ETIMEDOUT	110	/* Connection timed out */
#define	ECONNREFUSED	111	/* Connection refused */
#define	EHOSTDOWN	112	/* Host is down */
#define	EHOSTUNREACH	113	/* No route to host */
#define	EALREADY	114	/* Operation already in progress */
#define	EINPROGRESS	115	/* Operation now in progress */
#define	ESTALE		116	/* Stale file handle */
#define	EUCLEAN		117	/* Structure needs cleaning */
#define	ENOTNAM		118	/* Not a XENIX named type file */
#define	ENAVAIL		119	/* No XENIX semaphores available */
#define	EISNAM		120	/* Is a named type file */
#define	EREMOTEIO	121	/* Remote I/O error */
#define	EDQUOT		122	/* Quota exceeded */

#define	ENOMEDIUM	123	/* No medium found */
#define	EMEDIUMTYPE	124	/* Wrong medium type */
#define	ECANCELED	125	/* Operation Canceled */
#define	ENOKEY		126	/* Required key not available */
#define	EKEYEXPIRED	127	/* Key has expired */
#define	EKEYREVOKED	128	/* Key has been revoked */
#define	EKEYREJECTED	129	/* Key was rejected by service */

/* for robust mutexes */
#define	EOWNERDEAD	130	/* Owner died */
#define	ENOTRECOVERABLE	131	/* State not recoverable */

#define ERFKILL		132	/* Operation not possible due to RF-kill */

#define EHWPOISON	133	/* Memory page has hardware error */

#endif

处理库函数错误

库函数错误被分为三类

  • 一些库函数表现近似于system call,会以-1作为返回值表示错误,并且辅以errno知悉错误具体是什么。比如remove() (使用的是unlink(),rmdir())。
  • 一些库函数返回非-1的值表示错误,但是依然会设置errno表示具体错误。比如fopen在错误情况下返回NULL指针,并且会设具体errno的值表示具体错误。
  • 另一类库函数完全不会设置errno。对于这类函数需要查看它的手册以了解它的用法。

3.5 使用这本书的一些文件

该书列出了一些后面所有程序都会用到的一些common function和header files,这些文件可以在这里找到并下载下来

List of source code files, by chapter, from "The Linux Programming Interface"

至于对于每个文件的测试和描述,等到后面使用到的时候再在这里补充完全。

3.6 移植问题

3.6.1 Feature Test Macros

在glibc当中,我们不仅有ISO C的定义,有的时候为了满足对BSD或者System V的可以执行,那么我们需要有能力仅使用这些系统所需要的定义(比如constants,相对应的function prototype等等)。 这个时候我们有两种选择可以做,第一种是在使用所有header file之前定义一个feature test macros,比如#define _BSD_SOURCE 1,这样子相应的代码段将会在编译中被使用。

还有一种方式即使用cc -D在compiler中,这样子可以针对所有文件定义需要的#define,比如cc -D_POXIS_SOURCE test.c

则在test.c 中应当使用

#ifdef _POXIS_SOURCE
... /* handling only in case _BSD_SOURCE is defined*/
#elif ... /* other possibilities explicit*/
#else ... /* other possibilities implicit*/
#endif

比如还是在我们之前使用的test.c当中我这样子使用了_POXIS_SOURCE。

#include <stdio.h>
#include <errno.h>
#include <string.h>
int main()
{
  errno = 0;
  printf("error is %s\n", strerror(errno));
#ifdef _POSIX_SOURCE_FAKE
  printf("I am using codes in _POSIX_SOURCE_FAKE!\n");
#else
  printf("I am NOT using codes in _POSIX_SOURCE_FAKE!!!\n");
#endif
}

在执行过程当中可以看到,cc -D_POSIX_SOURCE_FAKE等同于在 source code 里开头定了一个#define _POSIX_SOURCE_FAKE,自然当compile到#ifdef _POSIX_SOURCE_FAKE的时候compiler会自动认为这个已经定义。这么做的最大的好处就是,直接在source code里面定义到后期很难找到定义的位置,而使用makefile作为编译系统的话,就会方便管理很多,尤其是对于大型工程而言。

pi@raspberrypi:~/sysprog $ cc -o test test.c
pi@raspberrypi:~/sysprog $ ./test
error is Success
I am NOT using codes in _POSIX_SOURCE_FAKE!!!
pi@raspberrypi:~/sysprog $ cc -D_POSIX_SOURCE_FAKE -o test test.c
pi@raspberrypi:~/sysprog $ ./test
error is Success
I am using codes in _POSIX_SOURCE_FAKE!

更具体关于什么情况下使用什么macro,请参阅Feature Test Macros (The GNU C Library)

事实上,/usr/include/features已经通过#define _BSD_SOURCE 类似的macro定义了很多default使用cc时已有的macro。事实上

cc -D_POSIX_SOURCE -D_POSIX_C_SOURCE=199506 -D_DEFAULT_SOURCE test.c 
/*_DEFAULT_SOURCE is collection of _BSD_SOURCE and _SVID_SOURCE*/

等同于

cc test.c

3.6.2 System Data Types

如果直接使用int或者long来存储一些系统信息比如 processID,userID等等会碰到一些问题比如

  • 不同的UNIX实现会使用int long对应着不同的bytes
  • 即使同一种UNIX,不同的版本的迭代也有可能改变int long所对应的bytes

所以在SUSv3的实现下,为了取得types的兼容性,则在type类型中利用typedef抽象化了类型。

比如process ID在linux x86-32体系下定义为:

typedef int pid_t

无论 pid_t真正对用的是那种类型,pid_t才是我们在程序当中应该使用的类型。否则如果使用int的话,那么在移植软件到另一个系统里之后,我们要花费多少时间来重新改写这些类型呢?

大多数的标准系统数据类型都以_t结尾,并且大部分这些system types都定义在 <sys/types.h>。在树莓派上,也就是在这里可以找到它所有的内容:

pi@raspberrypi:/usr/include/arm-linux-gnueabihf/sys $ cat types.h
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

/*
 *	POSIX Standard: 2.6 Primitive System Data Types	<sys/types.h>
 */

#ifndef	_SYS_TYPES_H
#define	_SYS_TYPES_H	1

#include <features.h>

__BEGIN_DECLS

#include <bits/types.h>

#ifdef	__USE_MISC
# ifndef __u_char_defined
typedef __u_char u_char;
typedef __u_short u_short;
typedef __u_int u_int;
typedef __u_long u_long;
typedef __quad_t quad_t;
typedef __u_quad_t u_quad_t;
typedef __fsid_t fsid_t;
#  define __u_char_defined
# endif
typedef __loff_t loff_t;
#endif

#ifndef __ino_t_defined
# ifndef __USE_FILE_OFFSET64
typedef __ino_t ino_t;
# else
typedef __ino64_t ino_t;
# endif
# define __ino_t_defined
#endif
#if defined __USE_LARGEFILE64 && !defined __ino64_t_defined
typedef __ino64_t ino64_t;
# define __ino64_t_defined
#endif

#ifndef __dev_t_defined
typedef __dev_t dev_t;
# define __dev_t_defined
#endif

#ifndef __gid_t_defined
typedef __gid_t gid_t;
# define __gid_t_defined
#endif

#ifndef __mode_t_defined
typedef __mode_t mode_t;
# define __mode_t_defined
#endif

#ifndef __nlink_t_defined
typedef __nlink_t nlink_t;
# define __nlink_t_defined
#endif

#ifndef __uid_t_defined
typedef __uid_t uid_t;
# define __uid_t_defined
#endif

#ifndef __off_t_defined
# ifndef __USE_FILE_OFFSET64
typedef __off_t off_t;
# else
typedef __off64_t off_t;
# endif
# define __off_t_defined
#endif
#if defined __USE_LARGEFILE64 && !defined __off64_t_defined
typedef __off64_t off64_t;
# define __off64_t_defined
#endif

#ifndef __pid_t_defined
typedef __pid_t pid_t;
# define __pid_t_defined
#endif

#if (defined __USE_XOPEN || defined __USE_XOPEN2K8) \
    && !defined __id_t_defined
typedef __id_t id_t;
# define __id_t_defined
#endif

#ifndef __ssize_t_defined
typedef __ssize_t ssize_t;
# define __ssize_t_defined
#endif

#ifdef	__USE_MISC
# ifndef __daddr_t_defined
typedef __daddr_t daddr_t;
typedef __caddr_t caddr_t;
#  define __daddr_t_defined
# endif
#endif

#if (defined __USE_MISC || defined __USE_XOPEN) && !defined __key_t_defined
typedef __key_t key_t;
# define __key_t_defined
#endif

#if defined __USE_XOPEN || defined __USE_XOPEN2K8
# include <bits/types/clock_t.h>
#endif
#include <bits/types/clockid_t.h>
#include <bits/types/time_t.h>
#include <bits/types/timer_t.h>

#ifdef __USE_XOPEN
# ifndef __useconds_t_defined
typedef __useconds_t useconds_t;
#  define __useconds_t_defined
# endif
# ifndef __suseconds_t_defined
typedef __suseconds_t suseconds_t;
#  define __suseconds_t_defined
# endif
#endif

#define	__need_size_t
#include <stddef.h>

#ifdef __USE_MISC
/* Old compatibility names for C types.  */
typedef unsigned long int ulong;
typedef unsigned short int ushort;
typedef unsigned int uint;
#endif

/* These size-specific names are used by some of the inet code.  */

#include <bits/stdint-intn.h>

#if !__GNUC_PREREQ (2, 7)

/* These were defined by ISO C without the first `_'.  */
typedef	unsigned char u_int8_t;
typedef	unsigned short int u_int16_t;
typedef	unsigned int u_int32_t;
# if __WORDSIZE == 64
typedef unsigned long int u_int64_t;
# else
__extension__ typedef unsigned long long int u_int64_t;
# endif

typedef int register_t;

#else

/* For GCC 2.7 and later, we can use specific type-size attributes.  */
# define __u_intN_t(N, MODE) \
  typedef unsigned int u_int##N##_t __attribute__ ((__mode__ (MODE)))

__u_intN_t (8, __QI__);
__u_intN_t (16, __HI__);
__u_intN_t (32, __SI__);
__u_intN_t (64, __DI__);

typedef int register_t __attribute__ ((__mode__ (__word__)));


/* Some code from BIND tests this macro to see if the types above are
   defined.  */
#endif
#define __BIT_TYPES_DEFINED__	1


#ifdef	__USE_MISC
/* In BSD <sys/types.h> is expected to define BYTE_ORDER.  */
# include <endian.h>

/* It also defines `fd_set' and the FD_* macros for `select'.  */
# include <sys/select.h>
#endif /* Use misc.  */


#if (defined __USE_UNIX98 || defined __USE_XOPEN2K8) \
    && !defined __blksize_t_defined
typedef __blksize_t blksize_t;
# define __blksize_t_defined
#endif

/* Types from the Large File Support interface.  */
#ifndef __USE_FILE_OFFSET64
# ifndef __blkcnt_t_defined
typedef __blkcnt_t blkcnt_t;	 /* Type to count number of disk blocks.  */
#  define __blkcnt_t_defined
# endif
# ifndef __fsblkcnt_t_defined
typedef __fsblkcnt_t fsblkcnt_t; /* Type to count file system blocks.  */
#  define __fsblkcnt_t_defined
# endif
# ifndef __fsfilcnt_t_defined
typedef __fsfilcnt_t fsfilcnt_t; /* Type to count file system inodes.  */
#  define __fsfilcnt_t_defined
# endif
#else
# ifndef __blkcnt_t_defined
typedef __blkcnt64_t blkcnt_t;	   /* Type to count number of disk blocks.  */
#  define __blkcnt_t_defined
# endif
# ifndef __fsblkcnt_t_defined
typedef __fsblkcnt64_t fsblkcnt_t; /* Type to count file system blocks.  */
#  define __fsblkcnt_t_defined
# endif
# ifndef __fsfilcnt_t_defined
typedef __fsfilcnt64_t fsfilcnt_t; /* Type to count file system inodes.  */
#  define __fsfilcnt_t_defined
# endif
#endif

#ifdef __USE_LARGEFILE64
typedef __blkcnt64_t blkcnt64_t;     /* Type to count number of disk blocks. */
typedef __fsblkcnt64_t fsblkcnt64_t; /* Type to count file system blocks.  */
typedef __fsfilcnt64_t fsfilcnt64_t; /* Type to count file system inodes.  */
#endif


/* Now add the thread types.  */
#if defined __USE_POSIX199506 || defined __USE_UNIX98
# include <bits/pthreadtypes.h>
#endif

__END_DECLS

#endif /* sys/types.h */

不同于书上所述在sys/types.h 里pid_t会直接对应一个int 或 long类型,而是它还有进一步的typedef-->bits/types.h-->bits/typesizes.h

bits/types.h

pi@raspberrypi:/usr/include/arm-linux-gnueabihf/bits $ cat types.h
/* bits/types.h -- definitions of __*_t types underlying *_t types.
   Copyright (C) 2002-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

/*
 * Never include this file directly; use <sys/types.h> instead.
 */

#ifndef	_BITS_TYPES_H
#define	_BITS_TYPES_H	1

#include <features.h>
#include <bits/wordsize.h>

/* Convenience types.  */
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;

/* Fixed-size types, underlying types depend on word size and compiler.  */
typedef signed char __int8_t;
typedef unsigned char __uint8_t;
typedef signed short int __int16_t;
typedef unsigned short int __uint16_t;
typedef signed int __int32_t;
typedef unsigned int __uint32_t;
#if __WORDSIZE == 64
typedef signed long int __int64_t;
typedef unsigned long int __uint64_t;
#else
__extension__ typedef signed long long int __int64_t;
__extension__ typedef unsigned long long int __uint64_t;
#endif

/* Smallest types with at least a given width.  */
typedef __int8_t __int_least8_t;
typedef __uint8_t __uint_least8_t;
typedef __int16_t __int_least16_t;
typedef __uint16_t __uint_least16_t;
typedef __int32_t __int_least32_t;
typedef __uint32_t __uint_least32_t;
typedef __int64_t __int_least64_t;
typedef __uint64_t __uint_least64_t;

/* quad_t is also 64 bits.  */
#if __WORDSIZE == 64
typedef long int __quad_t;
typedef unsigned long int __u_quad_t;
#else
__extension__ typedef long long int __quad_t;
__extension__ typedef unsigned long long int __u_quad_t;
#endif

/* Largest integral types.  */
#if __WORDSIZE == 64
typedef long int __intmax_t;
typedef unsigned long int __uintmax_t;
#else
__extension__ typedef long long int __intmax_t;
__extension__ typedef unsigned long long int __uintmax_t;
#endif


/* The machine-dependent file <bits/typesizes.h> defines __*_T_TYPE
   macros for each of the OS types we define below.  The definitions
   of those macros must use the following macros for underlying types.
   We define __S<SIZE>_TYPE and __U<SIZE>_TYPE for the signed and unsigned
   variants of each of the following integer types on this machine.

	16		-- "natural" 16-bit type (always short)
	32		-- "natural" 32-bit type (always int)
	64		-- "natural" 64-bit type (long or long long)
	LONG32		-- 32-bit type, traditionally long
	QUAD		-- 64-bit type, always long long
	WORD		-- natural type of __WORDSIZE bits (int or long)
	LONGWORD	-- type of __WORDSIZE bits, traditionally long

   We distinguish WORD/LONGWORD, 32/LONG32, and 64/QUAD so that the
   conventional uses of `long' or `long long' type modifiers match the
   types we define, even when a less-adorned type would be the same size.
   This matters for (somewhat) portably writing printf/scanf formats for
   these types, where using the appropriate l or ll format modifiers can
   make the typedefs and the formats match up across all GNU platforms.  If
   we used `long' when it's 64 bits where `long long' is expected, then the
   compiler would warn about the formats not matching the argument types,
   and the programmer changing them to shut up the compiler would break the
   program's portability.

   Here we assume what is presently the case in all the GCC configurations
   we support: long long is always 64 bits, long is always word/address size,
   and int is always 32 bits.  */

#define	__S16_TYPE		short int
#define __U16_TYPE		unsigned short int
#define	__S32_TYPE		int
#define __U32_TYPE		unsigned int
#define __SLONGWORD_TYPE	long int
#define __ULONGWORD_TYPE	unsigned long int
#if __WORDSIZE == 32
# define __SQUAD_TYPE		__quad_t
# define __UQUAD_TYPE		__u_quad_t
# define __SWORD_TYPE		int
# define __UWORD_TYPE		unsigned int
# define __SLONG32_TYPE		long int
# define __ULONG32_TYPE		unsigned long int
# define __S64_TYPE		__quad_t
# define __U64_TYPE		__u_quad_t
/* We want __extension__ before typedef's that use nonstandard base types
   such as `long long' in C89 mode.  */
# define __STD_TYPE		__extension__ typedef
#elif __WORDSIZE == 64
# define __SQUAD_TYPE		long int
# define __UQUAD_TYPE		unsigned long int
# define __SWORD_TYPE		long int
# define __UWORD_TYPE		unsigned long int
# define __SLONG32_TYPE		int
# define __ULONG32_TYPE		unsigned int
# define __S64_TYPE		long int
# define __U64_TYPE		unsigned long int
/* No need to mark the typedef with __extension__.   */
# define __STD_TYPE		typedef
#else
# error
#endif
#include <bits/typesizes.h>	/* Defines __*_T_TYPE macros.  */


__STD_TYPE __DEV_T_TYPE __dev_t;	/* Type of device numbers.  */
__STD_TYPE __UID_T_TYPE __uid_t;	/* Type of user identifications.  */
__STD_TYPE __GID_T_TYPE __gid_t;	/* Type of group identifications.  */
__STD_TYPE __INO_T_TYPE __ino_t;	/* Type of file serial numbers.  */
__STD_TYPE __INO64_T_TYPE __ino64_t;	/* Type of file serial numbers (LFS).*/
__STD_TYPE __MODE_T_TYPE __mode_t;	/* Type of file attribute bitmasks.  */
__STD_TYPE __NLINK_T_TYPE __nlink_t;	/* Type of file link counts.  */
__STD_TYPE __OFF_T_TYPE __off_t;	/* Type of file sizes and offsets.  */
__STD_TYPE __OFF64_T_TYPE __off64_t;	/* Type of file sizes and offsets (LFS).  */
__STD_TYPE __PID_T_TYPE __pid_t;	/* Type of process identifications.  */
__STD_TYPE __FSID_T_TYPE __fsid_t;	/* Type of file system IDs.  */
__STD_TYPE __CLOCK_T_TYPE __clock_t;	/* Type of CPU usage counts.  */
__STD_TYPE __RLIM_T_TYPE __rlim_t;	/* Type for resource measurement.  */
__STD_TYPE __RLIM64_T_TYPE __rlim64_t;	/* Type for resource measurement (LFS).  */
__STD_TYPE __ID_T_TYPE __id_t;		/* General type for IDs.  */
__STD_TYPE __TIME_T_TYPE __time_t;	/* Seconds since the Epoch.  */
__STD_TYPE __USECONDS_T_TYPE __useconds_t; /* Count of microseconds.  */
__STD_TYPE __SUSECONDS_T_TYPE __suseconds_t; /* Signed count of microseconds.  */

__STD_TYPE __DADDR_T_TYPE __daddr_t;	/* The type of a disk address.  */
__STD_TYPE __KEY_T_TYPE __key_t;	/* Type of an IPC key.  */

/* Clock ID used in clock and timer functions.  */
__STD_TYPE __CLOCKID_T_TYPE __clockid_t;

/* Timer ID returned by `timer_create'.  */
__STD_TYPE __TIMER_T_TYPE __timer_t;

/* Type to represent block size.  */
__STD_TYPE __BLKSIZE_T_TYPE __blksize_t;

/* Types from the Large File Support interface.  */

/* Type to count number of disk blocks.  */
__STD_TYPE __BLKCNT_T_TYPE __blkcnt_t;
__STD_TYPE __BLKCNT64_T_TYPE __blkcnt64_t;

/* Type to count file system blocks.  */
__STD_TYPE __FSBLKCNT_T_TYPE __fsblkcnt_t;
__STD_TYPE __FSBLKCNT64_T_TYPE __fsblkcnt64_t;

/* Type to count file system nodes.  */
__STD_TYPE __FSFILCNT_T_TYPE __fsfilcnt_t;
__STD_TYPE __FSFILCNT64_T_TYPE __fsfilcnt64_t;

/* Type of miscellaneous file system fields.  */
__STD_TYPE __FSWORD_T_TYPE __fsword_t;

__STD_TYPE __SSIZE_T_TYPE __ssize_t; /* Type of a byte count, or error.  */

/* Signed long type used in system calls.  */
__STD_TYPE __SYSCALL_SLONG_TYPE __syscall_slong_t;
/* Unsigned long type used in system calls.  */
__STD_TYPE __SYSCALL_ULONG_TYPE __syscall_ulong_t;

/* These few don't really vary by system, they always correspond
   to one of the other defined types.  */
typedef __off64_t __loff_t;	/* Type of file sizes and offsets (LFS).  */
typedef char *__caddr_t;

/* Duplicates info from stdint.h but this is used in unistd.h.  */
__STD_TYPE __SWORD_TYPE __intptr_t;

/* Duplicate info from sys/socket.h.  */
__STD_TYPE __U32_TYPE __socklen_t;

/* C99: An integer type that can be accessed as an atomic entity,
   even in the presence of asynchronous interrupts.
   It is not currently necessary for this to be machine-specific.  */
typedef int __sig_atomic_t;

#undef __STD_TYPE

#endif /* bits/types.h */

bits/typesizes.h

pi@raspberrypi:/usr/include/arm-linux-gnueabihf/bits $ cat typesizes.h 
/* bits/typesizes.h -- underlying types for *_t.  Generic version.
   Copyright (C) 2002-2018 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef _BITS_TYPES_H
# error "Never include <bits/typesizes.h> directly; use <sys/types.h> instead."
#endif

#ifndef	_BITS_TYPESIZES_H
#define	_BITS_TYPESIZES_H	1

/* See <bits/types.h> for the meaning of these macros.  This file exists so
   that <bits/types.h> need not vary across different GNU platforms.  */

#define __DEV_T_TYPE		__UQUAD_TYPE
#define __UID_T_TYPE		__U32_TYPE
#define __GID_T_TYPE		__U32_TYPE
#define __INO_T_TYPE		__ULONGWORD_TYPE
#define __INO64_T_TYPE		__UQUAD_TYPE
#define __MODE_T_TYPE		__U32_TYPE
#define __NLINK_T_TYPE		__UWORD_TYPE
#define __OFF_T_TYPE		__SLONGWORD_TYPE
#define __OFF64_T_TYPE		__SQUAD_TYPE
#define __PID_T_TYPE		__S32_TYPE
#define __RLIM_T_TYPE		__ULONGWORD_TYPE
#define __RLIM64_T_TYPE		__UQUAD_TYPE
#define	__BLKCNT_T_TYPE		__SLONGWORD_TYPE
#define	__BLKCNT64_T_TYPE	__SQUAD_TYPE
#define	__FSBLKCNT_T_TYPE	__ULONGWORD_TYPE
#define	__FSBLKCNT64_T_TYPE	__UQUAD_TYPE
#define	__FSFILCNT_T_TYPE	__ULONGWORD_TYPE
#define	__FSFILCNT64_T_TYPE	__UQUAD_TYPE
#define	__FSWORD_T_TYPE		__SWORD_TYPE
#define	__ID_T_TYPE		__U32_TYPE
#define __CLOCK_T_TYPE		__SLONGWORD_TYPE
#define __TIME_T_TYPE		__SLONGWORD_TYPE
#define __USECONDS_T_TYPE	__U32_TYPE
#define __SUSECONDS_T_TYPE	__SLONGWORD_TYPE
#define __DADDR_T_TYPE		__S32_TYPE
#define __KEY_T_TYPE		__S32_TYPE
#define __CLOCKID_T_TYPE	__S32_TYPE
#define __TIMER_T_TYPE		void *
#define __BLKSIZE_T_TYPE	__SLONGWORD_TYPE
#define __FSID_T_TYPE		struct { int __val[2]; }
#define __SSIZE_T_TYPE		__SWORD_TYPE
#define __SYSCALL_SLONG_TYPE	__SLONGWORD_TYPE
#define __SYSCALL_ULONG_TYPE	__ULONGWORD_TYPE
#define __CPU_MASK_TYPE 	__ULONGWORD_TYPE

#ifdef __LP64__
/* Tell the libc code that off_t and off64_t are actually the same type
   for all ABI purposes, even if possibly expressed as different base types
   for C type-checking purposes.  */
# define __OFF_T_MATCHES_OFF64_T	1

/* Same for ino_t and ino64_t.  */
# define __INO_T_MATCHES_INO64_T	1

/* And for rlim_t and rlim64_t.  */
# define __RLIM_T_MATCHES_RLIM64_T	1
#else
# define __RLIM_T_MATCHES_RLIM64_T	0
#endif

/* Number of descriptors that can fit in an `fd_set'.  */
#define	__FD_SETSIZE		1024


#endif /* bits/typesizes.h */

再回到/bits/types.h 可以找到定义

bits/types.h:#define __U32_TYPE		unsigned int

这只是一个pid_t为例,其余的system types均可以通过这样的方式找到他们的最底层定义。

以下是书中列出之后会用到的system types合集:

对于printf的使用来说,最好将interger类的都cast到%ld也就是long类型上。当然除了off_t类型使用long long的需要格外注意以外。毕竟printf不是一个能够在runtime中自动识别类型的function。

另外就是structure类型的一些system types,很多时候他们的不兼容来源于顺序的不一样,这个时候建议初始化的时候使用explicit方式:

struct sembuf s;
s.sem_num = 3;
s.sem_op = -1;
s.sem_flg = SEM_UNDO;

或者

struct sembuf s = {.sem_num = 3, .sem_op = -1, .sem_flg = SEM_UNDO};

最好不要使用
struct sembuf s = {3, -1, SEM_UNDO}; 这样子对于未来想要做移植的程序很危险

如果不是SUSv3明确定义的macro,在使用的过程中也要注意,因为有可能别的UNIX系统并没有实现这个macro。

3.8 练习

3-1. When using the Linux-specific reboot() system call to reboot the system, the second argument, magic2, must be specified as one of a set of magic numbers (e.g., LINUX_REBOOT_MAGIC2). What is the significance of these numbers? (Converting them to hexadecimal provides a clue.)

答案参考这里Linux系统调用reboot中魔术参数背后鲜为人知的趣味_smstong的博客-CSDN博客

这个算一个彩蛋。


版权声明:本文为u010933311原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。