一,进程控制
1)getpid,getppid——获取进程识别号
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void);
获取进程标识号。这可以作为一个独一无二的临时文件名。
pid_t getppid(void);
获取父进程标示号。
 
2)fork——创建一个子进程
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
通过完全复制当前进程创建一个新进程。
如果创建子进程成功,在父进程中返回子进程的进程标示符,在子进程中返回0。
如果失败,在父进程中返回-1,子进程不会被创建,errno被设置。
因为在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,
变量值,程序调用栈,环境变量,缓冲区,等等。所以,子进程和父进程是不能通过
程序内的变量(即使是全局变量)通信的,对于这两个进程来说,它们有各自的进程空间,
互不影响。但父进程和子进程可以通过管道,共享内存,等方式实现通信。
 
3)sleep——使进程睡眠指定的秒数
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
到达指定时间后返回0,若有信号中断,则返回剩余的秒数。
 
4)wait,waitpid——等待子进程终止
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
等待子进程的状态发生变化,并且获得状态变化的信息。状态变化是指:子进程终止;子进程被信号停止;
或者子进程被信号恢复。
等待子进程终止可以让系统释放子进程的资源,如果不等待,那么就会产生僵尸进程。
如果一个子进程状态发生变化,系统调用立即返回,否则就一直阻塞,直到子进程状态发生变化,或者一个
信号中断系统调用。
wait(&status);等价于waitpid(-1, &status, 0);
wait等待一个子进程终止,waitpid等待pid指定的子进程状态改变。默认waitpid仅等待子进程终止,可以
通过options来改变行为。
wait执行成功返回终止子进程的进程号,否则返回-1.
waitpid执行成功返回状态改变子进程的进程号;如果指定了WHOHANG并且pid指定的子进程存在,但是
状态没有改变,立即返回0。错误返回-1.
子进程的返回状态由status存储,可以通过宏来获得信息。如果不关心,可以将status设成NULL。
 
5)execve——执行程序
#include <unistd.h>
int execve(const char *filename, char *const argv[],
             char *const envp[]);
execve执行一个名字为filename的程序,filename必须是一个二进制可执行文件或一个开始行为如下的脚本文件:
  1. #! interpreter [optional-arg]

argv是一个传给新程序的参数的字符串数组。为了方便起见,这个数组的第一个应该是filename,也就是执行文件本身。envp是一个字符串数组,每个字符串的形式为key=value,它是传给新程序的环境变量。argv和envp都必须以NULL结尾。这个参数和环境变量可以用被调用程序的main函数来访问,这时,main函数的定义如下:

  1. int main(int argc, char *argv[], char *envp[])
惊奇的发现,这么定义main函数,gcc带着-Wall选项竟然不警告。
如果当前程序被跟踪了(ptraced),那么,当成功执行execve后,会产生一个SIGTRAP信号。
在执行成功的情况下,execve函数不返回,执行失败返回-1,errno被设置。
执行成功后,调用进程的text,data,bss和stack段被调用execve运行的程序覆盖。
 
6)ptrace——进程跟踪
#include <sys/ptrace.h>
long ptrace(enum __ptrace_request request, pid_t pid,
               void *addr, void *data);
ptrace系统调用提供了一个进程(tracer)可以观察和控制另一个执行的进程(tracee),并且检查和改变tracee的内存和寄存器。
一个tracee首先需要和一个tracer关联。关联和随后的命令是在一个线程中的,在多线程的进程中,每一个线程都可以单独的和一个tracer(可以不相同)关联或者不关联。因此,tracee总是意味着一个线程,而不是一个(多线程)进程。跟踪命令总是以如下的方式向tracee发送: 
  1. ptrace(PTRACE_foo, pid, ...)

这里,pid是一个相应的linux线程的id。

当被跟踪时,tracee会在每次收到信号的时候停止,即使这个信号被忽略了,一种例外是SIGKILL信号,它和平常一样。tracer将会在下次调用waitpid(或者其他相关联的wait系统调用),这可以返回一个status值,表明tracee停止的原因。这时tracer可以使用ptarce请求来检查或修改tracee,然后tracer可以在忽略tracee收到的信号(或者传递另外一个信号)的情况下继续tracee的运行。
当tracer跟踪完毕,可以让tracee继续以平常方式运行,这通过PTRACE_DETACH模式来完成。
request的值决定要完成的动作,下面简单介绍几个:
a)PTRACE_TRACEME
表示该进程被父进程所跟踪,父进程应该希望跟踪该进程。
b)PTRACE_PEEKTEXT, PTRACE_PEEKDATA
从内存地址中读取一个字(word),内存地址由addr给出。在linux中没有区分test和data地址空间,所以它们是等价的。
c)PTRACE_PEEKUSR
从USER区域中读取一个字(word),偏移量为addr。
d)PTRACE_POKETEXT, PTRACE_POKEDATA
往内存地址中写入一个字(word)。内存地址由addr给出。
e)PTRACE_POKEUSR
往USER区域中写入一个字(word)。偏移量为addr。
执行错误返回-1,errno被设置。
 
7)setsid——创建一个会话
#include <unistd.h>
pid_t setsid(void);
如果调用这个函数的进程不是进程组的leader,那么就创建一个新的会话,调用者进程是这个会话的leader,进程组的leader,并且没用控制终端与之相关联。进程组id和会话id设置为这个进程的pid。这个进程是这个进程组和会话的唯一一个进程。
成功返回调用者进程的会话id,否则返回(pid_t)-1,errno被设置。
 
8)getsid——获取会话id
#include <unistd.h>
pid_t getsid(pid_t pid);
getsid(0)返回当前进程的会话id,getsid(p)返回进程id为p的会话id,会话id是会话leader的进程组id。
成功返回会话id,否则返回(pid_t)-1,errno被设置。
 
二,进程间通信
1)kill——向进程发送一个信号
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
kill系统调用可以用来向任何进程组或进程发送信号。
如果pid为正,则信号发给pid指定的进程。
如果pid为0,则信号发送给调用进程所在进程组的所有进程。
如果pid为-1,则信号发送给调用进程有权限发送信号的所有进程,除了进程1(init)。
如果pid小于-1,则信号发送给进程组为-pid内的所有进程。
如果sig为0,那么没有发送信号,但是错误检查会进行,这个可以用来检查进程号或进程组号存在与否。
如果成功(至少一个发送了一个信号),返回0,否则,返回-1,errno被设置。
 
