进程:运行状态下的程序,叫进程。
程序:磁盘上的可执行文件。
进程IP: PID 唯一的。
//打印进程PID
#include
#include
#include
main()
{
printf("ppid=%d\n",getppid()); //父进程
printf("pid=%d\n",getpid());//子进程,当前进程
}
进程的调度:事件调度,时间片调度,抢占式调度:
进程5态:
1、执行态:正在使用CPU运行
2、就绪态:可以运行,没有CPU运行时间
3、等待态:需要某种条件或某些资源才能继续运行
4、暂停态:收到暂停信号。
5、僵尸态:进程结束,资源还没有被完全回收。
进程状态标记
D 不可中断
R 正在运行,或在队列中的进程
S 处于休眠状态
T 停止或被追踪
Z 僵尸进程
X 死掉的进程
< 高优先级
N 低优先级
L 有些页被锁进内存
s 包含子进程
创建进程
#include
pid_t fork(void);
返回值: < 0 err > 0 在父进程中 ==0 在子进程中
fork函数会创建一个子进程,将父进程空间所有数据,代码,缓冲区等,均拷贝一份。同时形成一个新的内存空间。
共有已打开的文件描述符,描述符表,文件指针。
fork后打开的文件描述符,独立的,描述符表,文件指针。
//创建子进程 会产生僵尸进程
#include
#include
#include
main()
{
pid_t pid;
printf("Start of fork testing.\n");
pid=fork();
printf("Return success:pid=%d\n",pid);
}
//循环创建子进程
#include
#include
#include
main()
{
int i;
for(i=0;i<3;i++)
{
if (fork() == 0)
{
printf("This is child process\n");
}
else
{
printf("This is parent process\n");
}
}
}
父进程先结束,子进程就会成为孤儿进程,由init进程收养。
子进程先结束,需符进程处理该僵尸进程,调用wait函数回收空间
wait();父进程调用,等待子进程结束。处理子进程的僵尸态。
#include
pid_t wait(int *stat_loc);
pid_t waitpid(pid_t pid, int *stat_loc, int options);
stat_loc:存放子进程结束状态。
什么原因结束,
退出的值。
NULL 表示不关心结束状态。
pid_t 返回值:结束的子进程PID
pid > 0 指定等待PID == pid子进程
pid ==0 等待任意一个子进程
pid < 0 等待进程组为 pid 绝对值的子进程
options ==0 阻塞等待 WNOHANG 不阻塞等待
//进程等待 避免僵尸进程
#include
#include
#include
main()
{
if (fork() == 0)
{
printf("This is child process\n");
sleep(1);
exit(0);
}
else
{
wait(NULL);
printf("This is parent process\n");
}
}
exit(1)//要处理缓冲,关闭文件描述符… _exit(2);
进程与程序的区别
1、程序指令集合是静态、进程是动态的,状态在实时改变
2、进程就是程序的一次执行。
3、进程是系统调度的基本单位。以及资源分配的基本单位。
4、进程包含数据多于程序(代码块,变量及其值),进程不仅包含程序数据,还有系统数据PCB
exec函数族:调用外部程序,并运行它
#include
extern char **environ;
int execl(const char *path, const char arg, …
/ (char *) NULL */);
int execlp(const char *file, const char arg, …
/ (char *) NULL */);
int execle(const char *path, const char arg, …
/, (char *) NULL, 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[]);
第五个字符 :
l:参数以列表方式传递
v:参数以指针数组方式传递
第六个字符 :
p: 使用shell默认路径搜索可执行程序
e: 携带指定的环境变量
path:路径 可执行程序的路径
arg:参数列表
envp:环境变量列表
//exec函数
#include
#include
#include
main()
{
if(fork()==0)
{
printf("This is child process\n");
execl("./hello",NULL); //执行外部程序./hello
}
else
{
sleep(1);
printf("This is parent process\n");
}
}
守护进程:
1、生存时间长(系统启动-系统关闭)
2、“外界”联系少,专门的接口
创建守护进程方式:
脱离终端控制
1、创建子进程,父进程结束 fork();
2、创建新会话 setsid();
3、修改工作目录 chdir("/");
4、修改文件权限掩码 umask(0);
5、关闭不使用的文件描述符 close(0); close(1); close(2);
//守护进程 向dome_log.txt写入当前时间
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
FILE *fp = NULL;
time_t tm ;
//1\创建子进程,父进程退出
pid = fork();
if(pid < 0)
{
perror("fork err");
exit(0);
}
if(pid > 0) exit(0);// 2、创建新会话
setsid();// 3、修改工作目录
chdir("/");// 4、修改文件权限掩码
umask(0);// 5、关闭不使用的文件描述符
close(0);
close(1);
close(2);//守护进程创建完成
while(1)
{
fp = fopen("dome_log.txt","a");
if(fp == NULL)
{
sleep(1);
continue;
}
tm = time(NULL);
fputs(ctime(&tm),fp);
fflush(fp);
fclose(fp);
sleep(5);
}
return 0;
}
线程:轻量级进程
1、同样与进程参与调度
2、进程空间独立,线程内存空间共享(全局变量),线程PCB独立的(栈,上下文)
3、线程创建耗费的系统时间较少
线程的创建
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
thread:传入存放tid 的容器
attr: 线程属性控制参数 通常为NULL, 使用默认参数。
start_routine:一个函数指针,指向线程函数入口
arg: 传递个线程函数的参数
返回值: 成功 0 !0 失败
编译时 链接线程库 增加选项 -lpthread
//创建线程
#include
#include
#include
void printid(char *s)
{
pid_t pid;
pthread_t tid;
pid=getpid();
tid=pthread_self();
printf("%s pid= %d, tid=%d .\n",s,pid, tid);
}
void thread_fun(char *s )
{
printid(s);
}
main()
{
pthread_t tid;
int ret;
ret=pthread_create(&tid,NULL,(void *)thread_fun,"new process");
if (ret != 0)
{
printf("Create thread error!\n");
exit(1);
}
printid("main process");
sleep(1);
}
等待线程结束,回收线程资源
#include
int pthread_join(pthread_t thread, void **retval);
thread:你要等待的线程tid
retval:NULL 不关心返回状态
//验证Linux中线程是否并发执行
#include
#include
void thread()
{
int i;
for (i=0;i<3;i++)
{
printf("This is new thread %d.\n",i);
sleep(i);
}
}
main()
{
pthread_t id;
int i,ret;
ret = pthread_create(&id,NULL,(void *)thread,NULL);
if (ret != 0)
{
printf("Create thread error!\n");
exit(1);
}
for (i=0;i<3;i++)
{
printf("This is the main thread %d .\n",i);
sleep(i);
}
pthread_join(id,NULL);
}
线程的取消 pthread_cancel
线程的结束 pthread_exit
//取消线程
#include
#include
void *thread_fun()
{
int i=1;
int j=0;
while(1)
{
i++;
printf("i= %d .\n",i);
for(j=0;j<1000000;j++);
}
}
main()
{
pthread_t id;
pthread_create(&id,NULL,thread_fun,NULL);
sleep(1);
pthread_cancel(id);
printf("OK!\n");
pthread_join(id,NULL);
}
互斥:对某个数据,只允许一个进程或线程操作。
互斥锁:保护共享资源,临界资源
//线程竞争 没有使用互斥锁
#include
#include
#define MAX 10000
int i,value=0;
void func1()
{
for(i=0;i
互斥锁
#include
初始化锁:int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
上锁:int pthread_mutex_lock(pthread_mutex_t *mutex);
尝试上锁: int pthread_mutex_trylock(pthread_mutex_t *mutex);
解锁:int pthread_mutex_unlock(pthread_mutex_t *mutex);
销毁锁:int pthread_mutex_destroy(pthread_mutex_t *mutex);
死锁:需要的资源始终无法获得锁。
//线程竞争 使用互斥锁
#include
#include
#define MAX 10000
int i,value=0;
pthread_mutex_t mutex;
void func1()
{
printf("thread1 open!");
pthread_mutex_lock(&mutex);
for(i=0;i
#include
int a,b;
pthread_mutex_t mut;//互斥锁
void *thread(void *p)
{
while(1)
{
//使用数据前,上锁
pthread_mutex_lock(&mut);
a++;
b =a;
//使用完毕,解锁
pthread_mutex_unlock(&mut);
}
}
int main()
{
pthread_t tid;
int ret;
//创建互斥锁
pthread_mutex_init(&mut,NULL);
a =0;b=0;
ret = pthread_create(&tid,NULL,thread,NULL);
if(ret != 0 )
{
printf("pthread_create err!\n");
return -1;
}
//分离线程
pthread_detach(tid);
while(1)
{
//使用数据前,上锁
pthread_mutex_lock(&mut);
if(a != b)
{
printf("a=%d,b=%d\n",a,b);
}
//使用完毕,解锁
pthread_mutex_unlock(&mut);
}
return 0;
}
同步:当两个或多个线程,需要按照一定的顺序进程
信号量:代表某个动作,或资源的数量
#include
初始化信号量:
int sem_init(sem_t *sem, int pshared, unsigned int value);
sem:信号量容器
pshared:选项, 0 在线程间使用 非0 表示在进程间使用
value:初始值
信号量增加
#include
int sem_post(sem_t *sem);
信号量减少
#include
int sem_wait(sem_t *sem); //当信号量为0,则该函数阻塞等待
int sem_trywait(sem_t *sem);//尝试获取信号量(-1),当信号量为0,则该函数出错退出
获得信号量的值
#include
int sem_getvalue(sem_t *sem, int *sval);
//信号量
#include
#include
#include
#include
#include
#include
sem_t sem;
void * func1(void * i)
{
while (1)
{
sem_post(&sem);
printf("++\n");
fflush(NULL);
sleep(2);
}
}
void * func2(void * i)
{
while (1)
{
sem_wait(&sem);
printf("--\n");
printf("hello word\n");
fflush(NULL);
sleep(1);
}
}
int main()
{
sem_init(&sem,0,5);
pthread_t tid1,tid2;
tid1=pthread_create(&tid1,NULL,func1,NULL);
//if (tid1 != 0)
//exit (0);
printf("ddddddddddddddd\n");
tid2=pthread_create(&tid2,NULL,func2,NULL);
//if (tid2 != 0)
//exit (0);
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
}
#include
#include
#include
sem_t sem;//信号量,指输入可用的数据
sem_t sources;
int sources_count;
int sem_count;
char buf[100];
void *thread1(void *arg)
{
while(1)
{
sleep(2);
sem_wait(&sources);
sem_wait(&sources);
sem_post(&sem);
sem_getvalue(&sem,sem_count);
sem_getvalue(&sources,sources_count);
printf("原料剩余:%d, 商品剩余:%d\n", sources_count,sem_count);
}
}
void *thread2(void *arg)
{
while(1)
{
sleep(1);
sem_wait(&sem);
sem_getvalue(&sem,sem_count);
sem_getvalue(&sources,sources_count);
printf("原料剩余:%d, 商品剩余:%d\n", sources_count,sem_count);
}
}
void *thread3(void *arg)
{
while(1)
{
sleep(2);
//信号量增加 +1
printf("生产了一个资源\n");
sem_post(&sources);
}
}
int main()
{
pthread_t tid;
sem_init(&sem,0,5);
sem_init(&sources,0,4);
pthread_create(&tid,NULL,thread1,NULL);
pthread_create(&tid,NULL,thread2,NULL);
pthread_create(&tid,NULL,thread3,NULL);
pthread_join(tid,NULL);
return 0;
}
1.管道
(1)、无名管道
#include
int pipe(int pipefd[2]);
管道破裂:内核向进程发送SIGPIPE 信号。
1、当一个进程试图往一个关闭了读端的管道中写数据,则会发生管道破裂。
2、管道本身会有一个缓冲。
3、读一个写端关闭的管道,管道中若没有数据,read立即返回0;
4、读端存在,管道中无数据,read 阻塞等待。
//无名管道
#include
#include
#include
int main()
{
pid_t pid;
char buf[100];
int fd[2];
if( pipe(fd) != 0)
{
perror("pipe err");
return -2;
}
pid = fork();
if(pid <0)
{
perror("fork err");
return -1;
}
if(pid == 0)
{//child
//读管道内容并打印
close(fd[1]);//关闭写端
while(1)
{
printf("读管道\n");
read(fd[0],buf,sizeof(buf));
printf("%s",buf);
}
}
if(pid > 0)
{//father
//读终端输入,写入到管道
close(fd[0]);//关闭读端
while(1)
{
fgets(buf,sizeof(buf),stdin);
printf("写入管道\n");
write(fd[1],buf,strlen(buf));
}
}
return 0;
}
(2)、有名管道:在文件系统中可见,不同进程可以通过对该文件的读写来进行通信。其管道缓冲数据是存在与内存中的。
创建管道文件:
#include
#include
int mkfifo(const char *pathname, mode_t mode);
程序1
//程序1和程序2通过管道实现通信,同样适用于进程
#include
#include
#include
#include
#include
int main()
{
int fd1 ;
int fd2 ;
char buf[100];
int ret ;
if( mkfifo("./fifoa",0666) !=0)
{
perror("mkfifo err");
return -2;
}
fd1 = open("./fifoa",O_RDONLY);
if(fd1 < 0 )
{
perror("open err");
return -1;
}
fd2 = open("./fifob",O_WRONLY);
if(fd2 < 0 )
{
perror("open err");
return -1;
}
while(1)
{
printf("写管道:\n");
fgets(buf,sizeof(buf),stdin);
write(fd2,buf,strlen(buf));
printf("读管道:\n");
ret = read(fd1,buf,sizeof(buf));
if(ret == 0)
{
printf("管道已经关闭\n");
break;
}
fputs(buf,stdout);
while(1);
}
return 0;
}
程序2
//程序1和程序2通过管道实现通信,同样适用于进程
#include
#include
#include
#include
#include
int main()
{
int fd1 ;
int fd2 ;
char buf[100];
int ret ;
fd1 = open("./fifoa",O_WRONLY);
if(fd1 < 0 )
{
perror("open err");
return -1;
}
if( mkfifo("./fifob",0666) !=0)
{
perror("mkfifo err");
return -2;
}
fd2 = open("./fifob",O_RDONLY);
if(fd2 < 0 )
{
perror("open err");
return -1;
}
while(1)
{
printf("写管道\n");
fgets(buf,sizeof(buf),stdin);
write(fd1,buf,strlen(buf));
printf("读管道\n");
ret = read(fd2,buf,sizeof(buf));
if(ret == 0)
{
printf("管道已经关闭\n");
break;
}
fputs(buf,stdout);
while(1);
}
return 0;
}
2.信号
软件层面的中断,是唯一一种异步通知方式。
信号的处理:
1、默认处理方式
(1)、忽略
(2)、结束进程
2、指定处理方式
信号捕获
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:信号编号,你要捕获的信号编号
handler 信号处理的函数指针
返回值: 返回之前的信号处理函数指针
闹钟信号:
#include
unsigned int alarm(unsigned int seconds);
seconds:定时的时间
返回值:剩余闹钟时间
信号的发送
#include
int kill(pid_t pid, int sig);
信号 取值 默认动作 含义(发出信号的原因)
SIGHUP 1 Term 终端的挂断或进程死亡
SIGINT 2 Term 来自键盘的中断信号
SIGQUIT 3 Core 来自键盘的离开信号
SIGILL 4 Core 非法指令
SIGABRT 6 Core 来自abort的异常信号
SIGFPE 8 Core 浮点例外
SIGKILL 9 Term 杀死
SIGSEGV 11 Core 段非法错误(内存引用无效)
SIGPIPE 13 Term 管道损坏:向一个没有读进程的管道写数据
SIGALRM 14 Term 来自alarm的计时器到时信号
SIGTERM 15 Term 终止
SIGUSR1 30,10,16 Term 用户自定义信号1
SIGUSR2 31,12,17 Term 用户自定义信号2
SIGCHLD 20,17,18 Ign 子进程停止或终止
SIGCONT 19,18,25 Cont 如果停止,继续执行
SIGSTOP 17,19,23 Stop 非来自终端的停止信号
SIGTSTP 18,20,24 Stop 来自终端的停止信号
SIGTTIN 21,21,26 Stop 后台进程读终端
SIGTTOU 22,22,27 Stop 后台进程写终端
SIGBUS 10,7,10 Core 总线错误(内存访问错误)
SIGPOLL Term Pollable事件发生(Sys V),与SIGIO同义
SIGPROF 27,27,29 Term 统计分布图用计时器到时
SIGSYS 12,-,12 Core 非法系统调用(SVr4)
SIGTRAP 5 Core 跟踪/断点自陷
SIGURG 16,23,21 Ign socket紧急信号(4.2BSD)
SIGVTALRM 26,26,28 Term 虚拟计时器到时(4.2BSD)
SIGXCPU 24,24,30 Core 超过CPU时限(4.2BSD)
SIGXFSZ 25,25,31 Core 超过文件长度限制(4.2BSD)
SIGIOT 6 Core IOT自陷,与SIGABRT同义
SIGEMT 7,-,7 Term
SIGSTKFLT -,16,- Term 协处理器堆栈错误(不使用)
SIGIO 23,29,22 Term 描述符上可以进行I/O操作
SIGCLD -,-,18 Ign 与SIGCHLD同义
SIGPWR 29,30,19 Term 电力故障(System V)
SIGINFO 29,-,- 与SIGPWR同义
SIGLOST -,-,- Term 文件锁丢失
SIGWINCH 28,28,20 Ign 窗口大小改变(4.3BSD, Sun)
SIGUNUSED -,31,- Term 未使用信号(will be SIGSYS)
#include
int pause(void); 进程休眠
systemV IPC对象
IPC对象操作shell命令
ipcs -a:查看所有的ipc对象
ipcs -m:查看共享内存
ipcs -q:消息对列
ipcs -s:信号量
删除ipc对象命令:
ipcrm -m SHM_ID 或ipcrm -M shm_key
ipcrm -q MSG_ID 或ipcrm -Q msg_key
ipcrm -s SEM_ID 或ipcrm -S sem_key
//信号捕获
#include
#include
#include
#include
#include
void do_sig(int sig)
{
printf("信号被捕获 %d\n",sig);
if(sig == 17)
{
wait(NULL);
printf("处理僵尸进程\n");
}
if(sig == SIGALRM)
{
printf("定时时间到!\n");
alarm(5);
}
}
int main()
{
pid_t pid;
int a =0;
signal(SIGINT,do_sig);//注册信号处理函数
signal(SIGCHLD,do_sig);
signal(SIGALRM,do_sig);
alarm(5);
printf("定时5s\n");
pid = fork();
if(pid <0)
{
perror("fork err");
return -1;
}
if(pid == 0)
{
sleep(4);
kill(getppid(),SIGSTOP);//向父进程发送停止信号
printf("stop\n");
sleep(4);
kill(getppid(),SIGCONT);//向父进程发送继续信号
printf("go...\n");
exit(0);
}
while(1)
{
sleep(1);
printf("hello %d\n",a);
a++;
if(a==13)
{
printf("休眠\n");
pause();//放弃cpu调度,进入休眠,等待信号唤醒
printf("退出休眠\n");
}
}
return 0;
}
3.消息队列
1、如何创建一个消息队列
#include
#include
#include
int msgget(key_t key, int msgflg);
key:键值,用于唯一确定一个消息队列
msgflg:权限
IPC_CREAT | 0666 ; IPC_EXCL 不存在就报错
返回值:消息队列ID
key 通常使用一个函数生成
#include
#include
key_t ftok(const char *pathname, int proj_id);
消息收发:
#include
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
msqid:队列ID
msgp:消息体
第一个8字节,存放一个消息类型, long > 0
之后才是消息数据
msgsz:消息大小
msgflg: 0 默认
msgtyp:接收时指定消息的类型
0 :接收消息队列中第一个消息。
大于0 :接收消息队列中第一个类型为msgtyp 的消息.
小于0 :接收消息队列中类型值不小于msgtyp 的绝对值且类型值又最小的消息。
#include
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid :消息队列的队列ID
cmd :
IPC_STAT :读取消息队列的属性,并将其保存在buf指向的缓冲区中。
IPC_SET :设置消息队列的属性。这个值取自buf 参数。
IPC_RMID :从系统中删除消息队列。
buf :消息队列缓冲区
函数返回值 成功:0
出错:-1
#include
#include
#include
#include
#include
#include
typedef struct msgdata_t
{
long type;
char s[50];
int a;
}msgdata_t;
int msg_id;
void * thread(void *arg)
{
msgdata_t msg;
while(1)
{
#ifdef RECV
msgrcv(msg_id,&msg,sizeof(msg) -sizeof(long),1l,0);
#else
msgrcv(msg_id,&msg,sizeof(msg) -sizeof(long),2l,0);
#endif
fputs(msg.s,stdout);
}
}
int main()
{
key_t key;
msgdata_t msg;
pthread_t tid;
//生成一个key值
key = ftok("./",2019);
//创建消息队列
msg_id = msgget(key,IPC_CREAT | 0666);
if( msg_id == -1)
{
perror("msgget err");
return -1;
}
printf("create ok!\n");
if(pthread_create(&tid,NULL,thread,NULL) == -1)
{
perror("pthread_create err");
return -2;
}
while(1)
{
#ifdef RECV
msg.type = 2l;
#else
msg.type = 1l;
#endif
fgets(msg.s,sizeof(msg.s),stdin);
msgsnd(msg_id,&msg,sizeof(msg)-sizeof(long),0);
}
return 0;
}
Makefile
all:
gcc msg_s_r.c -o send -lpthread
gcc msg_s_r.c -o recv -DRECV -lpthread
4.共享内存
1、模型:
#include
#include
int shmget(key_t key, size_t size, int shmflg);
key:键值,用于唯一确定一个共享内存
size:内存大小
shmflg:
IPC_CREAT | 0666 ; IPC_EXCL 不存在就报错
返回值:共享内存ID
2、建立内存映射
#include
#include
建立映射
void *shmat(int shmid, const void *shmaddr, int shmflg);
解除映射
int shmdt(const void *shmaddr);
shmid:共享内存ID
shmaddr:指定映射地址 NULL表示系统默认地址
shmflg:0 默认属性 可读可写
void *建立的内存映射后的地址
#include
#include
#include
#include
#include
typedef struct shm_t
{
char s[100];
int a;
}shm_t;
int main()
{
key_t key;
int shmid;
shm_t * shmp =NULL;
key = ftok("./",2019);
shmid = shmget(key,sizeof(shm_t),IPC_CREAT | 0666 );
if(shmid == -1)
{
perror("shmget err");
return -1;
}
//建立映射
shmp = shmat(shmid,NULL,0);
if(shmp == (void *)-1)
{
perror("shmat err");
return -2;
}
#ifdef RECV
shmp->a =0;
while(1)
{
shmp->a ++;
sleep(1);
}
#else
while(1)
{
printf("a=%d\n",shmp->a);
sleep(1);
}
#endif
return 0;
}
5.信号灯集
#include
#include
#include
#include
#include
#include
typedef struct shm_t
{
char s[100];
int a;
}shm_t;
void sem_p(int sem_id)
{
struct sembuf semt;
semt.sem_num = 0;
semt.sem_op = -1;
semt.sem_flg = 0;
semop(sem_id,&semt,1);
}
void sem_v(int sem_id)
{
struct sembuf semt;
semt.sem_num = 0;
semt.sem_op = 1;
semt.sem_flg = 0;
semop(sem_id,&semt,1);
}
int main()
{
key_t key;
int shmid;
int semid;
shm_t * shmp =NULL;
key = ftok("./",2019);
shmid = shmget(key,sizeof(shm_t),IPC_CREAT | 0666 );
if(shmid == -1)
{
perror("shmget err");
return -1;
}
//建立映射
shmp = shmat(shmid,NULL,0);
if(shmp == (void *)-1)
{
perror("shmat err");
return -2;
}
//创建信号灯集
semid = semget(key,1,IPC_CREAT | 0666 );
if(semid == -1)
{
perror("semget err!");
return -3;
}
//灯集初始化
semctl(semid,0,SETVAL,0);
//共享内存使用
#ifdef RECV
shmp->a =0;
while(1)
{
shmp->a ++;
printf("a++!\n");
//可以读
sem_v(semid);
sleep(1);
}
#else
while(1)
{
//消耗读资源
sem_p(semid);
printf("a=%d\n",shmp->a);
}
#endif
return 0;
}
能使用到的命令
运行时修改程序优先级 renice
Ctrl + z 暂停 Ctrl + c 停止 bg + 任务号 重新运行 fg + 任务号 放到前台运行
进程状态标记
Ps -axj 查看进程更详细的信息
Ps -aux