进程号的类型是pid_t(typedef unsigned int pid_t)。
获得进程和父进程ID的API如下:
#include
#include
pid_t getpid();//获得进程ID
pid_t getppid();//获得父进程ID
进程复制可以通过fork()函数以为进城为蓝本复制一个进程,其ID号和父进程不同,fork()执行一次返回两次。
父进程中返回子进程ID,子进程中返回0;创建进程失败返回-1。
#include
#include
pid_t fork();
system()函数调用shell的外部命令在当前进程中开始另一个进程。
调用成功会返回进程状态值,shell不能执行返回127,失败返回-1
#include
int system(const char *command);
exec()族函数会用更新进程代替原有的进程,新进程PID和原进程相同。
在当系统的可执行路径中根据指定的文件名找到合适的可执行文件名,并用来取代调用进程的内容,即在原来的进程内部运行一个可执行文件。
程序执行成功不返回,失败返回-1
#include
int execl(const char *path, const char *arg, ...);
int execle(const char *path, const char *arg, ... , char * const envp[]);
int execv(const char *path, char *const argv[]);
int execve(const char *filename, char *const argv[], char *const envp[]);
int execvp(const char *file, char * const argv[]);
int execlp(const char *file, const char *arg, ...);
其中只有execve是真正意义上的系统调用,其它都是在此基础上经过包装的库函数。
exec调用举例如下:
char *const ps_argv[] ={"ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL};
char *const ps_envp[] ={"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execv("/bin/ps", ps_argv);
execle("/bin/ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL, ps_envp);
execve("/bin/ps", ps_argv, ps_envp);
execlp("ps", "ps", "-o", "pid,ppid,pgrp,session,tpgid,comm", NULL);
execvp("ps", ps_argv);
注意exec函数族形参展开时的前两个参数:
输入的数组是一个文件描述符的数组,用于保存管道返回的两个文件描述符(输入的时候直接定义数组,不需要赋初值)。
#include
int pipe(int filedes[2]);
读写数据分别为read()和write()函数。关闭读写端口用close()函数。
int write(int *fd, char *str, int len);
//返回写入的字符数
//参数为:指向写端口的指针,写入的字符串指针,写入的字符串长度
int read(int *fd, char *buffer, int len);
//返回读到的字符数
//参数为:指向读端口的指针,要读入的缓冲区间指针,读到的字符串长度
void close(int *fd);
//参数为端口指针
和普通管道不同之处:
mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限
若成功则返回0,否则返回-1,错误原因存于errno中。
创建函数如下:
#include
#include
int mkfifo(const char *pathname, mode_t mode)
消息队列是内核地址空间中的内部链表,通过Linux内核在各个进程之间传送内容,每个消息队列可以用IPC标识符位移地进行确认。不同的消息队列之间相互独立,每个消息队列中的消息,又构成一个独立的链表。
消息缓冲区常用结构为msgbuf结构(可自定义,如设置mtext长度,结构体总长度不能大于8192字节),位于
信号量是计数器,用来控制对多个进程共享的资源锁进行的访问,某个进程在对特定资源进行操作时,信号量可以防止另一个进程去访问它。
新建信号量函数semget()
创建一个新的信号量或获取一个已经存在的信号量的键值。
key为整型值,用户可以自己设定。有两种情况:键值是IPC_PRIVATE,该值通常为0,意思就是创建一个仅能被进程进程给我的信号量;键值不是IPC_PRIVATE,我们可以指定键值,例如1234;也可以一个ftok()函数来取得一个唯一的键值。
nsems 表示初始化信号量的个数;
semflg:信号量的创建方式或权限,有IPC_CREAT和IPC_EXCL,IPC_CREAT如果信号量不存在,则创建一个信号量,否则获取。IPC_EXCL只有信号量不存在的时候,新的信号量才建立,否则就产生错误。
成功返回信号量的标识码ID。失败返回-1;
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
信号量操作函数semop()
信号量的P、V操作是通过向已经建立好的信号量(使用senget()函数),发送semop()命令来完成的。用户通过semop()改变信号量的值。也就是使用资源还是释放资源使用权。
semid:信号量的标识码。也就是semget()的返回值。
sops指向要在信号量集合上执行操作的数组。
struct sembuf{
unsigned short sem_num;//要处理的信号量的编号,第一个信号的编号为0
short sem_op;
short sem_flg;
};
sem_op : 如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权;如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。
sem_flg可取的值如下:
成功返回0,失败返回-1。
#include
#include
#include
int semop(int semid, struct sembuf *sops, unsigned int nsops);
信号量控制函数semctl()
在这个函数中我们可以删除信号量或初始化信号量。
semid:信号量的标志码(ID),也就是semget()函数的返回值。
semnum:操作信号在信号集中的编号。从0开始。是信号量集合的一个索引值。
cmd:命令,表示要进行的操作。取值如下:
第4个参数是可选的;semunion:是union semun的实例。
成功返回0,失败返回-1。
#include
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
共享内存是在多个进程之间共享内存区域的一种进程间的通信方式,它实在多个进程之间对内存段进行映射的方式实现共享内存的。
创建共享内存函数shmget()
得到一个现有的共享内存标识符或创建一个共享内存对象并返回共享内存标识符。
key:共享内存关键字的值,将与内核中现有的共享内存段的关键字值进行比较。0(IPC_PRIVATE):会建立新共享内存对象,大于0的32位整数:视参数shmflg来确定操作。通常要求此值来源于ftok()返回的IPC键值
size:大于0的整数:新建的共享内存大小,以字节为单位,0:只获取共享内存时指定为0
shmflg:0:取共享内存标识符,若不存在则函数会报错;IPC_CREAT:当shmflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的共享内存,则新建一个共享内存;如果存在这样的共享内存,返回此共享内存的标识符;IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的共享内存,则新建一个消息队列;如果存在这样的共享内存则报错。
PS:上述shmflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行 | 运算来确定信号量集的存取权限。
返回的错误代码有:
成功:返回共享内存的标识符,出错:-1,错误原因存于error中
#include
#include
int shmget(key_t key, size_t size, int shmflg)
映射共享内存地址函数shmat()
连接共享内存标识符为shmid的共享内存,连接成功后把共享内存区对象映射到调用进程的地址空间,随后可像本地空间一样访问。
msqid:共享内存标识符
shmaddr:指定共享内存出现在进程内存地址的什么位置,直接指定为NULL让内核自己决定一个合适的地址位置
shmaddr:SHM_RDONLY:为只读模式,其他为读写模式
成功返回附加好的共享内存地址;出错返回-1,错误原因存于error中。
错误代码如下:
fork()后子进程继承已连接的共享内存地址。exec后该子进程与已连接的共享内存地址自动脱离(detach)。进程结束后,已连接的共享内存地址会自动脱离(detach)
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg)
删除内存共享函数shmdt()
与shmat()函数相反,是用来断开与共享内存附加点的地址,禁止本进程访问此片共享内存。
shmaddr:连接的共享内存的起始地址。
成功返回0;出错返回-1,错误原因存于error中:EINVAL:无效的参数shmaddr
本函数调用并不删除所指定的共享内存区,而只是将先前用shmat函数连接(attach)好的共享内存脱离(detach)目前的进程。在成功完成断开连接操作后,shmid_ds结构的shm_nattch成员的值将减去1,如果这个值减到0,内核将真正删除该内存段。
#include
#include
int shmdt(const void *shmaddr)
共享内存控制函数shmctl()
完成对共享内存的控制,向共享内存的句柄(共享内存标识符)发送命令来完成某种功能。
shmid:共享内存标识符
cmd取值如下:
buf:共享内存管理结构体。为命令的参数部分
成功返回0,出错返回-1,错误原因存于error中。
错误代码为:
函数原型为:
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
signal()用于截取系统的信号,对此信号挂接用户自己的处理函数:
返回一个函数指针。第一个参数signum是一个整型数,第二个参数是函数指针
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
挂接了信号处理函数后,可以等待系统信号的到来,用户也可以自己构造信号发送到目标进程中。此类函数有raise()和kill()。
#include
#include
int kill(pid_t pid, int sig);//向进程号为pid的进程发送信号,信号值为sig。当pid为0时。向当前系统的所有进程发送信号sig
int raise(int sig);//在当前进程中自举一个信号sig
可以通过pthread_create()函数创建新线程。
tidp:新创建的线程ID会被设置成tidp指向的内存单元。用于标识一个线程。是pthread_t类型的变量
typedef unsigned long int pthread_t;
attr:用于定制各种不能的线程属性,默认为NULL
start_rtn:新创建的线程从start_rtn函数的地址开始运行,该函数只有一个void类型的指针参数即arg,如果start_rtn需要多个参数,可以将参数放入一个结构中,然后将结构的地址作为arg传入
arg:线程函数运行时传入的参数。
若成功,返回0;否则,返回错误编码,常见的错误为EAGAIN(线程数量达到上限)和EINVAL(线程属性非法)
#include
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_rtn)(void *),
void *restrict arg);
pthread_join()
pthread_join()函数会一直阻塞调用线程,直到指定的线程tid终止。当pthread_join()返回之后,应用程序可回收。
tid:需要等待的线程,指定的线程必须位于当前的进程中,而且不得是分离线程。
status:线程tid所执行的回调函数start_rtn()的返回值(start_rtn()返回值地址需要保证有效),其中status可以为null。
调用成功完成后,pthrea_join()返回0。其他任何返回值都表示出现了错误。
int pthread_join(pthread_t tid, void **status);
pthread_exit()
使用函数pthread_exit退出线程,这是线程的主动行为;由于一个进程中的多个线程是共享数据段的,因此通常在线程退出之后,退出线程所占用的资源并不会随着线程的终止而得到释放,但是可以用pthread_join()函数来同步并释放资源。
retval:pthread_exit()调用线程的返回值,可由其他函数如pthread_join()来检索获取。
#include
void pthread_exit(void *retval)
线程的属性结构为pthread_attr_t,在头文件
多线程编程中,可以用互斥锁(也称互斥量)可以用来保护关键代码段,以确保其独占式的访问,这有点像二进制信号量。互斥锁相关函数主要有以下5个:
#include
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);//互斥初始化
int pthread_mutex_destroy(pthread_mutex_t *mutex);//锁定互斥
int pthread_mutex_lock(pthread_mutex_t *mutex);//互斥预锁定
int pthread_mutex_trylock(pthread_mutex_t *mutex);//解锁互斥
int pthread_mutex_unlock(pthread_mutex_t *mutex);//销毁互斥
互斥锁类型为pthread_mutex_t。
信号量是一个非负的整数计数器,用来实现对公共资源的控制。
PV原子操作
P操作:
V操作:
信号量主要函数如下:
#include
sem_t sem //定义信号量
sem_init(sem_t *sem, int pshared, unsigned int value) //初始化信号量
//sem:指向信号量结构的一个指针
//pshared:信号量的共享类型,不为0时信号来那个可以在进程间共享,否则只能在当前进程的线程间共享
//value:设置信号量初始化的时候信号量的值
sem_wait(sem_t *sem) //获取信号量,信号量的数值-1,如果信号量为0,组线程阻塞到信号量值大于0为止,信号量为0时不再减少
/* 访问共享资源代码 */
sem_post(sem_t *sem) //释放一个信号量,及信号量的数值+1
sem_destroy(sem_t *sem) //如果不再使用信号量,则销毁信号量