2)pipe——创建管道
#include <unistd.h>
int pipe(int pipefd[2]);
创建一个无向的管道用于进程间通信。数组pipefd返回两个文件描述符指向管道的两端。
pipefd[0]指向读端,pipefd[1]指向写端。
执行成功返回0,否则返回-1,errno被设置。
 
3)mkfifo——创建一个FIFO的特殊文件(命名管道)
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
mkfifo创建一个名称为pathname指向字符串的FIFO特殊文件,mode指定FIFO文件的访问权限。
被umask修改,所以创建的文件权限是(mode & ~umask)。
一旦创建了FIFO特殊文件,任何进程可以以读写的方式打开,就像使用一般文件一样。
但它的写端和读端必须同时打开,以读的方式打开FIFO文件会阻塞直到另一个进程以写的方式
打开同一个FIFO文件,反之亦然。
执行成功返回0,错误情况下返回-1,errno被设置。
 
4)semop——信号量操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
每一个信号量集有如下相关联的值:
unsigned short semval; /* semaphore value */
unsigned short semzcnt; /* # waiting for zero */
unsigned short semncnt; /* # waiting for increase */
pid_t sempid;/* ID of process that did last op */
semop函数在semid指定的信号量上执行操作。有nsops个元素的的sops数组中的每个元素
指定了单个信号量上的操作。sops数组的元素是一个结构体。包含如下成员:
unsigned short sem_num; //semaphore number
short  sem_op;//semaphore operations
short  sem_flg;//operation flags
sem_flg可以取两个值是IPC_NOWAIT和SEM_UNDO,如果指定了前者,当某个信号量的资源不足时
进行P操作,此时不会阻塞等待,而是直接返回资源不可用的错误;不是如果指定了后者,那么在程
序终止的时候会自动撤销对信号量的操作。IPC_UNDO标志保证进程终止(不论正常非正常)后,它对信号量的修改都撤销,
好像它从来没有操作过信号量一样
sops中的操作集合按照数组的顺序,原子的执行,也就是说,要么全部执行,要么全都不执行。
每一个操作在信号量集的编号为sem_num的信号量上执行,第一个信号量的编号是0。根据sem_op
 的不同可以有三种操作:
a,sem_op是一个正整数,这个操作将这个值加到信号量上(semval)。如果指定了SEM_UNDO,
系统将从这个信号量的信号量调节值(semadj)减去sem_op。进程必须有信号量集上的写权限。
b,sem_op是0,进程必须有信号量集上的读权限。这是一个“wait-for-zero”操作:如果semval是0,这个操作
可以立即执行。否则,如果sem_flg指定了IPC_NOWAIT,semop执行失败,errno设置成EAGAIN(没有opos中的操作被执行)。
如果没有指定IPC_NOWAIT,semzcnt(等待信号量值为0的线程数)加一,然后线程开始睡眠直到下面情况之一发生:
第一,semval变成0,这时semzcnt减一;第二,移除了信号量集,semop执行失败,errno设置成EIDRM;第三,调用线程
扑捉到了一个信号,semzcnt减一,semop执行失败,errno被设置成EINTR。
semop函数成功执行完成后,数组sops中每一个信号量的sempid值被设置成调用进程的进程标识符,sem_otime被设置成当前时间。
执行成功semop返回0,否则返回-1,errno被设置。
 
5)semget——获取一个信号量集标识
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
系统调用semget获取与参数key关联的信号量集标识。
信号量集建立的方式有两种:
a,一种是如果key的值是IPC_PRIVATE,那么新的有nsems个信号量的信号量集被建立。
b,如果不存在信号量集与key关联,并且semflg指定IPC_CREAT。
如果semflg同时指定了IPC_CREAT和IPC_EXCL并且key已经与信号量集关联,那么
semget函数会执行失败,errno被设置成EEXIST。
一旦创建信号量集成功,semflg的最低9个有效位,用来定义这个信号量集的权限(所有者,组和其他)。
可执行权限对信号量没有意义,写权限意味着可以修改信号量的值。
新创建的信号量集的值是不定的,尽管Linux像其他的实现那样,将信号量的值初始化为0,但是一个
可移植的应用程序,不能依赖这一点,应该明确的将信号量的值初始化成希望的值。
创建了信号量集后,它的相应semid_ds结构体被初始化为如下:
sem_perm.cuid和sem_perm.uid被设置成该进程的有效用户ID(EUID)。
sem_perm.cgid和sem_perm.gid被设置成该进程的有效组ID(EGID)。
sem_perm.mode的最低9个有效位被设置成semflg的最低9个有效位。
sem_nsems被设置成nsems。
sem_otime被设置成0。
sem_ctime被设置成当前时间。
当信号量集没有被创建的时候,nsems可以为0(不关心),否则nsems必须大于0并且小于或等于每一个信号
量集的最大信号量集(SEMMSL)。
如果信号量集已经存在,则会验证权限。
执行成功返回信号量集标识(一个非负整数),否则返回-1,errno被设置。
 
6)ftok——转换IPC键值函数
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
系统建立IPC通讯是必须指定一个IPC的键值,一般通过ftok函数来获得。
该函数使用pathname指向的文件或目录(必须存在并且可访问)和proj_id的最低8个有效位(不能为0)
来生成一个key_t的IPC键值,可以被msgget,semget,shmget使用。
如果pathname指向同一个文件或目录,并且使用了相同的proj_id那么返回的键值是一样的。
执行成功返回生成的键值,否则,返回-1,errno被设置。
 
7)shmget——获取共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
shmget函数得到一个与键值key关联的共享内存标识符。
以下情况创建一个新的共享内存:
a,key的值是IPC_PRIVATE,这时shmflg的值不起作用。
b,key的值不是IPC_PRIVATE,且shmflg指定了IPC_CREAT,若key相应的共享内存不
存在,就创建一个新的共享内存,如果存在则打开这个共享内存。
以上两种情况创建一个大小为size(round到PAGE_SIZE的整数倍)的共享内存。
如果shmflg同时制定了IPC_CREAT和IPC_EXCL,且key相应的共享内存存在,那么
shmget执行失败,errno被设置。
shmflg的最低9个有效位用来设置共享内存的访问权限,可执行没有意义。
当一个新的共享内存创建的时候,它的内存被初始化成0,与它关联的结构体shmid_ds初始化为:
shm_perm.cuid和shm_perm.uid被设置成进程的有效用户ID(EUID)。
shm_perm.cgid和shm_perm.gid被设置成进程的有效组ID(GUID)。
shm_perm.mode的最低9个有效位被设置成shmflg的最低9个有效位。
shm_segsz被设置成size。
shm_lpid,shm_nattch,shm_atime,shm_dtime被设置成0。
shm_ctime被设置成当前时间。
执行成功返回一个有效的共享内存标识符,否则返回-1,errno被设置。
 
