linux常用系统调用简介

linux常用系统调用简介
一,进程控制
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必须是一个二进制可执行文件或一个开始行为如下的脚本文件:
#! interpreter [optional-arg]
argv是一个传给新程序的参数的字符串数组。为了方便起见,这个数组的第一个应该是filename,也就是执行文件本身。envp是一个字符串数组,每个字符串的形式为key=value,它是传给新程序的环境变量。argv和envp都必须以NULL结尾。这个参数和环境变量可以用被调用程序的main函数来访问,这时,main函数的定义如下:
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发送:
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>文件中:
struct shmid_ds {
	struct ipc_perm shm_perm;    /* Ownership and permissions */
	size_t          shm_segsz;   /* Size of segment (bytes) */
	time_t          shm_atime;   /* Last attach time */
	time_t          shm_dtime;   /* Last detach time */
	time_t          shm_ctime;   /* Last change time */
	pid_t           shm_cpid;    /* PID of creator */
	pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
	shmatt_t        shm_nattch;  /* No. of current attaches */
	...
};
ipc_perm结构体定义如下:
struct ipc_perm {
	key_t          __key;    /* Key supplied to shmget(2) */
	uid_t          uid;      /* Effective UID of owner */
	gid_t          gid;      /* Effective GID of owner */
	uid_t          cuid;     /* Effective UID of creator */
	gid_t          cgid;     /* Effective GID of creator */
	unsigned short mode;     /* Permissions + SHM_DEST and
		                   SHM_LOCKED flags */
	unsigned short __seq;    /* Sequence number */
};
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类型。调用的程序必须定义这个
联合类型为:
union semun {
	int              val;    /* Value for SETVAL */
	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short  *array;  /* Array for GETALL, SETALL */
	struct seminfo  *__buf;  /* Buffer for IPC_INFO
		                   (Linux-specific) */
};
最后一个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>中:
struct msqid_ds {
	struct ipc_perm msg_perm;     /* Ownership and permissions */
	time_t          msg_stime;    /* Time of last msgsnd(2) */
	time_t          msg_rtime;    /* Time of last msgrcv(2) */
	time_t          msg_ctime;    /* Time of last change */
	unsigned long   __msg_cbytes; /* Current number of bytes in
		                        queue (nonstandard) */
	msgqnum_t       msg_qnum;     /* Current number of messages
		                        in queue */
	msglen_t        msg_qbytes;   /* Maximum number of bytes
		                        allowed in queue */
	pid_t           msg_lspid;    /* PID of last msgsnd(2) */
	pid_t           msg_lrpid;    /* PID of last msgrcv(2) */
};
ipc_perm结构体定义如下:
struct ipc_perm {
	key_t          __key;       /* Key supplied to msgget(2) */
	uid_t          uid;         /* Effective UID of owner */
	gid_t          gid;         /* Effective GID of owner */
	uid_t          cuid;        /* Effective UID of creator */
	gid_t          cgid;        /* Effective GID of creator */
	unsigned short mode;        /* Permissions */
	unsigned short __seq;       /* Sequence number */
};

有效的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是一个调用者定义的结构体指针,这个结构体一般有如下形式:
struct msgbuf {
	long mtype;       /* message type, must be > 0 */
	char mtext[1];    /* message data */
};
结构体中的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被设置。

四,系统控制
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结构体中,其定义如下:
struct rlimit {
        rlim_t rlim_cur;  /* Soft limit */
        rlim_t rlim_max;  /* Hard limit (ceiling for rlim_cur) */
};
软限制是内核强制相应资源的限制值,硬限制是软限制的向上取整。一个没有授权的进程可以设置软限制的值为从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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值