并发
进程
命令
ps -ef显示系统中所有进程信息
ps aux比ps -ef多显示当前进程状态
top每隔3秒刷新所有进程状态等信息
进程详细信息在/proc中
nice -n 优先级(-209,数字越小优先级越低,仅可修改为09) 进程:更改进程优先级,renice改变已有进程优先级
fg可让进程从后台运行改为前台运行
函数
创建
需要引用unisd.h
pid_t fork(void);失败会返回-1,成功父进程会返回子进程的进程号,子进程返回0
可通过fork的返回值区分父进程和子进程
getpid()获取进程pid
父进程先结束,子进程会变成孤儿进程被init进程收养,子进程变成后台进程
子进程先结束,父进程没有及时回收子进程会变成僵尸进程(要避免)
子进程在fork语句后执行
结束
需要引用unisd.h和stdlib.h
void exit(int status);
void _exit(int status);
结束进程后会将status返回
exit结束进程时会刷新缓冲区,_exit会丢弃缓冲区数据
exec函数族
需要引用unisd.h
进程当前内容被指定内容替换
让父子进程执行不同程序
int execl(const char *path, const char *arg, 。。。);
int execlp(const char *file, const char *arg, 。。。);
成功执行指定程序,失败返回EOF
path执行程序名称和路径
arg。。。传递给执行程序的参数列表,最后一个一定要是NULL
file执行程序名称,在PATH中查找
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
成功执行指定程序,失败返回EOF
arg。。。封装成指针数组,同样最后一个参数为NULL
需要引用stdlib.h
int system(const char *command);
成功返回命令command的返回值,失败返回EOF
回收
子进程由父进程回收
孤儿进程由init进程回收
没有及时回收会变成僵尸进程
需要引用unisd.h
pid_t wait(int *status);
成功返回回收的子进程的进程号,失败返回EOF
子进程没有结束父进程会一直阻塞
多个子进程哪个先结束哪个先回收
status保存子进程返回值和结束方式的地址
status为NULL表示直接释放子进程PCB,不接收返回值
pid_t waitpid(pid_t pid, int *status, int option);
成功返回子进程的pid或0,失败返回EOF
pid指定回收哪个子进程或者任意子进程(-1)
status保存子进程返回值和结束方式的地址
option指定回收方式,0(阻塞)或WNOHANG(非阻塞)
守护进程(Daemon)
系统启动时运行,系统关闭时结束
linux很多服务程序以守护进程形式运行
后台运行,独立于任何终端
周期性执行某种任务
会话是一个或多个进程组的集合,每个进程都属于一个进程组
创建
子进程创建,父进程退出变成孤儿进程被init进程收养后台运行
子进程创建新会话变成新会话的组长,这样就脱离了原先的终端
更改当前工作目录
守护进程一直在后台运行,工作目录不能被卸载,重新设定当前工作目录cwd
重设文件权限掩码,文件权限掩码设置为0,只影响当前文件
关闭当前的文件描述符,关闭所有从父进程继承的打开的文件
线程
进程有独立的地址空间
每个进程都参与内核调度,互不影响
进程在切换时系统开销大
轻量级进程LWP为线程
同一进程中线程共享相同的地址空间
创建
需要引入pthread.h
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*routine)(void *), void *arg);成功返回0,失败返回错误码
thread线程对象
attr线程属性,NULL表示默认属性
routine线程执行的函数
arg传递给routine的参数
回收
需要引入pthread.h
int pthread_join(pthread_t thread, void **retval);成功返回0,失败返回错误码
thread要回收的线程对象
调用线程阻塞直到thread结束
*retval接收线程的返回值
void pthread_exit(void *retval);
结束当前线程
retval可被其他线程通过pthread_join获取
线程私有资源会被释放
信号量
有名信号量既可以用在进程之间也可以用在线程之间
需要引用semaphore.h
int sem_init(sem_t *sem, int pshared, unsigned int value);
成功返回0,失败返回EOF
sem指向要初始化的信号量对象
pshared 0线程间 1进程间
val信号量初始值
int sem_wait(sem_t *sem);P操作
int sem_post(sem_t *sem);V操作
成功返回0,失败返回EOF
sem指向要操作的信号量对象
互斥
临界资源:一次只允许一个任务访问的共享资源
临界区
互斥机制:任务访问临界资源前先申请锁,访问完释放锁
mutex互斥锁
互斥锁
初始化
需要引入pthread.h
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
成功返回0,失败返回错误码
mutex指向要初始化的互斥锁对象
如果无法获得锁就阻塞任务
释放锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功返回0,失败返回错误码
mutex指向要初始化的互斥锁对象
执行完临界区要及时释放锁
进程间通信
无名管道
只能用于有亲缘关系的进程间
单工通信模式,单向
创建
需要引用unisd.h
int pipe(int pfd[2]);
成功返回0,失败返回EOF(-1)
pfd包含两个元素的整形数组,用于保存文件描述符
pfd[0]用于读管道, pfd[1]用于写管道
需要在创建进程前创建管道
文件一旦读走就不存在了
读
当管道中数据量大于读端定义的大小只会读定义的大小对应的数据,否则全读
如果管道中无数据就返回0
写
当空间不足时,会先写满空间然后阻塞写端
当读端不存在不会写入管道,这种情况叫管道断裂,对应的信号是13/d
有名管道
需要引用unisd.h和fcntl.h
int mkfifo(const char *path, mode_t mode);
成功返回0,失败返回EOF
path创建管道文件路径
mode管道文件的权限,如0666
管道程序永远是0
必须同时有读端和写端才可以正常运行,否则直接阻塞
信号
kill -9 -1 很危险,给系统中所有程序发信号
需要引用unisd.h和signal.h
int kill(pid_t pid, int sig);
int raise(int sig);给自己发信号
成功返回0,失败返回EOF
pid接收进程的进程号,0代表同组进程,-1代表所有进程
sig信号类型
int alarm(unsigned int seconds);定时器(经常用于超时检测)
成功返回上一个定时器的剩余时间,失败返回EOF
seconds定时器时间,如果为0代表取消当前定时器
一个进程只能设定一个定时器,时间到时产生SIGALRM
int pause(void);让进程睡眠
进程一直被阻塞,直到被信号中断
被信号中断后返回-1,errno为EINTR
设置信号响应方式
需要引用unisd.h和signal.h
void (*signal(int signo, void (*handler)(int)))(int);
成功返回原先的信号处理函数,失败返回SIG_ERR
signo设置的信号类型
handler指定的信号处理函数, SIG_DFL代表缺省方式, SIG_IGN代表忽略信号
IPC
需要引用sys/types.h和sys/ipc.h
key_t ftok(const char *path, int proj_id);
成功返回合法的key值,失败返回EOF
path存在且可访问的文件的路径
proj_id用于生成key的数字,不能为0
共享内存
创建
需要引用sys/shm.h和sys/ipc.h
int shmget(key_t key, int size, int shmflg);
成功返回共享内存id,失败返回EOF
key是和共享内存关联的key,由IPC_PRIVATE(私有)或ftok生成的地址
shmflg共享内存标志位IPC_CREAT(私有可以没有IPC_CREAT)|0666
映射
需要引用sys/shm.h和sys/ipc.h
void *shmat(int shmid, const void *shmaddr, int shmflg);映射后可像访问正常地址一样访问
成功返回映射后的地址,失败返回(void *)-1
shmid要映射的共享内存id
shmaddr映射后的地址,NULL由系统自动映射
shmflg标志位, 0表示可读写,SHM_RDONLY表示只读
撤销映射
需要引用sys/shm.h和sys/ipc.h
int shmdt(void *shmaddr);
成功返回0,失败返回EOF
不使用共享内存时应及时撤销映射,进程结束系统会自动撤销
控制
需要引用sys/shm.h和sys/ipc.h
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
成功返回0,失败返回EOF
shmid要操作的共享内存id
cmd要执行的操作IPC_STAT(获取共享内存属性) IPC_SET(设置共享内存属性) IPC_RMID(删除共享内存id,buf可设置为NULL)
buf结构体指针,保存或设置共享内存属性的地址
ipcs -l查看共享内存大小等信息
cat /proc/sys/kernel/shmmax设定共享内存大小等信息
shmctl仅添加标记,当所有映射该共享内存的进程都取消映射后再删除,nattach记录映射该共享内存的进程数
消息队列(和共享内存函数有一定的相似度)
创建/打开
需要引用sys/msg.h和sys/ipc.h
int msgget(key_t key, int msgflg);
成功返回消息队列id,失败返回EOF
key是和消息队列关联的key,由IPC_PRIVATE(私有)或ftok生成的地址
msgflg标志位 IPC_CREAT(私有可以没有IPC_CREAT)|0666
发送消息
需要引用sys/msg.h和sys/ipc.h
int msgsnd(int msgid, const void *msgp, size_t size, int msgflg);
成功返回0,失败返回-1
msgid消息队列id
msgp消息缓冲区地址
size消息正文长度
msgflg标志位 0或IPC_NOWAIT(不论消息是否发送成功都返回)
接收消息
需要引用sys/msg.h和sys/ipc.h
int msgrcv(int msgid, const void *msgp, size_t size, long msgtype, int msgflg);
成功返回收到的消息长度,失败返回-1
msgid消息队列id
msgp消息缓冲区地址
size接收的消息长度
msgtype指定接收的消息类型 0为接收最早的消息
msgflg标志位 0或IPC_NOWAIT(不论消息是否接收成功都返回,不成功返回错误)
控制消息队列
需要引用sys/msg.h和sys/ipc.h
int msgctl(int msgid, int cmd, struct msqid_ds *buf);
成功返回0,失败返回-1
msgid消息队列id
cmd要执行的操作 IPC_STAT/IPC_SET/IPC_RMID
System V 信号灯
创建/打开
需要引用sys/sem.h和sys/ipc.h
int semget(key_t key, int nsems, int semflg);
成功返回信号灯id,失败返回-1
key是和消息队列关联的key,由IPC_PRIVATE(私有)或ftok生成的地址
nsems集合中包含的计数信号灯个数
semflg标志位 IPC_CREAT|0666 | IPC_EXCL(检查对象是否存在)
初始化
需要引用sys/sem.h和sys/ipc.h
int semctl(int semid, int semnum, int cmd, …);
成功返回0,失败返回EOF
semid要操作的信号灯集id
semnum要操作的集合中的信号灯编号
cmd执行的操作 SETVAL(需要加第四个参数union semun) IPC_RMID
union semun(需要自己定义)取决于cmd
P/V操作
需要引用sys/sem.h和sys/ipc.h
int semop(int semid, struct sembuf *sops, unsigned nsops);
成功返回0,失败返回-1
semid要操作的信号灯集id
sops描述对信号灯操作的结构体(数组)
nsops要操作的信号灯个数
struct sembuf
{
short semnum;
short sem_op;
short sem_flg;
}
semnum信号灯编号
sem_op:-1-P操作/1-V操作
sem_flg:0/IPC_NOWAIT