8)shmctl——共享内存控制
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
shmctl执行一个cmd指定的操作在shmid指定的共享内存上。
参数buf是一个shmid_ds结构体指针,定义在<sys/shm.h>文件中: 
  1. struct shmid_ds {
  2. struct ipc_perm shm_perm;    /* Ownership and permissions */
  3. size_t          shm_segsz;   /* Size of segment (bytes) */
  4. time_t          shm_atime;   /* Last attach time */
  5. time_t          shm_dtime;   /* Last detach time */
  6. time_t          shm_ctime;   /* Last change time */
  7. pid_t           shm_cpid;    /* PID of creator */
  8. pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
  9. shmatt_t        shm_nattch;  /* No. of current attaches */
  10. ...
  11. };

ipc_perm结构体定义如下:

  1. struct ipc_perm {
  2. key_t          __key;    /* Key supplied to shmget(2) */
  3. uid_t          uid;      /* Effective UID of owner */
  4. gid_t          gid;      /* Effective GID of owner */
  5. uid_t          cuid;     /* Effective UID of creator */
  6. gid_t          cgid;     /* Effective GID of creator */
  7. unsigned short mode;     /* Permissions + SHM_DEST and
  8. SHM_LOCKED flags */
  9. unsigned short __seq;    /* Sequence number */
  10. };
cmd有三个可能的取值:
a,IPC_STAT,复制shmid关联的shmid_ds结构内容到buf指向的空间,调用者必须有对共享内存的读权限。
b,IPC_SET,将buf指向结构体中成员的值写入到与shmid关联的shmid_ds结构体,同事更新shm_ctime。
下面的成员可以被改变:shm_perm.uid, shm_perm.gid和shm_perm.mode的最低9个有效位。必须有足够权限。
b,IPC_RMID,删除共享内存段。必须有足够权限。只有没有进程与这个共享内存连接的时候才可以删除。
执行错误返回-1,errno被设置。
 
9)semctl——信号量控制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
semctl执行一个由cmd指定的控制操作在semid指定的信号量集上或这个信号量集的编号为semnum的信号量上(编号从0开始)。
这个函数根据cmd的取值可以有3个或4个参数。当参数为4个的时候,第四个参数是union semun类型。调用的程序必须定义这个
联合类型为: 
  1. union semun {
  2. int              val;    /* Value for SETVAL */
  3. struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
  4. unsigned short  *array;  /* Array for GETALL, SETALL */
  5. struct seminfo  *__buf;  /* Buffer for IPC_INFO
  6. (Linux-specific) */
  7. };

最后一个struct seminfo的结构体是linux特有的。

cmd的合法取值有:
a,IPC_STAT,复制shmid关联的shmid_ds结构内容到arg.buf指向的空间,这时,semnum会被忽略。
调用者必须有对共享内存的读权限。
b,IPC_SET,将arg.buf指向结构体中成员的值写入到与shmid关联的shmid_ds结构体,同事更新shm_ctime。

下面的成员可以被改变:shm_perm.uid, shm_perm.gid和shm_perm.mode的最低9个有效位。必须有足够权限。
参数semnum被忽略。
c,IPC_RMID,立即移除信号量集,这会唤醒系统调用semop阻塞的所有进程。必须有足够权限。参数semnum被忽略。
d,GETALL,返回信号量集中的所有信号量值(semval)到arg.array数组中,参数sennum被忽略,必须有读权限。
e,GETNCNT,返回信号量集中编号为semnum的信号量的semncnt值(也就是等待编号为semnnum的信号量的值semval
增加的进程数),必须具有读权限。
f,GETPID,返回信号量集中编号为semnum的信号量的sempid值(也就是最后一个调用semop等待编号为semnnum的信号量
的进程ID),必须具有读权限。
g,GETVAL,返回信号量集中编号为semnum的信号量的semval值,必须具有读权限。
h,GETZCNT,返回信号量集中编号为semnum的信号量的semzcnt值(也就是等待编号为semnnum的信号量的值semval

变成0的进程数),必须具有读权限。
i,SETALL,用arg.array数组中的值设置信号量集中的所有信号量值(semval),同事更新semid_ds的成员sem_ctime当前时间。
所有进程调用semop设置的撤销操作将被清除。如果信号量集中信号量的修改可以使调用semop的进程执行,那么,那个进程将被唤醒。
参数sennum被忽略,必须有写权限。
j,SETVAl,将arg.val的值设置成编号为semnum的信号量的值(semval),并且更新与之关联的semid_ds结构体的sem_ctime值为
当前时间。所有进程调用semop设置的撤销操作将被清除。如果信号量集中信号量的修改可以使调用semop的进程执行,
那么,那个进程将被唤醒。必须有写权限。
执行失败返回-1,errno被设置。
 
10)shmat,shmdt——共享内存操作
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
shmat将shmid指向的共享内存段连接到进程地址空间。这个地址可由如下方式获得:
a,shmaddr是NULL,系统选择一个合适的没有使用的地址连接到共享内存段。
b,shmadrr不是NULL,并且SHM_RND被shmflg指定, 那么连接的地址会向下自动的调整为SHMLBA的整数倍。
否则,这个连接的地址必须是shmaddr必须是page-aligned的。
如果shmflg指定了SHM_RDONLY,那么进程必须对共享内存段有读权限,且只能以读的方式操作这个返回的共享内存地址。
否则,进程必须对共享内存段有读写权限,能以读写的方式操作这个返回的共享内存地址。没有符号来指定只写的操作。
shmat执行成功后会更新共享内存关联的shmid_ds结构体中的成员:
shm_atime被设置成当前时间,shm_lpid被设置成调用进程的进程id,shm_nattch加1。
shmdt将用户空间的shmaddr地址与共享内存段脱离,这个shmaddr必须与shmat返回的一样。
shmdt执行成功后会更新共享内存关联的shmid_ds结构体中的成员:
shm_dtime被设置成当前时间,shm_lpid被设置成调用进程的进程id,shm_nattch减1,当它变成0时,意味着将共享内存段删除。
fork执行后,子进程继承连接的共享内存段。execve执行过后,全部连接的共享内存段将从进程脱离,一旦执行了_exit函数,全部连接
的共享内存段将从进程脱离。
shmat执行成功返回连接的用户空间地址,否则返回(void *)-1,errno被设置。
shmdt执行成功后返回0,否则返回-1,errno被设置。
 
11)msgget——获取消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
msgget函数返回一个与键值key关联的消息队列标识符。
以下情况创建一个新的消息队列:
a)key的值为IPC_PRIVATE。
b)key的值不是IPC_PRIVATE,与键值key关联的消息队列不存在,并且msgflg指定了IPC_CREAT。
如果msgflg同时指定了IPC_CREAT和IPC_EXCL并且与键值key关联的消息队列已经存在,那么msgget执行失败,
errno被设置成EEXIST。
一旦消息队列被创建,msgflg的最低9个有效位被用来设置消息队列的权限(可执行权限没有意义)。
如果一个新的消息队列被创建,那么与它关联的数据结构msgid_ds被初始化为:
msg_perm.cuid和msg_perm.uid被设置成进程的有效用户ID。
msg_perm.cgid和msg_perm.gid被设置成进程的有效组ID。
msg_perm.mode的最低9个有效位被设置成msgflg的最低9个有效位。
msg_qnum,msg_lsqid,msg_lrpid,msg_stime,msg_rtime被设置成0。
msg_ctime被设置成当前时间。
msg_qbytes被设置成MSGMNB。
执行成功返回一个消息队列标识符(非负整数),否则返回-1,errno被设置。
 
12)msgctl——消息队列控制操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl函数在msqid指定的消息队列上执行一个cmd指定的操作。
msqid_ds结构体定在在文件<sys/msg.h>中: 
  1. struct msqid_ds {
  2. struct ipc_perm msg_perm;     /* Ownership and permissions */
  3. time_t          msg_stime;    /* Time of last msgsnd(2) */
  4. time_t          msg_rtime;    /* Time of last msgrcv(2) */
  5. time_t          msg_ctime;    /* Time of last change */
  6. unsigned long   __msg_cbytes; /* Current number of bytes in
  7. queue (nonstandard) */
  8. msgqnum_t       msg_qnum;     /* Current number of messages
  9. in queue */
  10. msglen_t        msg_qbytes;   /* Maximum number of bytes
  11. allowed in queue */
  12. pid_t           msg_lspid;    /* PID of last msgsnd(2) */
  13. pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
  14. };

ipc_perm结构体定义如下:

  1. struct ipc_perm {
  2. key_t          __key;       /* Key supplied to msgget(2) */
  3. uid_t          uid;         /* Effective UID of owner */
  4. gid_t          gid;         /* Effective GID of owner */
  5. uid_t          cuid;        /* Effective UID of creator */
  6. gid_t          cgid;        /* Effective GID of creator */
  7. unsigned short mode;        /* Permissions */
  8. unsigned short __seq;       /* Sequence number */
  9. };

有效的cmd值是:

a)IPC_STAT,将msqid关联的msqid_ds结构体中的内容复制到buf指向的空间。进程调用者必须对消息
队列有读权限。
b)IPC_SET,将buf指向的结构体中的内容写入到msqid关联的msqid_ds结构体中,同时更新msg_ctime。
下面的成员可以被更新:msg_qbytes,msg_perm.uid,msg_perm.gid,msg_perm.mode的最低9个有效位。
调用者必须有足够的权限。
c)IPC_RMID,立即删除消息队列,唤醒所有等待读和写这个消息队列的进程,这些进程中,产生一个失败的错误,errno被设置成EIDRM。
调用者必须有足够的权限。
执行成功返回0,否则返回-1,errno被设置。
 
13)msgsnd,msgrcv——发送,接收消息
#include <sys/types.h>
#inlcude <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
int msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
msgsnd,msgrcv系统调用分别用来向消息队列发送消息和从消息队列中接收消息。调用的进程对消息队列必须
有写(msgsnd)和读(msgrcv)的权限。
参数msgp是一个调用者定义的结构体指针,这个结构体一般有如下形式: 
  1. struct msgbuf {
  2. long mtype;       /* message type, must be > 0 */
  3. char mtext[1];    /* message data */
  4. };
结构体中的mtest成员是一个数组(或者其他的结构体),它的大小由参数msgsz(非负整数)指定。
长度为0的消息是允许的(也就是说没有mtext成员)。mtype成员必须是一个正整数,这个值可以用来作为
接收进程的消息选择。
msgsnd系统调用将msgp指向的结构体的拷贝添加到msqid指向的消息队列。
如果队列中的可用空间足够,msgsnd立即执行成功。(队列的容量被消息队列关联的msqid_ds结构体中的
msg_qbytes成员定义。这个限制可以使用msgctl修改)如果没有足够的可用空间,那么msgsnd的默认行为是
阻塞直到空间足够。如果msgflg指定了IPC_NOWAIT,那么msgsnd执行失败,errno被设置成EAGAIN。
msgsnd阻塞时也可能发生错误:
a)消息队列被移除,msgsnd执行失败,errno被设置成EIDRM。
b)捕捉到一个信号,在这种情况下,msgsnd执行失败,errno被设置成EINTR。msgsnd不会在信号处理函数
执行过后自动重新发送,除非信号处理函数设置了SA_RESTART标志。
一旦执行成功,消息队列关联的数据结构更新如下内容:
msg_lspid被设置成进程的ID。msg_qnum加1。msg_stime被设置成当前时间。
msgrcv系统调用从msqid指向的消息队列中移除一个消息,将其存放在msgp指向的空间中。
参数msgsz指定了msgp的mtext成员的最大字节数。如果消息的长度大于msgsz,那么msgrcv函数的行为取决于
在msgflg参数中有没有指定MSG_NOERROR。如果没有MSG_NOERROR,那么消息不会从消息队列中移除,
msgrcv返回-1,errno被设置成E2BIG。
参数msgtyp有以下类型:
a)如果msgtyp是0,那么读入队列中的第一个消息。
b)如果msgtyp大于0,那么读入队列中第一个类型为msgtyp的消息。如果msgflg指定了MSG_EXCEPT,那么,
读入队列中第一个不是msgtyp类型的消息。
c)如果msgtyp小于0,那么读入消息队列中消息类型小于或等于-msgtyp的最小的类型的消息。
参数msgflg可以由或运算获得,取值有:
a)IPC_NOWAIT,如果没有请求的消息,就立即返回。errno被设置成ENOMSG。
b)MSG_EXCEPT,当msgtyp大于0的时候,读入队列中第一个不是msgtyp类型的消息。
c)MSG_NOERROR,当消息长度大于msgsz时,截取消息。
如果没有请求的消息,并且msgflg没有指定IPC_NOWAIT,那么msgrcv一直阻塞,直到下面情况之一满足:
a)请求的消息被放入了消息队列。
b)消息队列被移除。这种情况下,执行失败,errno被设置成EIDRM。
c)进程捕捉到一个信号。在这种情况下,执行失败,errno被设置成EINTR。msgrcv不会在信号处理函数

执行过后自动重新接收,除非信号处理函数设置了SA_RESTART标志。
一旦执行成功,与消息队列关联的数据结构跟新如下内容:
msg_lrpid被设置成进程ID,msg_qnum减1,msg_rtime被设置成当前时间。
执行失败返回-1,errno被设置。否则,msgsnd返回0,msgrcv返回实际复制到mtext数组中的字节数。
 
三,文件系统控制
1)open,creat——打开或创建文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);
给定一个pathname,返回一个文件描述符,它是一个小的,非负整数,可以被
其他系统调用使用(read,write,lseek,fcntl,等等)。
参数flags必须包含下面三个中的一个:O_RDONLY, O_WRONLY, ORDWR,分别
表示只读,只写,读写。
此外,另外的选项是可选的,通过按位或操作添加,它们是:O_CLOEXEC, O_CREAT,
O_DIRECTORY,O_EXCL,O_NOCTTY,O_NOFOLLOW,O_TRUNC和O_TTY_INIT.
creat与open的flags参数为O_CREAT|O_WRONLY|O_TRUNC等价。
文件打开后,这些可选参数可以通过fcntl来改变。
新文件被创建时mode参数具体指明了使用权限。这个参数必须在flags设置O_CREAT是才起作用,否则,会被忽略。它通常会被umask修改,所以一般创建的文件的权限是(mode & ~umask)。
mode参数的具体值如下:
S_IRWXU:所有者拥有读写执行权限。
S_IRUSR(S_IREAD):允许所有者读
S_IWUSR(S_IWRITE):允许所有者写
S_IXUSR(S_IEXEC):允许所有者执行
S_IRWXG:允许所在组读写执行
S_IRGRP:允许所在组读
S_IWGRP:允许所在组写
S_IXGRP:允许所在组执行
S_IRWXO:允许其他用户读写执行
S_IROTH:允许其他用户读
S_IWOTH:允许其他用户写
S_IXOTH:允许其他用户执行
执行成功返回一个新的文件描述符,否则返回-1,errno被设置。
 
2)read——从一个文件描述符中读取内容
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
read从文件描述符fd指向的文件读取count个字节存放在buf缓冲区中。read从当前
文件指针指向的位置读取,如果在文件末尾,返回0,表示没有读取字节。
执行成功返回读取的字节个数,错误情况下返回-1,errno被设置。
 
3)write——向一个文件描述符中写入内容
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
write向文件描述符fd指向的文件写入buf缓冲区指向的count个字节。
写入的字节数可能会少于count,如存储空间不足或系统调用setrlimit设置了RLIMIT_FSIZE资源限制,或者
write被信号中断。
执行成功返回写入的字节个数,0表示没有写入,否则返回-1,errno被设置。
 
4)close——关闭文件描述符
关闭一个文件描述符,它不再引用一个文件和重新使用。
执行成功返回0,否则返回-1,errno被设置。
 
5)access——确定文件的可存取性
#include <unistd.h>
int access(const char *pathname, int mode);
access检查进程对pathname指向文件的可存取性,如果它是一个符号链接,就指向原来的文件。
mode指定检查的权限,如下:
F_OK:检测文件的存在性。
R_OK,W_OK,X_OK分别检测文件的读,写,执行权限。
mode可以是F_OK或者另外三个的按位或。
拥有权限或文件存在返回0,出错或文件不存在或没有权限返回-1。
 
6)fcntl——对文件描述符操作
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ );
对文件描述符fd执行一个由cmd指定的操作。
fcntl由cmd确定是不是可以有第三个可选的参数。
具体参数见:
执行错误返回-1,成功,返回值由cmd确定。
 
7)truncate,ftruncate——改变文件大小
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);
将一个path或fd指向的普通文件大小改变为length,如果原来文件大小大于length,那么额外的数据会丢失,如果原来文件小于length,那么原来文件会被扩展,扩展部分为'\0'。文件偏移指针不会被改变。文件必须是可写的。
执行成功返回0,否则返回-1,errno被设置。
 
8)fallocate(),在创建文件时预留空间大小,xfs和ext4文件系统创建文件的时候要使用
 
四,系统控制
1)getrlimit, setrlimit——获取/设置系统资源限制
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
这两个函数分别获取和设置系统资源限制。每一种资源有软限制和硬限制,被定义在rlimit结构体中,其定义如下: 
  1. struct rlimit {
  2. rlim_t rlim_cur;  /* Soft limit */
  3. rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
  4. };

软限制是内核强制相应资源的限制值,硬限制是软限制的向上取整。一个没有授权的进程可以设置软限制的值为从0到硬限制的值和设置(不可逆)硬限制的值为一个更低的值,而一个授权的进程可以任意的改变这两个值,在linux中,这个权限是CAP_SYS_RESOURCE。

两个函数中,值RLIM_INFINITY表示对资源没有限制。
resource参数的值必须是如下的值(部分,详细见手册):
a)RLIMIT_AS,进程最大的虚拟内存空间,单位为字节。
b)RLIMIT_CORE,内核转存文件的最大长度。
c)RLIMIT_CPU,最大允许的CPU使用时间,秒为单位。当进程达到软限制,内核将给其发送SIGXCPU信号,这一信号的默认行为是终止进程的执行。然而,可以捕捉信号,处理句柄可将控制返回给主程序。如果进程继续耗费CPU时间,核心会以每秒一次的频率给其发送SIGXCPU信号,直到达到硬限制,那时将给进程发送 SIGKILL信号终止其执行。
d)RLIMIT_DATA,进程数据段的最大值。
e)RLIMIT_FSIZE,进程可建立的文件的最大长度。如果进程试图超出这一限制时,核心会给其发送SIGXFSZ信号,默认情况下将终止进程的执行。
f)RLIMIT_LOCKS,进程可建立的锁和租赁的最大值。
g)RLIMIT_MEMLOCK,进程可锁定在内存中的最大数据量,字节为单位。
h)RLIMIT_MSGQUEUE,进程可为POSIX消息队列分配的最大字节数。
i)RLIMIT_NICE,进程可通过setpriority() 或 nice()调用设置的最大完美值。
j)RLIMIT_NOFILE,指定比进程可打开的最大文件描述词大一的值,超出此值,将会产生EMFILE错误。
k)RLIMIT_NPROC,用户可拥有的最大进程数。
l)RLIMIT_RTPRIO,进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级。
m)RLIMIT_SIGPENDING,用户可拥有的最大挂起信号数。
n)RLIMIT_STACK,最大的进程堆栈,以字节为单位
执行成功,返回0,否则,返回-1,errno被设置。
 
最后介绍一下其他非系统调用函数:
一,多线程
因为pthread并非Linux系统的默认库,而是POSIX线程库。在Linux中将其作为一个库来使用,
因此加上-lpthread(或-pthread)以显式链接该库。函数在执行错误时的错误信息将作为返
回值返回,并不修改系统全局变量errno,当然也无法使用perror()打印错误信息。
1)pthread_create——创建一个新的线程
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_addr_t *addr, void *(*start_routine)(void *), void *arg);
编译和链接的时候要使用-pthread或-lpthread。
pthread_create函数在进程中开启一个新的线程。这个线程调用start_routine()函数开始执行,arg是传给start_routine函数的参数。
这个线程可以通过如下的方式之一终止:
a,调用pthread_exit函数,这个函数指定的参数由该进程另外一个调用pthread_join的线程获得。
b,函数start_routine返回。
c,取消线程(调用pthread_cancel)。
d,进程中的任意一个线程调用exit,或者主线程中main函数中返回。这将导致进程中的所有线程终止。
参数attr用来确定创建的线程的各种属性,这个结构体可以用pthread_attr_init来初始化,如果attr为NULL,
那么用默认的属性来创建一个线程。
pthread_create函数返回之前,如果执行成功,将新创建的线程的ID号存放在thread指向的内存中。
函数执行成功返回0,错误情况下,返回错误号,这时*thread的内容是未定义的。
 
2)pthread_self——获取线程的ID号
#include <pthread.h>
pthread_t pthread_self(void);
返回调用线程的线程ID,这与创建线程时分配的ID号一致。
这个函数总是成功的,返回线程ID。
 
3)pthread_exit——终止线程
#include <pthread.h>
void pthread_exit(void *retval);
终止调用线程,通过retval返回一个值,前提是这个线程是joinable的,这个值可以通过本进程的
其他线程调用pthread_join获得。
 
4)pthread_join——等待一个指定的线程终止
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
pthread_join阻塞到thread指定的线程终止时返回,如果该线程已经终止,则它立即返回。
thread指向的线程必须是joinable的。ruguo如果retval不是NULL,那么pthread_join将复制
thread线程的退出状态(这个值由pthread_exit提供)到*retval指定的内存。如果thread线程已经
取消,那么将PTHREAD_CANCELED存放在*retval中。
执行成功返回0,否则返回错误编号。
 
二,其他
1)system——执行一个shell命令。
#include <stdlib.h>
int system(const char *command);
通过/bin/sh -c执行命令。执行错误返回-1,否则返回命令的执行状态。
 
2)perror——打印一个系统错误信息
#include <stdio.h>
void perror(const char *s);
#include <error.h>
int errno;
perror函数在标准错误输出上产生一个信息,描述最后依次调用系统或库函数出现的错误。
如果s不是NULL并且*s不是'\0',那么首先输出s,然后一个冒号和一个空格,然后是错误信息
和换行符。
 
3)printf,fprintf,sprintf,snprintf——格式化输出函数
#include <stdio.h>
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
printf输出到标准输出(stdout),fprintf输出到stream指定的文件,sprintf输出的str指向的
缓冲区,snprintf指定了最大输出的字符数(包括'\0')。
执行成功返回输出的字符数(不包括'\0'),否则返回一个负数。errno被设置。
 
4)fflush——刷新缓冲区
#include <stdio.h>
int fflush(FILE *stream);
对于输出流,强制刷新缓冲区中的内容到文件流;对于输入流,丢弃缓冲区中的数据。
如果stream参数是NULL,刷新所有打开的输出流。
执行成功返回0,否则返回EOF并且errno被设置。
 
5)fgetc,fgets,getc,getchar,gets,ungetc——输入字符或字符串
#include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FIZE *stream);
int getc(FILE *stream);
int getchar(void);
char *gets(char *s);
int ungetc(int c, FILE *stream);
fgetc从流stream中读取下一个字符,将unsigned char强制转换成int,文件结尾或错误返回EOF。
getc等价于fgetc,不过getc可能是通过宏来实现的,stream能会被多次求值。
getchar等价于getc(stdin);
gets从stdin读取一行,存放在s指向的缓冲区中,直到遇到换行符或EOF,将换行符替换为'\0'。
fgets最多从stream读size个字符(包括'\0'),存放在s指向的缓冲区中,直到遇到换行符或EOF,
换行符读进来后将其存放在缓冲区中,'\0'存放在最后一个字符后边。
ungetc将c强制转换成unsigned char放回stream,它可以被后来的读取操作读取到。
fgetc,getc,getchar返回一个强制转换成int的字符或者在文件结尾或出错时返回EOF。
gets,fgets执行成功返回s,在错误或文件结尾返回NULL。
ungetc执行成功返回c,错误返回EOF。
ISO C11将gets从c语言规范中移除,所以不建议使用gets。
 
6)fputc,fputs,putc,putchar,puts——输出字符或字符串
#include <stdio.h>
int fputc(int c, FILE *stream);
int fputs(const char *s, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
int puts(const char *s);
fputc向stream写入一个字符c,强制转换成unsigned char。
fputs向stream写入一个字符串s,不包括'\0'。
putc与fputc等价,不过putc可能使用宏来实现,stream可能被求值多次。
putchar与putc(c,stdout)等价。
fputc,putc,putchar执行成功返回写入的字符(unsigned char强制转换成int),执行失败返回EOF。
puts和fputs执行成功返回一个非负数,否则返回EOF。
 
7)fopen,fdopen,freopn——打开文件流
#include <stdio.h>
FILE *fopen(const char *path, const char *mode);
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
fopen打开一个path指向的文件,并用一个流来关联它。
mode指定打开文件的方式。描述如下:
r —— 以读的方式打开文件,文件指针指向文件开头。
r+ —— 以读写的方式打开文件,文件指针指向文件开头。
w —— 以写的方式打开文件,如果文件不存在则创建,如果文件存在则覆盖。文件指针指向文件开头。
w+ —— 以读写的方式打开文件。其他同w。
a —— 以添加的方式打开文件(在文件末尾写入)。文件不存在则创建,文件指针指向文件结尾。
a+ —— 以读和添加的方式打开文件(在文件末尾写入)。其他同a。
mode字符串还可以包含b字符,其作为最后一个字符或者在+前边,如rb或rb+。表示打开一个二进制文件。
这完全兼容C89.但字符b在全部遵循POSIX的系统中被忽略,包括linux。其他系统可能将文本文件和二进制
文件以不同的方式对待。在读写二进制文件时,最好加上字符b,便于向non-UNIX系统移植。
fdopen将一个存在的文件描述符fd与一个流关联。打开流的mode("r","r+","w","w+","a","a+"中的一个)必须
与文件描述符的模式兼容。新流的文件指针与fd一致,错误和文件末尾指针被清除。模式w和w+不会覆盖文件。
文件描述符没有被复制,当使用fd创建的流关闭时,文件描述符也会被关闭。将fdopen用于共享内存对象的结果是未定义的。
freopen打开一个path指向的文件,并与stream流关联。freopen的主要用途是对stderr,stdin,stdout进行重定向。
fopen,fdopen,freopen执行成功返回一个文件指针,否则返回NULL,errno被设置。
 
8)popen,pclose——进程输入输出的管道流
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen函数通过创建一个管道,调用fork产生一个子进程,执行一个shell以运行命令来开启一个进程。因为管道是双向的,所以type参数只能是读或者写,而不能两者都是。
参数command是一个以'\0'结尾的字符串,表示一个shell命令,这个命令使用/bin/sh并带-c标志来运行。type必须是包含'r'或'w'的以'\0'结尾的字符串。函数的返回值是一个标准IO流,它必须使用pclose来关闭,而不是fclose。向这个流写入相当于对这个命令的标准输入进行写入,命令的标准输出与调用popen进程的相同,从这个流读相当于从这个命令的标准输出读,命令的标准输入与调用popen进程的标准输入相同。
pclose通过调用wait4等待命令相关的进程结束,返回命令的执行状态。
popen调用fork或pipe失败,或不能分配内存,返回NULL。
pclose执行错误返回-1,errno被设置。
 
9)execl, execlp, execle, execv, execvp——运行一个文件
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
           ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
               char *const envp[]);
exec函数家族用新运行程序的镜像代替当前进程的镜像,具体见execve的手册。
第一个参数是将要执行的可执行程序名字。
在函数execl,execlp,execle中,const char *arg和后边的省略号可以传进来形如arg0,arg1,...,argn的参数。这个列表表示传递给运行程序的参数列表,每个参数指向一个以'\0'结尾的字符串。这个参数列表必须以NULL指针结尾,因为是可变参数,所以,必须强转成char *,也就是(char *)NULL。
函数execv,execvp提供了一个字符串数组,每个字符串以'\0'结尾,表示传递给新程序的参数列表,为了方便,第一个参数为新程序的名字,也就是这两个函数的第一个参数值,这个字符串数据必须以NULL结尾。
execle函数允许通过envp参数来向新程序传递一些环境变量。evnp是一个以'\0'结尾的字符串数组,这个数组必须以NULL结尾,调用execle进程的其他函数可以通过外部变量environ来获取这写环境变量。
这些函数只有在错误的时候返回-1,errno被设置。
 
10)fexecve——通过一个文件描述符来执行程序
#include <unistd.h>
int fexecve(int fd, char *const argv[], char *const envp[]);
fexecve函数与execve做相同的事情,不过,它通过文件描述符来执行一个程序,而不是通过文件名。这个文件描述符必须以只读的方式打开,并且调用者必须有权限执行它只想的文件。
这个函数只在失败的时候返回-1,errno被设置。
 
11)closelog,openlog,syslog,vsyslog——向系统日志发送一个消息
 #include <syslog.h>
void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);

#include <stdarg.h>
void vsyslog(int priority, const char *format, va_list ap);
closelog关闭一个系统日志写入描述符,这个函数的使用是可选的。
openlog打开一个系统日志的连接。ident指向的字符串会在每个消息前加上,典型的设置的程序名。如果ident是NULL,程序名就会被使用。
option参数指定控制openlog和后来的syslog的标记。facility参数指定一个缺省值,它会在随后的syslog没有指定priority时起作用。option和facility的值会在下面描述。
openlog的使用是可选的,调用syslog的时候会自动调用openlog,这种情况下,ident是NULL。
syslog生成一个系统日志消息,这个消息被syslogd发布。priority参数的形式是facility和level(下面会描述)的或运算。format参数与printf类似,除了两个字符序列%m会被代替为字符串strerror(errno),换行符会在需要的时候打印出来。
vsyslog与syslog类似,不过它没有使用可变参数,而是使用了一个va_list类型的变量作为参数。
下面介绍参数的取值:
A)option
a)LOG_CONS,如果将信息发送给syslogd守护进程时发生错误,直接将相关信息输出到终端
b)LOG_NDELAY,立即打开与系统日志的连接(通常情况下,只有在产生第一条日志信息的情况下才会打开与日志系统的连接)
c)LOG_NOWAIT,在记录日志信息时,不等待可能的子进程的创建
d)LOG_ODELAY,类似于LOG_NDELAY参数,与系统日志的连接只有在syslog函数调用时才会创建
e)LOG_PERROR,在将信息写入日志的同时,将信息发送到标准错误输出(POSIX.1-2001和POSIX.1-2008不支持该参数)
f)LOG_PID,每条日志信息中都包括进程号
 
B)facility
这个参数通常用来表示写入日志的程序的类型,这会让配置文件以不同的方式处理这些日志消息。
a)LOG_AUTH,安全/授权信息
b)LOG_AUTHPRIV,安全/授权信息(私有)
c)LOG_CRON,时钟守护进程(cron和at)
d)LOG_DAEMON,没有单独facility值的系统守护进程
e)LOG_FTP,ftp守护进程
f)LOG_KERN,内核消息(不能由用户进程产生)
g)LOG_LOCAL0到LOG_LOCAL7,为本地用户预留
h)LOG_LPR,line printer subsystem
i)LOG_MAIL,mail subsystem
j)LOG_NEWS,USENET news subsystem
k)LOG_SYSLOG,messages generated internally by syslogd(8)
l)LOG_USER,通常的用户级消息
m)LOG_UUCP,UUCP subsystem
 
C)level
这用来确定消息的重要程度,重要程度依次递减:
LOG_EMERG      system is unusable
LOG_ALERT      action must be taken immediately
LOG_CRIT       critical conditions
LOG_ERR        error conditions
LOG_WARNING    warning conditions
LOG_NOTICE     normal, but significant, condition
LOG_INFO       informational message
LOG_DEBUG      debug-level message
Never pass a string with user-supplied data as a format, use the following instead:
syslog(priority, "%s", string);
 
参考:
1)http://blog.sina.com.cn/s/blog_4ac74e9a0100n7w1.html
2)http://www.cnblogs.com/resound/archive/2010/06/17/1759290.html
3)http://blog.csdn.net/bingqingsuimeng/article/details/8741389
 
 

【Linux】【Basis】【Kernel】Linux常见系统调用的更多相关文章

  1. Linux kernel中常见的宏整理

    0x00 宏的基本知识 // object-like #define 宏名 替换列表 换行符 //function-like #define 宏名 ([标识符列表]) 替换列表 换行符 替换列表和标识 ...

  2. Lab1:Linux内核编译及添加系统调用(详细版)

    实验一:Linux内核编译及添加系统调用(HDU) 花了一上午的时间来写这个,良心制作,发现自己刚学的时候没有找到很详细的,就是泛泛的说了下细节地方也没有,于是自己写了这个,有点长,如果你认真的看完了 ...

  3. Linux内核分析之扒开系统调用的三层皮(下)

    一.实验内容 1. 通过内核的方式使用系统调用 需要使用的命令 rm menu -rf //强制删除当前menugit clone http://github.com/mengning/menu.gi ...

  4. Linux系统的中断、系统调用和调度概述【转】

    转自:http://blog.csdn.net/yanlinwang/article/details/8169725 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近学习Linux操作系统, ...

  5. Linux内核学习笔记1——系统调用原理【转】

    1什么是系统调用 系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口.用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文 ...

  6. (转)Linux内核之进程和系统调用

    Linux内核之进程和系统调用 什么是系统调用 在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的.功能十分强大的一系列的函数.这些系统调用是在内核中实现的,再通过一定 ...

  7. 如何在Linux中添加新的系统调用

    系统调用是应用程序和操作系统内核之间的功能接口.其主要目的是使得用户 可以使用操作系统提供的有关设备管理.输入/输入系统.文件系统和进程控制. 通信以及存储管理等方面的功能,而不必了解系统程序的内部结 ...

  8. Linux C 文件操作,系统调用 -- open()、read() 和 标准I/O库 -- fopen()、fread()

    函数汇总: open().write().read().close() fopen().fwrite().fread().fclose() 一.什么是文件 在讲述文件操作之前,我们首先要知道什么是文件 ...

  9. CPU frequency and voltage scaling code in the Linux(TM) kernel

    CPU frequency and voltage scaling code in the Linux(TM) kernel CPU frequency scaling Using CPUfreq G ...

  10. Linux内核分析之扒开系统调用的三层皮(上)

    一.原理总结 本周老师讲的内容主要包括三个方面,用户态.内核态和中断,系统调用概述,以及使用库函数API获取系统当前时间.系统调用是操作系统为用户态进程与硬件设备进行交互提供的一组接口,也是一种特殊的 ...

随机推荐

  1. Redis网络库源码分析(1)之介绍篇

    一.前言 Redis网络库是一个单线程EPOLL模型的网络库,和Memcached使用的libevent相比,它没有那么庞大,代码一共2000多行,因此比较容易分析.其实网上已经有非常多有关这个网络库 ...

  2. 【java+selenium3】自动化cookie操作+图形验证码处理 (十五)

    一.cookie操作 1.获取浏览器所有的cookie import java.util.Set; import org.openqa.selenium.Cookie; //获取浏览器所有的cooki ...

  3. Emmet语法 —— 快速生成HTML结构

    快速生成HTML结构语法 1.生成单个标签 : 标签名+tab,比如 div 然后tab 键, 就可以生成 <div></div> 2.生成多个相同标签 div*3 + tab ...

  4. STC单片机控制28BYJ-48步进电机

    STC单片机4*4按键控制步进电机旋转 28BYJ-48型步进电机说明 四相永磁式的含义 28BYJ-48工作原理 让电机转起来 最简单的电机转动程序 电机转速缓慢的原因分析 便于控制转过圈数的改进程 ...

  5. Linux mem 2.4 Buddy 内存管理机制

    文章目录 1. Buddy 简介 2. Buddy 初始化 2.1 Struct Page 初始化 2.2 Buddy 初始化 3. 内存释放 4. 内存分配 4.1 gfp_mask 4.2 nod ...

  6. python grpc 微服务

    https://realpython.com/python-microservices-grpc/ https://www.manning.com/books/developing-microserv ...

  7. celery tasks always in pending

    Result backend doesn't work or tasks are always in PENDING state¶All tasks are PENDING by default, s ...

  8. 为什么建议使用你 LocalDateTime ,而不是 Date?

    在项目开发过程中经常遇到时间处理,但是你真的用对了吗,理解阿里巴巴开发手册中禁用static修饰SimpleDateFormat吗 通过阅读本篇文章你将了解到: 为什么需要LocalDate.Loca ...

  9. [第二章]c++学习笔记5(构造函数和析构函数调用时机)

    示例函数 注:输出并不一定从main函数开始,如全局对象的初始化在main函数前执行,如构造函数中存在输出,则从构造函数的输出开始 此处6被类型转换构造函数的存在转换为临时对象赋值,而在这个过程结束后 ...

  10. MySQL配置参数innodb_flush_log_at_trx_commit

    innodb_flush_log_at_trx_commit 此参数有3个值可设置:0.1.2 0表示每秒刷写一次日志到硬盘,极端情况下MySQL或操作系统挂了最多丢1秒的数据更新 1表示每次事务提交 ...